From db2da43fc178a5a9e1bb2c2a13681d5be919533e Mon Sep 17 00:00:00 2001 From: dongheng Date: Wed, 14 Sep 2016 19:39:24 +0800 Subject: [PATCH 001/285] components/openssl: add API header for openssl compatibility layer --- components/openssl/include/openssl/ssl.h | 1651 ++++++++++++++++++++++ 1 file changed, 1651 insertions(+) create mode 100644 components/openssl/include/openssl/ssl.h diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h new file mode 100644 index 0000000000..b7b784fe71 --- /dev/null +++ b/components/openssl/include/openssl/ssl.h @@ -0,0 +1,1651 @@ +#ifndef HEADER_SSL_H +#define HEADER_SSL_H + +#include "ssl_types.h" + +/* +{ +*/ + +/* + * SSL_CTX_new - create a SSL context + * + * @param method - the SSL context method point + * + * @return the context point, if create failed return NULL + */ +SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); + +/* + * SSL_CTX_free - free a SSL context + * + * @param method - the SSL context point + * + * @return none + */ +void SSL_CTX_free(SSL_CTX *ctx); + +/* + * SSL_new - create a SSL + * + * @param ctx - the SSL context point + * + * @return the SSL point or NULL if failed + */ +SSL* SSL_new(SSL_CTX *ctx); + +/* + * SSL_free - free the SSL + * + * @param ssl - the SSL point + * + * @return none + */ +void SSL_free(SSL *ssl); + +/* + * SSL_connect - connect to the remote SSL server + * + * @param ssl - the SSL point + * + * @return + * 1 : OK + * -1 : failed + */ +int SSL_connect(SSL *ssl); + +/* + * SSL_accept - accept the remote connection + * + * @param ssl - the SSL point + * + * @return + * 1 : OK + * -1 : failed + */ +int SSL_accept(SSL *ssl); + +/* + * SSL_read - read data from to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the received data buffer point + * @param len - the received data length + * + * @return + * > 0 : OK, and return received data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_read(SSL *ssl, void *buffer, int len); + +/* + * SSL_write - send the data to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the send data buffer point + * @param len - the send data length + * + * @return + * > 0 : OK, and return sent data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_write(SSL *ssl, const void *buffer, int len); + +/* + * SSL_get_verify_result - get the verifying result of the SSL certification + * + * @param ssl - the SSL point + * + * @return the result of verifying + */ +long SSL_get_verify_result(const SSL *ssl); + +/* + * SSL_shutdown - shutdown the connection + * + * @param ssl - the SSL point + * + * @return + * 1 : OK + * 0 : shutdown is not finished + * -1 : an error catch + */ +int SSL_shutdown(SSL *ssl); + +/* + * SSL_set_fd - bind the socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_set_fd(SSL *ssl, int fd); + +/* + * SSL_CTX_use_PrivateKey - These functions load the private key into the SSL_CTX or SSL object + * + * @param ctx - the SSL context point + * @param pkey - private key object point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); + +/* + * SSL_CTX_use_PrivateKey - These functions load the certification into the SSL_CTX or SSL object + * + * @param ctx - the SSL context point + * @param pkey - certification object point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); + +/* + * SSLv23_client_method - create the target SSL context client method + * + * @param none + * + * @return the SSLV2.3 version SSL context client method + */ +const SSL_METHOD* SSLv23_client_method(void); + +/* + * TLSv1_client_method - create the target SSL context client method + * + * @param none + * + * @return the TLSV1.0 version SSL context client method + */ +const SSL_METHOD* TLSv1_client_method(void); + +/* + * SSLv3_client_method - create the target SSL context client method + * + * @param none + * + * @return the SSLV1.0 version SSL context client method + */ +const SSL_METHOD* SSLv3_client_method(void); + +/* + * TLSv1_1_client_method - create the target SSL context client method + * + * @param none + * + * @return the TLSV1.1 version SSL context client method + */ +const SSL_METHOD* TLSv1_1_client_method(void); + + +/* + * SSLv23_server_method - create the target SSL context server method + * + * @param none + * + * @return the SSLV2.3 version SSL context server method + */ +const SSL_METHOD* SSLv23_server_method(void); + +/* + * TLSv1_1_server_method - create the target SSL context server method + * + * @param none + * + * @return the TLSV1.1 version SSL context server method + */ +const SSL_METHOD* TLSv1_1_server_method(void); + +/* + * TLSv1_server_method - create the target SSL context server method + * + * @param none + * + * @return the TLSV1.0 version SSL context server method + */ +const SSL_METHOD* TLSv1_server_method(void); + +/* + * SSLv3_server_method - create the target SSL context server method + * + * @param none + * + * @return the SSLV3.0 version SSL context server method + */ +const SSL_METHOD* SSLv3_server_method(void); + +/* + * SSL_CTX_set_alpn_select_cb - set the SSL context ALPN select callback function + * + * @param ctx - SSL context point + * @param cb - ALPN select callback function + * @param arg - ALPN select callback function entry private data point + * + * @return none + */ +void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, + int (*cb) (SSL *ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg), + void *arg); + + +/* + * SSL_CTX_set_alpn_protos - set the SSL context ALPN select protocol + * + * @param ctx - SSL context point + * @param protos - ALPN protocol name + * @param protos_len - ALPN protocol name bytes + * + * @return + * 0 : OK + * 1 : failed + */ +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, unsigned int protos_len); + +/* + * SSL_CTX_set_next_proto_select_cb - set the SSL context next ALPN select callback function + * + * @param ctx - SSL context point + * @param cb - ALPN select callback function + * @param arg - ALPN select callback function entry private data point + * + * @return none + */ +void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, + int (*cb) (SSL *ssl, + unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg), + void *arg); + +/* + * SSL_get_error - get SSL error code + * + * @param ssl - SSL point + * @param ret_code - SSL return code + * + * @return SSL error number + */ +int SSL_get_error(const SSL *ssl, int ret_code); + +/* + * ERR_clear_error - clear the SSL error code + * + * @param none + * + * @return none + */ +void ERR_clear_error(void); + +/* + * ERR_get_error - get the current SSL error code + * + * @param none + * + * @return current SSL error number + */ +int ERR_get_error(void); + +/* + * ERR_load_SSL_strings - register the SSL error strings + * + * @param none + * + * @return none + */ +void ERR_load_SSL_strings(void); + +/* + * SSL_library_init - initialize the SSL library + * + * @param none + * + * @return none + */ +void SSL_library_init(void); + +/* + * ERR_error_string - generates a human-readable string representing the error code e + * and store it into the "ret" point memory + * + * @param e - error code + * @param ret - memory point to store the string + * + * @return the result string point + */ +char *ERR_error_string(unsigned long e, char *ret); + +/* + * SSL_CTX_set_options - add the SSL context option + * + * @param ctx - SSL context point + * @param opt - new SSL context option + * + * @return the SSL context option + */ +unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt); + +/* + * SSL_CTX_set_options - add the SSL context mode + * + * @param ctx - SSL context point + * @param mod - new SSL context mod + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_mode(SSL_CTX *ctx, int mod); + +/* +} +*/ + +/* + * SSL_do_handshake - perform the SSL handshake + * + * @param ssl - SSL point + * + * @return + * 1 : OK + * 0 : failed + * -1 : a error catch + */ +int SSL_do_handshake(SSL *ssl); + +/* + * SSL_get_version - get the SSL current version + * + * @param ssl - SSL point + * + * @return the version string + */ +const char *SSL_get_version(const SSL *ssl); + +/* + * SSL_CTX_set_ssl_version - set the SSL context version + * + * @param ctx - SSL context point + * @param meth - SSL method point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); + +/* + * SSL_CTX_get_ssl_method - get the SSL context current method + * + * @param ctx - SSL context point + * + * @return the SSL context current method + */ +const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); + +/* + * SSL_CTX_get_ssl_method - get the SSL current method + * + * @param ssl - SSL point + * + * @return the SSL current method + */ +const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); + +/* + * SSL_set_ssl_method - set the SSL method + * + * @param ssl - SSL point + * @param meth - SSL method point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); + +/* + * SSL_add_client_CA - add CA client certification into the SSL + * + * @param ssl - SSL point + * @param x - CA certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_add_client_CA(SSL *ssl, X509 *x); + +/* + * SSL_add_client_CA - add CA client certification into the SSL context + * + * @param ctx - SSL context point + * @param x - CA certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + +/* + * SSL_set_client_CA_list - set the SSL CA certification list + * + * @param ssl - SSL point + * @param name_list - CA certification list + * + * @return none + */ +void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list); + +/* + * SSL_CTX_set_client_CA_list - set the SSL context CA certification list + * + * @param ctx - SSL context point + * @param name_list - CA certification list + * + * @return none + */ +void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); + +/* + * SSL_get_client_CA_list - get the SSL CA certification list + * + * @param ssl - SSL point + * + * @return CA certification list + */ +STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl); + +/* + * SSL_CTX_get_client_CA_list - get the SSL context CA certification list + * + * @param ctx - SSL context point + * + * @return CA certification list + */ +STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx); + +/* + * SSL_get_certificate - get the SSL certification point + * + * @param ssl - SSL point + * + * @return SSL certification point + */ +X509 *SSL_get_certificate(const SSL *ssl); + +/* + * SSL_get_privatekey - get the SSL private key point + * + * @param ssl - SSL point + * + * @return SSL private key point + */ +EVP_PKEY *SSL_get_privatekey(const SSL *ssl); + +/* + * SSL_set_info_callback - set the SSL information callback function + * + * @param ssl - SSL point + * @param cb - information callback function + * + * @return none + */ +void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)); + +/* + * SSL_get_state - get the SSL state + * + * @param ssl - SSL point + * + * @return SSL state + */ +OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); + +/* + * SSL_CTX_set_default_read_buffer_len - set the SSL context read buffer length + * + * @param ctx - SSL context point + * @param len - read buffer length + * + * @return none + */ +void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); + +/* + * SSL_set_default_read_buffer_len - set the SSL read buffer length + * + * @param ssl - SSL point + * @param len - read buffer length + * + * @return none + */ +void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); + +/* + * SSL_set_security_level - set the SSL security level + * + * @param ssl - SSL point + * @param level - security level + * + * @return none + */ +void SSL_set_security_level(SSL *ssl, int level); + +/* + * SSL_get_security_level - get the SSL security level + * + * @param ssl - SSL point + * + * @return security level + */ +int SSL_get_security_level(const SSL *ssl); + +/* + * SSL_CTX_get_verify_mode - get the SSL verifying mode of the SSL context + * + * @param ctx - SSL context point + * + * @return verifying mode + */ +int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); + +/* + * SSL_CTX_get_verify_depth - get the SSL verifying depth of the SSL context + * + * @param ctx - SSL context point + * + * @return verifying depth + */ +int SSL_CTX_get_verify_depth(const SSL_CTX *ctx); + +/* + * SSL_CTX_set_verify - set the SSL context verifying of the SSL context + * + * @param ctx - SSL context point + * @param mode - verifying mode + * @param verify_callback - verifying callback function + * + * @return none + */ +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); + +/* + * SSL_set_verify - set the SSL verifying of the SSL context + * + * @param ctx - SSL point + * @param mode - verifying mode + * @param verify_callback - verifying callback function + * + * @return none + */ +void SSL_set_verify(SSL *s, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); + +/* + * SSL_CTX_set_verify_depth - set the SSL verify depth of the SSL context + * + * @param ctx - SSL context point + * @param depth - verifying depth + * + * @return one + */ +void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); + +/* + * verify_callback - certification verifying callback function + * + * @param preverify_ok - verifying result + * @param x509_ctx - X509 certification point + * + * @return verifying result + */ +int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx); + +/* + * SSL_CTX_set_timeout - set the session timeout time + * + * @param ctx - SSL context point + * @param t - new session timeout time + * + * @return old session timeout time + */ +long SSL_CTX_set_timeout(SSL_CTX *ctx, long t); + +/* + * SSL_CTX_get_timeout - get the session timeout time + * + * @param ctx - SSL context point + * + * @return current session timeout time + */ +long SSL_CTX_get_timeout(const SSL_CTX *ctx); + +/* + * SSL_CTX_set_cipher_list - set the SSL context cipher through the list string + * + * @param ctx - SSL context point + * @param str - cipher controller list string + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); + +/* + * SSL_set_cipher_list - set the SSL cipher through the list string + * + * @param ssl - SSL point + * @param str - cipher controller list string + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_set_cipher_list(SSL *ssl, const char *str); + +/* + * SSL_get_cipher_list - get the SSL cipher list string + * + * @param ssl - SSL point + * + * @return cipher controller list string + */ +const char *SSL_get_cipher_list(const SSL *ssl, int n); + +/* + * SSL_get_current_cipher - get the SSL cipher + * + * @param ssl - SSL point + * + * @return current cipher + */ +const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl); + +/* + * SSL_get_cipher - get the SSL cipher string + * + * @param ssl - SSL point + * + * @return cipher string + */ +const char *SSL_get_cipher(const SSL *ssl); + +/* + * SSL_CTX_get_cert_store - get the SSL context object X509 certification storage + * + * @param ctx - SSL context point + * + * @return x509 certification storage + */ +X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx); + +/* + * SSL_CTX_set_cert_store - set the SSL context object X509 certification store + * + * @param ctx - SSL context point + * @param store - X509 certification store + * + * @return none + */ +void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store); + +/* + * SSL_want - get the SSL specifical statement + * + * @param ssl - SSL point + * + * @return specifical statement + */ +int SSL_want(const SSL *ssl); + +/* + * SSL_want_x509_lookup - check if the SSL is SSL_X509_LOOKUP state + * + * @param ssl - SSL point + * + * @return + * 1 : yes + * 0 : no + */ +int SSL_want_x509_lookup(const SSL *ssl); + +/* + * SSL_clear - reset the SSL + * + * @param ssl - SSL point + * + * @return + * 1 : yes + * 0 : no + */ +int SSL_clear(SSL *ssl); + +/* + * SSL_get_fd - get the socket handle of the SSL + * + * @param ssl - SSL point + * + * @return + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_fd(const SSL *ssl); + +/* + * SSL_get_rfd - get the read only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_rfd(const SSL *ssl); + +/* + * SSL_get_wfd - get the write only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_wfd(const SSL *ssl); + +/* + * SSL_set_read_ahead - set the SSL if we can read as many as data + * + * @param ssl - SSL point + * @param yes - enbale the function + * + * @return none + */ +void SSL_set_read_ahead(SSL *s, int yes); + +/* + * SSL_set_read_ahead - set the SSL context if we can read as many as data + * + * @param ctx - SSL context point + * @param yes - enbale the function + * + * @return none + */ +void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes); + +/* + * SSL_set_read_ahead - get the SSL ahead signal if we can read as many as data + * + * @param ssl - SSL point + * + * @return SSL context ahead signal + */ +int SSL_get_read_ahead(const SSL *ssl); + +/* + * SSL_set_read_ahead - get the SSL context ahead signal if we can read as many as data + * + * @param ctx - SSL context point + * + * @return SSL context ahead signal + */ +long SSL_CTX_get_read_ahead(SSL_CTX *ctx); + +/* + * SSL_has_pending - check if some data can be read + * + * @param ssl - SSL point + * + * @return SSL context ahead signal + */ +int SSL_has_pending(const SSL *ssl); + +/* + * SSL_CTX_use_certificate - load the X509 certification into SSL context + * + * @param ctx - SSL context point + * @param x - X509 certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);//loads the certificate x into ctx + +/* + * SSL_CTX_use_certificate_ASN1 - load the ASN1 certification into SSL context + * + * @param ctx - SSL context point + * @param len - certification length + * @param d - data point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, unsigned char *d); + +/* + * SSL_CTX_use_certificate_file - load the certification file into SSL context + * + * @param ctx - SSL context point + * @param file - certification file name + * @param type - certification encoding type + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); + +/* + * SSL_CTX_use_certificate_chain_file - load the certification chain file into SSL context + * + * @param ctx - SSL context point + * @param file - certification chain file name + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); + + +/* + * SSL_CTX_use_certificate_ASN1 - load the ASN1 private key into SSL context + * + * @param ctx - SSL context point + * @param d - data point + * @param len - private key length + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, unsigned char *d, long len);//adds the private key of type pk stored at memory location d (length len) to ctx + +/* + * SSL_CTX_use_certificate_file - load the private key file into SSL context + * + * @param ctx - SSL context point + * @param file - private key file name + * @param type - private key encoding type + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);//adds the first private key found in file to ctx, The formatting type of the certificate must be specified from the known types SSL_FILETYPE_PEM, SSL_FILETYPE_ASN1. + +/* + * SSL_CTX_use_certificate - load the RSA private key into SSL context + * + * @param ctx - SSL context point + * @param x - RSA private key point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); + +/* + * SSL_CTX_use_certificate_ASN1 - load the RSA ASN1 private key into SSL context + * + * @param ctx - SSL context point + * @param d - data point + * @param len - RSA private key length + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, unsigned char *d, long len); + +/* + * SSL_CTX_use_certificate_file - load the RSA private key file into SSL context + * + * @param ctx - SSL context point + * @param file - RSA private key file name + * @param type - private key encoding type + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type); + + +/* + * SSL_CTX_check_private_key - check if the private key and certification is matched + * + * @param ctx - SSL context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_check_private_key(const SSL_CTX *ctx); + +/* + * SSL_CTX_use_serverinfo - set the SSL context server information + * + * @param ctx - SSL context point + * @param serverinfo - server information string + * @param serverinfo_length - server information length + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, size_t serverinfo_length); + +/* + * SSL_CTX_use_serverinfo - load the SSL context server infomation file into SSL context + * + * @param ctx - SSL context point + * @param file - server information file + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); + +/* + * SSL_select_next_proto - SSL select next function + * + * @param out - point of output data point + * @param outlen - output data length + * @param in - input data + * @param inlen - input data length + * @param client - client data point + * @param client_len -client data length + * + * @return + * OPENSSL_NPN_UNSUPPORTED : not support + * OPENSSL_NPN_NEGOTIATED : negotiated + * OPENSSL_NPN_NO_OVERLAP : no overlap + */ +int SSL_select_next_proto(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const unsigned char *client, unsigned int client_len); + +/* + * SSL_CTX_add_extra_chain_cert - load the extra certification chain into the SSL context + * + * @param ctx - SSL context point + * @param x509 - X509 certification + * + * @return + * 1 : OK + * 0 : failed + */ +long SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *); + +/* + * SSL_CTX_ctrl - control the SSL context + * + * @param ctx - SSL context point + * @param cmd - command + * @param larg - parameter length + * @param parg - parameter point + * + * @return + * 1 : OK + * 0 : failed + */ +long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, char *parg); + +/* + * SSL_CTX_get_ciphers - get the SSL context cipher + * + * @param ctx - SSL context point + * + * @return SSL context cipher + */ +STACK *SSL_CTX_get_ciphers(const SSL_CTX *ctx); + +/* + * SSL_CTX_get_ciphers - check if the SSL context can read as many as data + * + * @param ctx - SSL context point + * + * @return + * 1 : Yes + * 0 : No + */ +long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx); + +/* + * SSL_CTX_get_ex_data - get the SSL context extra data + * + * @param ctx - SSL context point + * @param idx - index + * + * @return data point + */ +char *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx); + +/* + * SSL_CTX_get_quiet_shutdown - get the SSL context quiet shutdown option + * + * @param ctx - SSL context point + * + * @return quiet shutdown option + */ +int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx); + +/* + * SSL_CTX_get_quiet_shutdown - load the SSL context CA file + * + * @param ctx - SSL context point + * @param CAfile - CA certification file + * @param CApath - CA certification file path + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); + +/* + * SSL_CTX_up_ref - add SSL context reference count by '1' + * + * @param ctx - SSL context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_up_ref(SSL_CTX *ctx); + +/* + * SSL_CTX_set_app_data - set SSL context application private data + * + * @param ctx - SSL context point + * @param arg - private data + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg); + +/* + * SSL_CTX_set_client_cert_cb - set SSL context client certification callback function + * + * @param ctx - SSL context point + * @param cb - callback function + * + * @return none + */ +void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey)); + +/* + * SSL_CTX_set_default_read_ahead - set the SSL context if we can read as many as data + * + * @param ctx - SSL context point + * @param m - enable the fuction + * + * @return none + */ +void SSL_CTX_set_default_read_ahead(SSL_CTX *ctx, int m); + +/* + * SSL_CTX_set_default_verify_paths - set SSL context default verifying path + * + * @param ctx - SSL context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); + +/* + * SSL_CTX_set_default_verify_paths - set SSL context default verifying directory + * + * @param ctx - SSL context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx); + +/* + * SSL_CTX_set_default_verify_paths - set SSL context default verifying file + * + * @param ctx - SSL context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_default_verify_file(SSL_CTX *ctx); + +/* + * SSL_CTX_set_ex_data - set SSL context extra data + * + * @param ctx - SSL context point + * @param idx - data index + * @param arg - data point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, char *arg); + +/* + * SSL_CTX_clear_options - clear the SSL context option bit of "op" + * + * @param ctx - SSL context point + * @param op - option + * + * @return SSL context option + */ +unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op); + +/* + * SSL_CTX_clear_options - get the SSL context option + * + * @param ctx - SSL context point + * @param op - option + * + * @return SSL context option + */ +unsigned long SSL_CTX_get_options(SSL_CTX *ctx); + +/* + * SSL_CTX_set_quiet_shutdown - set the SSL context quiet shutdown mode + * + * @param ctx - SSL context point + * @param mode - mode + * + * @return none + */ +void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode); + +/* + * SSL_CTX_get0_certificate - get the SSL context X509 certification + * + * @param ctx - SSL context point + * + * @return X509 certification + */ +X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx); + +/* + * SSL_CTX_get0_certificate - get the SSL context private key + * + * @param ctx - SSL context point + * + * @return private key + */ +EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx); + +/* + * SSL_CTX_use_psk_identity_hint - set SSL context PSK identity hint + * + * @param ctx - SSL context point + * @param hint - PSK identity hint + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint); + +/* + * SSL_CTX_set_psk_server_callback - set SSL context PSK server callback function + * + * @param ctx - SSL context point + * @param callback - callback function + * + */ +void SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, + unsigned int (*callback)(SSL *ssl, + const char *identity, + unsigned char *psk, + int max_psk_len)); +/* + * SSL_alert_desc_string - get alert description string + * + * @param value - alert value + * + * @return alert description string + */ +const char *SSL_alert_desc_string(int value); + +/* + * SSL_alert_desc_string - get alert description long string + * + * @param value - alert value + * + * @return alert description long string + */ +const char *SSL_alert_desc_string_long(int value); + +/* + * SSL_alert_type_string - get alert type string + * + * @param value - alert value + * + * @return alert type string + */ +const char *SSL_alert_type_string(int value); + +/* + * SSL_alert_type_string_long - get alert type long string + * + * @param value - alert value + * + * @return alert type long string + */ +const char *SSL_alert_type_string_long(int value); + +/* + * SSL_get_SSL_CTX - get SSL context of the SSL + * + * @param ssl - SSL point + * + * @return SSL context + */ +SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); + +/* + * SSL_get_app_data - get SSL application data + * + * @param ssl - SSL point + * + * @return application data + */ +char *SSL_get_app_data(SSL *ssl); + +/* + * SSL_get_cipher_bits - get SSL cipher bits + * + * @param ssl - SSL point + * @param alg_bits - algorithm bits + * + * @return strength bits + */ +int SSL_get_cipher_bits(const SSL *ssl, int *alg_bits); + +/* + * SSL_get_cipher_name - get SSL cipher name + * + * @param ssl - SSL point + * + * @return SSL cipher name + */ +char *SSL_get_cipher_name(const SSL *ssl); + +/* + * SSL_get_cipher_version - get SSL cipher version + * + * @param ssl - SSL point + * + * @return SSL cipher version + */ +char *SSL_get_cipher_version(const SSL *ssl); + +/* + * SSL_get_ex_data - get SSL extra data + * + * @param ssl - SSL point + * @param idx - data index + * + * @return extra data + */ +char *SSL_get_ex_data(const SSL *ssl, int idx); + +/* + * SSL_get_ex_data_X509_STORE_CTX_idx - get index of the SSL extra data X509 storage context + * + * @param none + * + * @return data index + */ +int SSL_get_ex_data_X509_STORE_CTX_idx(void); + +/* + * SSL_get_peer_cert_chain - get peer certification chain + * + * @param ssl - SSL point + * + * @return certification chain + */ +STACK *SSL_get_peer_cert_chain(const SSL *ssl); + +/* + * SSL_get_peer_certificate - get peer certification + * + * @param ssl - SSL point + * + * @return certification + */ +X509 *SSL_get_peer_certificate(const SSL *ssl); + +/* + * SSL_get_quiet_shutdown - get SSL quiet shutdown mode + * + * @param ssl - SSL point + * + * @return quiet shutdown mode + */ +int SSL_get_quiet_shutdown(const SSL *ssl); + +/* + * SSL_get_rbio - get SSL read only IO handle + * + * @param ssl - SSL point + * + * @return IO handle + */ +BIO *SSL_get_rbio(const SSL *ssl); + +/* + * SSL_get_shared_ciphers - get SSL shared ciphers + * + * @param ssl - SSL point + * @param buf - buffer to store the ciphers + * @param len - buffer len + * + * @return shared ciphers or NULL if failed + */ +char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len); + +/* + * SSL_get_shutdown - get SSL shutdown mode + * + * @param ssl - SSL point + * + * @return shutdown mode + */ +int SSL_get_shutdown(const SSL *ssl); + +/* + * SSL_get_time - get SSL session time + * + * @param ssl - SSL point + * + * @return session time + */ +long SSL_get_time(const SSL *ssl); + +/* + * SSL_get_timeout - get SSL session timeout time + * + * @param ssl - SSL point + * + * @return session timeout time + */ +long SSL_get_timeout(const SSL *ssl); + +/* + * SSL_get_verify_mode - get SSL verifying mode + * + * @param ssl - SSL point + * + * @return verifying mode + */ +int SSL_get_verify_mode(const SSL *ssl); + +/* + * SSL_get_wbio - get SSL write only IO handle + * + * @param ssl - SSL point + * + * @return IO handle + */ +BIO *SSL_get_wbio(const SSL *ssl); + +/* + * SSL_load_client_CA_file - load SSL client CA certification file + * + * @param file - file name + * + * @return certification loading object + */ +STACK *SSL_load_client_CA_file(const char *file); + +/* + * SSL_up_ref - add SSL reference by '1' + * + * @param ssl - SSL point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_up_ref(SSL *ssl); + +/* + * SSL_peek - read and put data into buf, but not clear the SSL low-level storage + * + * @param ssl - SSL point + * @param buf - storage buffer point + * @param num - data bytes + * + * @return + * > 0 : OK, and return read bytes + * = 0 : connect is closed + * < 0 : a error catch + */ +int SSL_peek(SSL *ssl, void *buf, int num); + +/* + * SSL_renegotiate - make SSL renegotiate + * + * @param ssl - SSL point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_renegotiate(SSL *ssl); + +/* + * SSL_rstate_string - get the state string where SSL is reading + * + * @param ssl - SSL point + * + * @return state string + */ +const char *SSL_rstate_string(SSL *ssl); + +/* + * SSL_rstate_string_long - get the statement long string where SSL is reading + * + * @param ssl - SSL point + * + * @return statement long string + */ +const char *SSL_rstate_string_long(SSL *ssl); + +/* + * SSL_set_accept_state - set SSL accept statement + * + * @param ssl - SSL point + * + * @return none + */ +void SSL_set_accept_state(SSL *ssl); + +/* + * SSL_set_app_data - set SSL application data + * + * @param ssl - SSL point + * @param arg - SSL application data point + * + * @return none + */ +void SSL_set_app_data(SSL *ssl, char *arg); + +/* + * SSL_set_bio - set SSL BIO + * + * @param ssl - SSL point + * @param rbio - read only IO + * @param wbio - write only IO + * + * @return none + */ +void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); + +/* + * SSL_clear_options - clear SSL option + * + * @param ssl - SSL point + * @param op - clear option + * + * @return SSL option + */ +unsigned long SSL_clear_options(SSL *ssl, unsigned long op); + +/* + * SSL_clear_options - get SSL option + * + * @param ssl - SSL point + * + * @return SSL option + */ +unsigned long SSL_get_options(SSL *ssl); + +/* + * SSL_clear_options - clear SSL option + * + * @param ssl - SSL point + * @param op - setting option + * + * @return SSL option + */ +unsigned long SSL_set_options(SSL *ssl, unsigned long op); + +/* + * SSL_set_quiet_shutdown - set SSL quiet shutdown mode + * + * @param ssl - SSL point + * @param mode - quiet shutdown mode + * + * @return none + */ +void SSL_set_quiet_shutdown(SSL *ssl, int mode); + +/* + * SSL_set_quiet_shutdown - set SSL shutdown mode + * + * @param ssl - SSL point + * @param mode - shutdown mode + * + * @return none + */ +void SSL_set_shutdown(SSL *ssl, int mode); + +/* + * SSL_set_time - set SSL session time + * + * @param ssl - SSL point + * @param t - session time + * + * @return session time + */ +void SSL_set_time(SSL *ssl, long t); + +/* + * SSL_set_time - set SSL session timeout time + * + * @param ssl - SSL point + * @param t - session timeout time + * + * @return session timeout time + */ +void SSL_set_timeout(SSL *ssl, long t); + +/* + * SSL_state_string - get SSL statement string + * + * @param ssl - SSL point + * + * @return SSL statement string + */ +char *SSL_state_string(const SSL *ssl); + +/* + * SSL_state_string_long - get SSL statement long string + * + * @param ssl - SSL point + * + * @return SSL statement long string + */ +char *SSL_state_string_long(const SSL *ssl); + +/* + * SSL_total_renegotiations - get SSL renegotiation count + * + * @param ssl - SSL point + * + * @return renegotiation count + */ +long SSL_total_renegotiations(SSL *ssl); + +/* + * SSL_version - get SSL version + * + * @param ssl - SSL point + * + * @return SSL version + */ +int SSL_version(const SSL *ssl); + +/* + * SSL_use_psk_identity_hint - set SSL PSK identity hint + * + * @param ssl - SSL point + * @param hint - identity hint + * + * @return + * 1 : oK + * 0 : failed + */ +int SSL_use_psk_identity_hint(SSL *ssl, const char *hint); + +/* + * SSL_get_psk_identity_hint - get SSL PSK identity hint + * + * @param ssl - SSL point + * + * @return identity hint + */ +const char *SSL_get_psk_identity_hint(SSL *ssl); + +/* + * SSL_get_psk_identity - get SSL PSK identity + * + * @param ssl - SSL point + * + * @return identity + */ +const char *SSL_get_psk_identity(SSL *ssl); + +#endif From 44c466c0ea077a28fa138edfdaed400c2b3bd416 Mon Sep 17 00:00:00 2001 From: dongheng Date: Tue, 20 Sep 2016 16:58:46 +0800 Subject: [PATCH 002/285] components/openssl: add base function version --- components/openssl/Makefile | 50 + components/openssl/include/internal/ssl3.h | 22 + .../openssl/include/internal/ssl_cert.h | 11 + .../openssl/include/internal/ssl_code.h | 95 + components/openssl/include/internal/ssl_dbg.h | 33 + components/openssl/include/internal/ssl_lib.h | 11 + .../openssl/include/internal/ssl_methods.h | 46 + .../openssl/include/internal/ssl_pkey.h | 11 + components/openssl/include/internal/ssl_rsa.h | 14 + .../openssl/include/internal/ssl_types.h | 190 ++ .../openssl/include/internal/ssl_x509.h | 12 + components/openssl/include/internal/tls1.h | 33 + components/openssl/include/openssl/ssl.h | 31 +- components/openssl/include/platform/ssl_pm.h | 41 + components/openssl/library/Makefile | 46 + components/openssl/library/ssl_cert.c | 28 + components/openssl/library/ssl_lib.c | 1622 +++++++++++++++++ components/openssl/library/ssl_methods.c | 43 + components/openssl/library/ssl_pkey.c | 50 + components/openssl/library/ssl_rsa.c | 70 + components/openssl/library/ssl_x509.c | 54 + components/openssl/platform/Makefile | 46 + components/openssl/platform/ssl_pm.c | 422 +++++ 23 files changed, 2976 insertions(+), 5 deletions(-) create mode 100644 components/openssl/Makefile create mode 100644 components/openssl/include/internal/ssl3.h create mode 100644 components/openssl/include/internal/ssl_cert.h create mode 100644 components/openssl/include/internal/ssl_code.h create mode 100644 components/openssl/include/internal/ssl_dbg.h create mode 100644 components/openssl/include/internal/ssl_lib.h create mode 100644 components/openssl/include/internal/ssl_methods.h create mode 100644 components/openssl/include/internal/ssl_pkey.h create mode 100644 components/openssl/include/internal/ssl_rsa.h create mode 100644 components/openssl/include/internal/ssl_types.h create mode 100644 components/openssl/include/internal/ssl_x509.h create mode 100644 components/openssl/include/internal/tls1.h create mode 100644 components/openssl/include/platform/ssl_pm.h create mode 100644 components/openssl/library/Makefile create mode 100644 components/openssl/library/ssl_cert.c create mode 100644 components/openssl/library/ssl_lib.c create mode 100644 components/openssl/library/ssl_methods.c create mode 100644 components/openssl/library/ssl_pkey.c create mode 100644 components/openssl/library/ssl_rsa.c create mode 100644 components/openssl/library/ssl_x509.c create mode 100644 components/openssl/platform/Makefile create mode 100644 components/openssl/platform/ssl_pm.c diff --git a/components/openssl/Makefile b/components/openssl/Makefile new file mode 100644 index 0000000000..bdd8a0e932 --- /dev/null +++ b/components/openssl/Makefile @@ -0,0 +1,50 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +UP_EXTRACT_DIR = .. +GEN_LIBS = libopenssl.a +COMPONENTS_libopenssl = library/liblibrary.a platform/libplatform.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include -I $(PDIR)include/platform -I $(PDIR)include/internal +INCLUDES += -I ./inlcude +INCLUDES += -I $(SDK_PATH)/include/openssl/internal +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/components/openssl/include/internal/ssl3.h b/components/openssl/include/internal/ssl3.h new file mode 100644 index 0000000000..d7c254563b --- /dev/null +++ b/components/openssl/include/internal/ssl3.h @@ -0,0 +1,22 @@ +#ifndef _SSL3_H_ +#define _SSL3_H_ + +# define SSL3_AD_CLOSE_NOTIFY 0 +# define SSL3_AD_UNEXPECTED_MESSAGE 10/* fatal */ +# define SSL3_AD_BAD_RECORD_MAC 20/* fatal */ +# define SSL3_AD_DECOMPRESSION_FAILURE 30/* fatal */ +# define SSL3_AD_HANDSHAKE_FAILURE 40/* fatal */ +# define SSL3_AD_NO_CERTIFICATE 41 +# define SSL3_AD_BAD_CERTIFICATE 42 +# define SSL3_AD_UNSUPPORTED_CERTIFICATE 43 +# define SSL3_AD_CERTIFICATE_REVOKED 44 +# define SSL3_AD_CERTIFICATE_EXPIRED 45 +# define SSL3_AD_CERTIFICATE_UNKNOWN 46 +# define SSL3_AD_ILLEGAL_PARAMETER 47/* fatal */ + +# define SSL3_AL_WARNING 1 +# define SSL3_AL_FATAL 2 + +#define SSL3_VERSION 0x0300 + +#endif diff --git a/components/openssl/include/internal/ssl_cert.h b/components/openssl/include/internal/ssl_cert.h new file mode 100644 index 0000000000..e0b3ea75dc --- /dev/null +++ b/components/openssl/include/internal/ssl_cert.h @@ -0,0 +1,11 @@ +#ifndef _SSL_CERT_H_ +#define _SSL_CERT_H_ + +#include "ssl_pkey.h" +#include "ssl_x509.h" + +CERT *ssl_cert_new(void); + +void ssl_cert_free(CERT *c); + +#endif diff --git a/components/openssl/include/internal/ssl_code.h b/components/openssl/include/internal/ssl_code.h new file mode 100644 index 0000000000..d45abff680 --- /dev/null +++ b/components/openssl/include/internal/ssl_code.h @@ -0,0 +1,95 @@ +#ifndef _SSL_CODE_H_ +#define _SSL_CODE_H_ + +#include "ssl3.h" +#include "tls1.h" + +/* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ +# define SSL_SENT_SHUTDOWN 1 +# define SSL_RECEIVED_SHUTDOWN 2 + +/* + * The following 3 states are kept in ssl->rlayer.rstate when reads fail, you + * should not need these + */ +# define SSL_ST_READ_HEADER 0xF0 +# define SSL_ST_READ_BODY 0xF1 +# define SSL_ST_READ_DONE 0xF2 + +# define SSL_NOTHING 1 +# define SSL_WRITING 2 +# define SSL_READING 3 +# define SSL_X509_LOOKUP 4 +# define SSL_ASYNC_PAUSED 5 +# define SSL_ASYNC_NO_JOBS 6 + + +# define SSL_ERROR_NONE 0 +# define SSL_ERROR_SSL 1 +# define SSL_ERROR_WANT_READ 2 +# define SSL_ERROR_WANT_WRITE 3 +# define SSL_ERROR_WANT_X509_LOOKUP 4 +# define SSL_ERROR_SYSCALL 5/* look at error stack/return value/errno */ +# define SSL_ERROR_ZERO_RETURN 6 +# define SSL_ERROR_WANT_CONNECT 7 +# define SSL_ERROR_WANT_ACCEPT 8 +# define SSL_ERROR_WANT_ASYNC 9 +# define SSL_ERROR_WANT_ASYNC_JOB 10 + +/* Message flow states */ +typedef enum { + /* No handshake in progress */ + MSG_FLOW_UNINITED, + /* A permanent error with this connection */ + MSG_FLOW_ERROR, + /* We are about to renegotiate */ + MSG_FLOW_RENEGOTIATE, + /* We are reading messages */ + MSG_FLOW_READING, + /* We are writing messages */ + MSG_FLOW_WRITING, + /* Handshake has finished */ + MSG_FLOW_FINISHED +} MSG_FLOW_STATE; + +typedef enum { + TLS_ST_BEFORE, + TLS_ST_OK, + DTLS_ST_CR_HELLO_VERIFY_REQUEST, + TLS_ST_CR_SRVR_HELLO, + TLS_ST_CR_CERT, + TLS_ST_CR_CERT_STATUS, + TLS_ST_CR_KEY_EXCH, + TLS_ST_CR_CERT_REQ, + TLS_ST_CR_SRVR_DONE, + TLS_ST_CR_SESSION_TICKET, + TLS_ST_CR_CHANGE, + TLS_ST_CR_FINISHED, + TLS_ST_CW_CLNT_HELLO, + TLS_ST_CW_CERT, + TLS_ST_CW_KEY_EXCH, + TLS_ST_CW_CERT_VRFY, + TLS_ST_CW_CHANGE, + TLS_ST_CW_NEXT_PROTO, + TLS_ST_CW_FINISHED, + TLS_ST_SW_HELLO_REQ, + TLS_ST_SR_CLNT_HELLO, + DTLS_ST_SW_HELLO_VERIFY_REQUEST, + TLS_ST_SW_SRVR_HELLO, + TLS_ST_SW_CERT, + TLS_ST_SW_KEY_EXCH, + TLS_ST_SW_CERT_REQ, + TLS_ST_SW_SRVR_DONE, + TLS_ST_SR_CERT, + TLS_ST_SR_KEY_EXCH, + TLS_ST_SR_CERT_VRFY, + TLS_ST_SR_NEXT_PROTO, + TLS_ST_SR_CHANGE, + TLS_ST_SR_FINISHED, + TLS_ST_SW_SESSION_TICKET, + TLS_ST_SW_CERT_STATUS, + TLS_ST_SW_CHANGE, + TLS_ST_SW_FINISHED +} OSSL_HANDSHAKE_STATE; + +#endif diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h new file mode 100644 index 0000000000..436d33132f --- /dev/null +++ b/components/openssl/include/internal/ssl_dbg.h @@ -0,0 +1,33 @@ +#ifndef _SSL_DEBUG_H_ +#define _SSL_DEBUG_H_ + +#define SSL_DEBUG_ENBALE 0 +#define SSL_DEBUG_LEVEL 0 +#define SSL_ASSERT_ENABLE 1 +#define SSL_DEBUG_LOCATION_ENABLE 1 + +#if SSL_DEBUG_ENBALE + #define SSL_PRINT os_printf +#else + #define SSL_PRINT(...) +#endif + +#if SSL_DEBUG_LOCATION_ENABLE + #define SSL_DEBUG_LOCATION() SSL_PRINT("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__) +#else + #define SSL_DEBUG_LOCATION() +#endif + +#if SSL_ASSERT_ENABLE + #define SSL_ASSERT(s) { if (!(s)) { SSL_DEBUG_LOCATION(); } } +#else + #define SSL_ASSERT(s) +#endif + +#define SSL_ERR(err, go, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(__VA_ARGS__); ret = err; goto go; } + +#define SSL_RET(go, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(__VA_ARGS__); goto go; } + +#define SSL_DEBUG(level, ...) { if (level > SSL_DEBUG_LEVEL) {SSL_PRINT(__VA_ARGS__);} } + +#endif diff --git a/components/openssl/include/internal/ssl_lib.h b/components/openssl/include/internal/ssl_lib.h new file mode 100644 index 0000000000..d95d219556 --- /dev/null +++ b/components/openssl/include/internal/ssl_lib.h @@ -0,0 +1,11 @@ +#ifndef _SSL_LIB_H_ +#define _SSL_LIB_H_ + +#include "ssl_types.h" + +#define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING) +#define SSL_want_read(s) (SSL_want(s) == SSL_READING) +#define SSL_want_write(s) (SSL_want(s) == SSL_WRITING) +#define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_WRITING) + +#endif diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h new file mode 100644 index 0000000000..e2806f177a --- /dev/null +++ b/components/openssl/include/internal/ssl_methods.h @@ -0,0 +1,46 @@ +#ifndef _SSL_METHODS_H_ +#define _SSL_METHODS_H_ + +#define IMPLEMENT_TLS_METHOD_FUNC(func_name, \ + new, free, \ + handshake, shutdown, clear, \ + read, send, pending, \ + set_fd, get_fd, \ + set_bufflen, \ + get_state) \ + static const SSL_METHOD_FUNC func_name = { \ + new, \ + free, \ + handshake, \ + shutdown, \ + clear, \ + read, \ + send, \ + pending, \ + set_fd, \ + get_fd, \ + set_bufflen, \ + get_state \ + }; + +#define IMPLEMENT_TLS_METHOD(ver, mode, fun, func_name) \ + const SSL_METHOD* func_name(void) { \ + static const SSL_METHOD func_name##_data = { \ + ver, \ + mode, \ + &(fun), \ + }; \ + return &func_name##_data; \ + } + +#define IMPLEMENT_SSL_METHOD(ver, mode, fun, func_name) \ + const SSL_METHOD* func_name(void) { \ + static const SSL_METHOD func_name##_data = { \ + ver, \ + mode, \ + &(fun), \ + }; \ + return &func_name##_data; \ + } + +#endif diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h new file mode 100644 index 0000000000..cc870e18ed --- /dev/null +++ b/components/openssl/include/internal/ssl_pkey.h @@ -0,0 +1,11 @@ +#ifndef _SSL_PKEY_H_ +#define _SSL_PKEY_H_ + +#include "ssl_types.h" + +EVP_PKEY *d2i_PrivateKey(int type, + EVP_PKEY **a, + const unsigned char **pp, + long length); + +#endif diff --git a/components/openssl/include/internal/ssl_rsa.h b/components/openssl/include/internal/ssl_rsa.h new file mode 100644 index 0000000000..7530bde734 --- /dev/null +++ b/components/openssl/include/internal/ssl_rsa.h @@ -0,0 +1,14 @@ +#ifndef _SSL_RSA_H_ +#define _SSL_RSA_H_ + +#include "ssl_lib.h" + +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, + const unsigned char *d); + +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); +int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, + const unsigned char *d, long len); + +#endif diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h new file mode 100644 index 0000000000..19eb6cb165 --- /dev/null +++ b/components/openssl/include/internal/ssl_types.h @@ -0,0 +1,190 @@ +#ifndef _SSL_TYPES_H_ +#define _SSL_TYPES_H_ + +#include "ssl_code.h" +#include + +typedef void SSL_CIPHER; + +typedef void X509_STORE_CTX; +typedef void X509_NAME; +typedef void X509_STORE; + +typedef void RSA; + +typedef void STACK; +typedef void BIO; + +#define STACK_OF(x) x + +struct ssl_method_st; +typedef struct ssl_method_st SSL_METHOD; + +struct ssl_method_func_st; +typedef struct ssl_method_func_st SSL_METHOD_FUNC; + +struct record_layer_st; +typedef struct record_layer_st RECORD_LAYER; + +struct ossl_statem_st; +typedef struct ossl_statem_st OSSL_STATEM; + +struct ssl_session_st; +typedef struct ssl_session_st SSL_SESSION; + +struct ssl_ctx_st; +typedef struct ssl_ctx_st SSL_CTX; + +struct ssl_st; +typedef struct ssl_st SSL; + +struct cert_st; +typedef struct cert_st CERT; + +struct x509_st; +typedef struct x509_st X509; + +struct evp_pkey_st; +typedef struct evp_pkey_st EVP_PKEY; + +struct evp_pkey_st { + + void *pkey_pm; +}; + +struct x509_st { + + /* X509 certification platform private point */ + void *x509_pm; +}; + +struct cert_st { + + int sec_level; + + X509 *x509; + + EVP_PKEY *pkey; + +}; + +struct ossl_statem_st { + MSG_FLOW_STATE state; + + int hand_state; +}; + +struct record_layer_st { + + int rstate; + + int read_ahead; +}; + +struct ssl_session_st { + + long timeout; + + long time; +}; + +struct ssl_ctx_st +{ + int version; + + int references; + + unsigned long options; + + #if 0 + struct alpn_protocols alpn_protocol; + #endif + + const SSL_METHOD *method; + + CERT *cert; + + X509 *client_CA; + + int verify_mode; + + long session_timeout; + + int read_ahead; +}; + +struct ssl_st +{ + /* protocol version(one of SSL3.0, TLS1.0, etc.) */ + int version; + + unsigned long options; + + /* shut things down(0x01 : sent, 0x02 : received) */ + int shutdown; + + CERT *cert; + + SSL_CTX *ctx; + + const SSL_METHOD *method; + + RECORD_LAYER rlayer; + + /* where we are */ + OSSL_STATEM statem; + + SSL_SESSION session; + + int rwstate; + + int err; + + void (*info_callback) (const SSL *ssl, int type, int val); + + /* SSL low-level system arch point */ + void *ssl_pm; +}; + +struct ssl_method_st { + /* protocol version(one of SSL3.0, TLS1.0, etc.) */ + int version; + + /* SSL mode(client(0) , server(1), not known(-1)) */ + int endpoint; + + const SSL_METHOD_FUNC *func; +}; + +struct ssl_method_func_st { + + int (*ssl_new)(SSL *ssl); + + void (*ssl_free)(SSL *ssl); + + int (*ssl_handshake)(SSL *ssl); + + int (*ssl_shutdown)(SSL *ssl); + + int (*ssl_clear)(SSL *ssl); + + int (*ssl_read)(SSL *ssl, void *buffer, int len); + + int (*ssl_send)(SSL *ssl, const void *buffer, int len); + + int (*ssl_pending)(const SSL *ssl); + + void (*ssl_set_fd)(SSL *ssl, int fd, int mode); + + int (*ssl_get_fd)(const SSL *ssl, int mode); + + void (*ssl_set_bufflen)(SSL *ssl, int len); + + OSSL_HANDSHAKE_STATE (*ssl_get_state)(const SSL *ssl); +}; + +typedef int (*next_proto_cb)(SSL *ssl, unsigned char **out, + unsigned char *outlen, const unsigned char *in, + unsigned int inlen, void *arg); + +#endif diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h new file mode 100644 index 0000000000..28a7baf513 --- /dev/null +++ b/components/openssl/include/internal/ssl_x509.h @@ -0,0 +1,12 @@ +#ifndef _SSL_X509_H_ +#define _SSL_X509_H_ + +#include "ssl_types.h" + +X509* sk_X509_NAME_new_null(void); + +X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); + +void X509_free(X509 *cert); + +#endif diff --git a/components/openssl/include/internal/tls1.h b/components/openssl/include/internal/tls1.h new file mode 100644 index 0000000000..70de22bb5b --- /dev/null +++ b/components/openssl/include/internal/tls1.h @@ -0,0 +1,33 @@ +#ifndef _TLS1_H_ +#define _TLS1_H_ + +# define TLS1_AD_DECRYPTION_FAILED 21 +# define TLS1_AD_RECORD_OVERFLOW 22 +# define TLS1_AD_UNKNOWN_CA 48/* fatal */ +# define TLS1_AD_ACCESS_DENIED 49/* fatal */ +# define TLS1_AD_DECODE_ERROR 50/* fatal */ +# define TLS1_AD_DECRYPT_ERROR 51 +# define TLS1_AD_EXPORT_RESTRICTION 60/* fatal */ +# define TLS1_AD_PROTOCOL_VERSION 70/* fatal */ +# define TLS1_AD_INSUFFICIENT_SECURITY 71/* fatal */ +# define TLS1_AD_INTERNAL_ERROR 80/* fatal */ +# define TLS1_AD_INAPPROPRIATE_FALLBACK 86/* fatal */ +# define TLS1_AD_USER_CANCELLED 90 +# define TLS1_AD_NO_RENEGOTIATION 100 +/* codes 110-114 are from RFC3546 */ +# define TLS1_AD_UNSUPPORTED_EXTENSION 110 +# define TLS1_AD_CERTIFICATE_UNOBTAINABLE 111 +# define TLS1_AD_UNRECOGNIZED_NAME 112 +# define TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE 113 +# define TLS1_AD_BAD_CERTIFICATE_HASH_VALUE 114 +# define TLS1_AD_UNKNOWN_PSK_IDENTITY 115/* fatal */ +# define TLS1_AD_NO_APPLICATION_PROTOCOL 120 /* fatal */ + +/* Special value for method supporting multiple versions */ +#define TLS_ANY_VERSION 0x10000 + +#define TLS1_VERSION 0x0301 +#define TLS1_1_VERSION 0x0302 +#define TLS1_2_VERSION 0x0303 + +#endif diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index b7b784fe71..89cb5bb9f7 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -1,7 +1,7 @@ #ifndef HEADER_SSL_H #define HEADER_SSL_H -#include "ssl_types.h" +#include "internal/ssl_types.h" /* { @@ -186,6 +186,15 @@ const SSL_METHOD* SSLv3_client_method(void); */ const SSL_METHOD* TLSv1_1_client_method(void); +/* + * TLSv1_1_client_method - create the target SSL context client method + * + * @param none + * + * @return the TLSV1.2 version SSL context client method + */ +const SSL_METHOD* TLSv1_2_client_method(void); + /* * SSLv23_server_method - create the target SSL context server method @@ -205,6 +214,15 @@ const SSL_METHOD* SSLv23_server_method(void); */ const SSL_METHOD* TLSv1_1_server_method(void); +/* + * TLSv1_1_server_method - create the target SSL context server method + * + * @param none + * + * @return the TLSV1.2 version SSL context server method + */ +const SSL_METHOD* TLSv1_2_server_method(void); + /* * TLSv1_server_method - create the target SSL context server method * @@ -774,7 +792,7 @@ int SSL_get_wfd(const SSL *ssl); * SSL_set_read_ahead - set the SSL if we can read as many as data * * @param ssl - SSL point - * @param yes - enbale the function + * @param yes - enable the function * * @return none */ @@ -813,7 +831,9 @@ long SSL_CTX_get_read_ahead(SSL_CTX *ctx); * * @param ssl - SSL point * - * @return SSL context ahead signal + * @return + * 1 : there are bytes to be read + * 0 : no data */ int SSL_has_pending(const SSL *ssl); @@ -840,7 +860,7 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);//loads the certificate x int * 1 : OK * 0 : failed */ -int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, unsigned char *d); +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); /* * SSL_CTX_use_certificate_file - load the certification file into SSL context @@ -879,7 +899,7 @@ int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); * 1 : OK * 0 : failed */ -int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, unsigned char *d, long len);//adds the private key of type pk stored at memory location d (length len) to ctx +int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len);//adds the private key of type pk stored at memory location d (length len) to ctx /* * SSL_CTX_use_certificate_file - load the private key file into SSL context @@ -1648,4 +1668,5 @@ const char *SSL_get_psk_identity_hint(SSL *ssl); */ const char *SSL_get_psk_identity(SSL *ssl); + #endif diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h new file mode 100644 index 0000000000..c75ae95af5 --- /dev/null +++ b/components/openssl/include/platform/ssl_pm.h @@ -0,0 +1,41 @@ +#ifndef _SSL_PM_H_ +#define _SSL_PM_H_ + +#include "ssl_types.h" +#include "esp_common.h" + +void* ssl_zalloc(size_t size); +void *ssl_malloc(size_t size); +void ssl_free(void *p); +void* ssl_memcpy(void *to, const void *from, size_t size); + +int ssl_pm_new(SSL *ssl); +void ssl_pm_free(SSL *ssl); + +int ssl_pm_handshake(SSL *ssl); +int ssl_pm_shutdown(SSL *ssl); +int ssl_pm_clear(SSL *ssl); + +int ssl_pm_read(SSL *ssl, void *buffer, int len); +int ssl_pm_send(SSL *ssl, const void *buffer, int len); +int ssl_pm_pending(const SSL *ssl); + +void ssl_pm_set_fd(SSL *ssl, int fd, int mode); +int ssl_pm_get_fd(const SSL *ssl, int mode); + +OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl); + +void ssl_pm_set_bufflen(SSL *ssl, int len); + +void* x509_pm_new(void); +void x509_pm_free(void *pm); +int x509_pm_load_crt(void *pm, const unsigned char *buffer, int len); +void x509_pm_unload_crt(void *pm); +void x509_pm_start_ca(void *ssl, void *pm); + +void* pkey_pm_new(void); +void pkey_pm_free(void *pm); +int pkey_pm_load_crt(void *pm, const unsigned char *buffer, int len); +void pkey_pm_unload_crt(void *pm); + +#endif diff --git a/components/openssl/library/Makefile b/components/openssl/library/Makefile new file mode 100644 index 0000000000..10f4067c64 --- /dev/null +++ b/components/openssl/library/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = liblibrary.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c new file mode 100644 index 0000000000..10f723bfcb --- /dev/null +++ b/components/openssl/library/ssl_cert.c @@ -0,0 +1,28 @@ +#include "ssl_cert.h" +#include "ssl_pm.h" + +CERT *ssl_cert_new(void) +{ + return ssl_zalloc(sizeof(CERT)); +} + +void ssl_cert_free(CERT *c) +{ + if (c->x509) + X509_free(c->x509); + + if (c->pkey) + EVP_PKEY_free(c->pkey); + + ssl_free(c); +} + +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(x); + + ctx->client_CA = x; + + return 1; +} diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c new file mode 100644 index 0000000000..ad78f3961a --- /dev/null +++ b/components/openssl/library/ssl_lib.c @@ -0,0 +1,1622 @@ +#include "ssl_lib.h" +#include "ssl_pkey.h" +#include "ssl_cert.h" +#include "ssl_dbg.h" +#include "ssl_pm.h" + +#define SSL_SEND_DATA_MAX_LENGTH 1460 + +static int ossl_statem_in_error(const SSL *ssl) +{ + if (ssl->statem.state == MSG_FLOW_ERROR) + return 1; + + return 0; +} + +/* + * SSL_get_error - get SSL error code + * + * @param ssl - SSL point + * @param ret_code - SSL return code + * + * @return SSL error number + */ +int SSL_get_error(const SSL *ssl, int ret_code) +{ + int ret = SSL_ERROR_SYSCALL; + + SSL_ASSERT(ssl); + + if (ret_code > 0) + ret = SSL_ERROR_NONE; + else if (ret_code < 0) + { + if (SSL_want_read(ssl)) + ret = SSL_ERROR_WANT_READ; + else if (SSL_want_write(ssl)) + ret = SSL_ERROR_WANT_WRITE; + else + ret = SSL_ERROR_SYSCALL; //unknown + } + else // ret_code == 0 + { + if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) + ret = SSL_ERROR_ZERO_RETURN; + else + ret = SSL_ERROR_SYSCALL; + } + + return ret; +} + +/* + * SSL_get_state - get the SSL state + * + * @param ssl - SSL point + * + * @return SSL state + */ +OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) +{ + OSSL_HANDSHAKE_STATE state; + + SSL_ASSERT(ssl); + + state = ssl->method->func->ssl_get_state(ssl); + + return state; +} + +/* + * SSL_CTX_new - create a SSL context + * + * @param method - the SSL context configuration file + * + * @return the context point, if create failed return NULL + */ +SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) +{ + int ret; + SSL_CTX *ctx; + CERT *cert; + X509 *client_ca; + + if (!method) SSL_RET(go_failed1, "method\n"); + + client_ca = sk_X509_NAME_new_null(); + if (!client_ca) + SSL_ERR(-2, go_failed1, "ssl_ctx_new:ctx:[%d]\n", ret); + + cert = ssl_cert_new(); + if (!cert) + SSL_ERR(-2, go_failed2, "ssl_ctx_new:ctx:[%d]\n", ret); + + ctx = (SSL_CTX *)ssl_zalloc(sizeof(SSL_CTX)); + if (!ctx) + SSL_ERR(-2, go_failed3, "ssl_ctx_new:ctx:[%d]\n", ret); + + ctx->method = method; + ctx->cert = cert; + ctx->client_CA = client_ca; + + ctx->version = method->version; + + return ctx; + +go_failed3: + ssl_cert_free(cert); +go_failed2: + X509_free(client_ca); +go_failed1: + return NULL; +} + +/* + * SSL_CTX_free - free a SSL context + * + * @param method - the SSL context point + * + * @return none + */ +void SSL_CTX_free(SSL_CTX* ctx) +{ + SSL_ASSERT(ctx); + + ssl_cert_free(ctx->cert); + + X509_free(ctx->client_CA); + + ssl_free(ctx); +} + +/* + * SSL_CTX_set_ssl_version - set the SSL context version + * + * @param ctx - SSL context point + * @param meth - SSL method point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(meth); + + ctx->method = meth; + + return 1; +} + +/* + * SSL_CTX_get_ssl_method - get the SSL context current method + * + * @param ctx - SSL context point + * + * @return the SSL context current method + */ +const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) +{ + SSL_ASSERT(ctx); + + return ctx->method; +} + +/* + * SSL_new - create a SSL + * + * @param ctx - the SSL context point + * + * @return the SSL point or NULL if failed + */ +SSL *SSL_new(SSL_CTX *ctx) +{ + int ret; + void *ssl_pm; + SSL *ssl; + + if (!ctx) + SSL_RET(failed1, "ctx:NULL\n"); + + ssl = (SSL *)ssl_zalloc(sizeof(SSL)); + if (!ssl) + SSL_RET(failed1, "ssl_zalloc\n"); + + ssl->ctx = ctx; + ssl->method = ctx->method; + + ssl->version = ctx->version; + ssl->options = ctx->options; + + ret = ssl->method->func->ssl_new(ssl); + if (ret) + SSL_RET(failed2, "ssl_new\n"); + + return ssl; + +failed2: + ssl_free(ssl); +failed1: + return NULL; +} + +/* + * SSL_free - free the SSL + * + * @param ssl - the SSL point + * + * @return none + */ +void SSL_free(SSL *ssl) +{ + SSL_ASSERT(ssl); + + ssl->method->func->ssl_free(ssl); + + ssl_free(ssl); +} + +/* + * SSL_do_handshake - perform the SSL handshake + * + * @param ssl - SSL point + * + * @return + * 1 : OK + * 0 : failed + * -1 : a error catch + */ +int SSL_do_handshake(SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + ret = ssl->method->func->ssl_handshake(ssl); + + return ret; +} + +/* + * SSL_connect - connect to the remote SSL server + * + * @param ssl - the SSL point + * + * @return + * 1 : OK + * -1 : failed + */ +int SSL_connect(SSL *ssl) +{ + SSL_ASSERT(ssl); + + return SSL_do_handshake(ssl); +} + +/* + * SSL_accept - accept the remote connection + * + * @param ssl - the SSL point + * + * @return + * 1 : OK + * -1 : failed + */ +int SSL_accept(SSL *ssl) +{ + SSL_ASSERT(ssl); + + return SSL_do_handshake(ssl); +} + +/* + * SSL_shutdown - shutdown the connection + * + * @param ssl - the SSL point + * + * @return + * 1 : OK + * 0 : shutdown is not finished + * -1 : an error catch + */ +int SSL_shutdown(SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + if (SSL_get_state(ssl) != TLS_ST_OK) return 0; + + ret = ssl->method->func->ssl_shutdown(ssl); + + return ret; +} + +/* + * SSL_clear - reset the SSL + * + * @param ssl - SSL point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_clear(SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + ret = SSL_shutdown(ssl); + if (1 != ret) + SSL_ERR(0, go_failed1, "SSL_shutdown\n"); + + ssl->method->func->ssl_free(ssl); + + ret = ssl->method->func->ssl_new(ssl); + if (!ret) + SSL_ERR(0, go_failed1, "ssl_new\n"); + + return 1; + +go_failed1: + return ret; +} + +/* + * SSL_read - read data from to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the received data buffer point + * @param len - the received data length + * + * @return + * > 0 : OK, and return received data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_read(SSL *ssl, void *buffer, int len) +{ + int ret; + + SSL_ASSERT(ssl); + SSL_ASSERT(buffer); + SSL_ASSERT(len); + + ret = ssl->method->func->ssl_read(ssl, buffer, len); + + return ret; +} + +/* + * SSL_write - send the data to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the send data buffer point + * @param len - the send data length + * + * @return + * > 0 : OK, and return sent data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_write(SSL *ssl, const void *buffer, int len) +{ + int ret; + int send_bytes; + const unsigned char *pbuf; + + SSL_ASSERT(ssl); + SSL_ASSERT(buffer); + SSL_ASSERT(len); + + send_bytes = len; + pbuf = (const unsigned char *)buffer; + + do { + int bytes; + + if (send_bytes > SSL_SEND_DATA_MAX_LENGTH) + bytes = SSL_SEND_DATA_MAX_LENGTH; + else + bytes = send_bytes; + + ret = ssl->method->func->ssl_send(ssl, buffer, len); + if (ret > 0) { + pbuf += ret; + send_bytes -= ret; + } + } while (ret > 0 && send_bytes); + + send_bytes = len - send_bytes; + if (send_bytes >= 0) + ret = send_bytes; + else + ret = -1; + + return ret; +} + +/* + * SSL_get_SSL_CTX - get SSL context of the SSL + * + * @param ssl - SSL point + * + * @return SSL context + */ +SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->ctx; +} + +/* + * SSL_CTX_get_ssl_method - get the SSL current method + * + * @param ssl - SSL point + * + * @return the SSL current method + */ +const SSL_METHOD *SSL_get_ssl_method(SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->method; +} + +/* + * SSL_set_ssl_method - set the SSL method + * + * @param ssl - SSL point + * @param meth - SSL method point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method) +{ + int ret; + + SSL_ASSERT(ssl); + SSL_ASSERT(method); + + if (ssl->version != method->version) { + + ret = SSL_shutdown(ssl); + if (1 != ret) + SSL_ERR(0, go_failed1, "SSL_shutdown\n"); + + ssl->method->func->ssl_free(ssl); + + ssl->method = method; + + ret = ssl->method->func->ssl_new(ssl); + if (!ret) + SSL_ERR(0, go_failed1, "ssl_new\n"); + } else { + ssl->method = method; + } + + + return 1; + +go_failed1: + return ret; +} + +/* + * SSL_get_shutdown - get SSL shutdown mode + * + * @param ssl - SSL point + * + * @return shutdown mode + */ +int SSL_get_shutdown(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->shutdown; +} + +/* + * SSL_set_quiet_shutdown - set SSL shutdown mode + * + * @param ssl - SSL point + * @param mode - shutdown mode + * + * @return none + */ +void SSL_set_shutdown(SSL *ssl, int mode) +{ + SSL_ASSERT(ssl); + + ssl->shutdown = mode; +} + + +/* + * SSL_pending - get the number of the bytes to be read + * + * @param ssl - SSL point + * + * @return number of the bytes + */ +int SSL_pending(const SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + ret = ssl->method->func->ssl_pending(ssl); + + return ret; +} + +/* + * SSL_has_pending - check if some data can be read + * + * @param ssl - SSL point + * + * @return + * 1 : there are bytes to be read + * 0 : no data + */ +int SSL_has_pending(const SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + if (SSL_pending(ssl)) + ret = 1; + else + ret = 0; + + return ret; +} + +/* + * SSL_CTX_clear_options - clear the SSL context option bit of "op" + * + * @param ctx - SSL context point + * @param op - option + * + * @return SSL context option + */ +unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op) +{ + return ctx->options &= ~op; +} + +/* + * SSL_CTX_clear_options - get the SSL context option + * + * @param ctx - SSL context point + * @param op - option + * + * @return SSL context option + */ +unsigned long SSL_CTX_get_options(SSL_CTX *ctx) +{ + return ctx->options; +} + +/* + * SSL_CTX_set_option - set the option of the SSL context + * + * @param ctx - the SSL context + * + * @return the SSL context option + * + */ +unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt) +{ + return ctx->options |= opt; +} + +/* + * SSL_clear_options - clear SSL option + * + * @param ssl - SSL point + * @param op - clear option + * + * @return SSL option + */ +unsigned long SSL_clear_options(SSL *ssl, unsigned long op) +{ + SSL_ASSERT(ssl); + + return ssl->options & ~op; +} + +/* + * SSL_clear_options - get SSL option + * + * @param ssl - SSL point + * + * @return SSL option + */ +unsigned long SSL_get_options(SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->options; +} + +/* + * SSL_clear_options - clear SSL option + * + * @param ssl - SSL point + * @param op - setting option + * + * @return SSL option + */ +unsigned long SSL_set_options(SSL *ssl, unsigned long op) +{ + SSL_ASSERT(ssl); + + return ssl->options |= op; +} + +/* + * SSL_get_fd - get the socket handle of the SSL + * + * @param ssl - SSL point + * + * @return + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_fd(const SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + ret = ssl->method->func->ssl_get_fd(ssl, 0); + + return ret; +} + +/* + * SSL_get_rfd - get the read only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_rfd(const SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + ret = ssl->method->func->ssl_get_fd(ssl, 0); + + return ret; +} + +/* + * SSL_get_wfd - get the write only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_wfd(const SSL *ssl) +{ + int ret; + + SSL_ASSERT(ssl); + + ret = ssl->method->func->ssl_get_fd(ssl, 0); + + return ret; +} + +/* + * SSL_set_fd - bind the socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_set_fd(SSL *ssl, int fd) +{ + int ret; + + SSL_ASSERT(ssl); + SSL_ASSERT(fd >= 0); + + ssl->method->func->ssl_set_fd(ssl, fd, 0); + + return 1; +} + +/* + * SSL_set_fd - bind the read only socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_set_rfd(SSL *ssl, int fd) +{ + int ret; + + SSL_ASSERT(ssl); + SSL_ASSERT(fd >= 0); + + ssl->method->func->ssl_set_fd(ssl, fd, 0); + + return 1; +} + +/* + * SSL_set_fd - bind the write only socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_set_wfd(SSL *ssl, int fd) +{ + int ret; + + SSL_ASSERT(ssl); + SSL_ASSERT(fd >= 0); + + ssl->method->func->ssl_set_fd(ssl, fd, 0); + + return 1; +} + +/* + * SSL_version - get SSL version + * + * @param ssl - SSL point + * + * @return SSL version + */ +int SSL_version(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->version; +} + +/* + * ssl_protocol_to_string - get the SSL version string + * + * @param version - the SSL version + * + * @return the SSL version string + */ +static const char* ssl_protocol_to_string(int version) +{ + const char *str; + + if (version == TLS1_2_VERSION) + str = "TLSv1.2"; + else if (version == TLS1_1_VERSION) + str = "TLSv1.1"; + else if (version == TLS1_VERSION) + str = "TLSv1"; + else if (version == SSL3_VERSION) + str = "SSLv3"; + else + str = "unknown"; + + return str; +} + +/* + * SSL_get_version - get the SSL current version + * + * @param ssl - SSL point + * + * @return the version string + */ +const char *SSL_get_version(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl_protocol_to_string(SSL_version(ssl)); +} + +/* + * SSL_alert_desc_string - get alert description string + * + * @param value - alert value + * + * @return alert description string + */ +const char* SSL_alert_desc_string(int value) +{ + const char *str; + + switch (value & 0xff) + { + case SSL3_AD_CLOSE_NOTIFY: + str = "CN"; + break; + case SSL3_AD_UNEXPECTED_MESSAGE: + str = "UM"; + break; + case SSL3_AD_BAD_RECORD_MAC: + str = "BM"; + break; + case SSL3_AD_DECOMPRESSION_FAILURE: + str = "DF"; + break; + case SSL3_AD_HANDSHAKE_FAILURE: + str = "HF"; + break; + case SSL3_AD_NO_CERTIFICATE: + str = "NC"; + break; + case SSL3_AD_BAD_CERTIFICATE: + str = "BC"; + break; + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + str = "UC"; + break; + case SSL3_AD_CERTIFICATE_REVOKED: + str = "CR"; + break; + case SSL3_AD_CERTIFICATE_EXPIRED: + str = "CE"; + break; + case SSL3_AD_CERTIFICATE_UNKNOWN: + str = "CU"; + break; + case SSL3_AD_ILLEGAL_PARAMETER: + str = "IP"; + break; + case TLS1_AD_DECRYPTION_FAILED: + str = "DC"; + break; + case TLS1_AD_RECORD_OVERFLOW: + str = "RO"; + break; + case TLS1_AD_UNKNOWN_CA: + str = "CA"; + break; + case TLS1_AD_ACCESS_DENIED: + str = "AD"; + break; + case TLS1_AD_DECODE_ERROR: + str = "DE"; + break; + case TLS1_AD_DECRYPT_ERROR: + str = "CY"; + break; + case TLS1_AD_EXPORT_RESTRICTION: + str = "ER"; + break; + case TLS1_AD_PROTOCOL_VERSION: + str = "PV"; + break; + case TLS1_AD_INSUFFICIENT_SECURITY: + str = "IS"; + break; + case TLS1_AD_INTERNAL_ERROR: + str = "IE"; + break; + case TLS1_AD_USER_CANCELLED: + str = "US"; + break; + case TLS1_AD_NO_RENEGOTIATION: + str = "NR"; + break; + case TLS1_AD_UNSUPPORTED_EXTENSION: + str = "UE"; + break; + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + str = "CO"; + break; + case TLS1_AD_UNRECOGNIZED_NAME: + str = "UN"; + break; + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + str = "BR"; + break; + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + str = "BH"; + break; + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + str = "UP"; + break; + default: + str = "UK"; + break; + } + + return str; +} + +/* + * SSL_alert_desc_string - get alert description long string + * + * @param value - alert value + * + * @return alert description long string + */ +const char* SSL_alert_desc_string_long(int value) +{ + const char *str; + + switch (value & 0xff) + { + case SSL3_AD_CLOSE_NOTIFY: + str = "close notify"; + break; + case SSL3_AD_UNEXPECTED_MESSAGE: + str = "unexpected_message"; + break; + case SSL3_AD_BAD_RECORD_MAC: + str = "bad record mac"; + break; + case SSL3_AD_DECOMPRESSION_FAILURE: + str = "decompression failure"; + break; + case SSL3_AD_HANDSHAKE_FAILURE: + str = "handshake failure"; + break; + case SSL3_AD_NO_CERTIFICATE: + str = "no certificate"; + break; + case SSL3_AD_BAD_CERTIFICATE: + str = "bad certificate"; + break; + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + str = "unsupported certificate"; + break; + case SSL3_AD_CERTIFICATE_REVOKED: + str = "certificate revoked"; + break; + case SSL3_AD_CERTIFICATE_EXPIRED: + str = "certificate expired"; + break; + case SSL3_AD_CERTIFICATE_UNKNOWN: + str = "certificate unknown"; + break; + case SSL3_AD_ILLEGAL_PARAMETER: + str = "illegal parameter"; + break; + case TLS1_AD_DECRYPTION_FAILED: + str = "decryption failed"; + break; + case TLS1_AD_RECORD_OVERFLOW: + str = "record overflow"; + break; + case TLS1_AD_UNKNOWN_CA: + str = "unknown CA"; + break; + case TLS1_AD_ACCESS_DENIED: + str = "access denied"; + break; + case TLS1_AD_DECODE_ERROR: + str = "decode error"; + break; + case TLS1_AD_DECRYPT_ERROR: + str = "decrypt error"; + break; + case TLS1_AD_EXPORT_RESTRICTION: + str = "export restriction"; + break; + case TLS1_AD_PROTOCOL_VERSION: + str = "protocol version"; + break; + case TLS1_AD_INSUFFICIENT_SECURITY: + str = "insufficient security"; + break; + case TLS1_AD_INTERNAL_ERROR: + str = "internal error"; + break; + case TLS1_AD_USER_CANCELLED: + str = "user canceled"; + break; + case TLS1_AD_NO_RENEGOTIATION: + str = "no renegotiation"; + break; + case TLS1_AD_UNSUPPORTED_EXTENSION: + str = "unsupported extension"; + break; + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + str = "certificate unobtainable"; + break; + case TLS1_AD_UNRECOGNIZED_NAME: + str = "unrecognized name"; + break; + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + str = "bad certificate status response"; + break; + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + str = "bad certificate hash value"; + break; + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + str = "unknown PSK identity"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +/* + * SSL_alert_type_string - get alert type string + * + * @param value - alert value + * + * @return alert type string + */ +const char *SSL_alert_type_string(int value) +{ + const char *str; + + switch (value >> 8) + { + case SSL3_AL_WARNING: + str = "W"; + break; + case SSL3_AL_FATAL: + str = "F"; + break; + default: + str = "U"; + break; + } + + return str; +} + +/* + * SSL_alert_type_string_long - get alert type long string + * + * @param value - alert value + * + * @return alert type long string + */ +const char *SSL_alert_type_string_long(int value) +{ + const char *str; + + switch (value >> 8) + { + case SSL3_AL_WARNING: + str = "warning"; + break; + case SSL3_AL_FATAL: + str = "fatal"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +/* + * SSL_rstate_string - get the state string where SSL is reading + * + * @param ssl - SSL point + * + * @return state string + */ +const char *SSL_rstate_string(SSL *ssl) +{ + const char *str; + + SSL_ASSERT(ssl); + + switch (ssl->rlayer.rstate) + { + case SSL_ST_READ_HEADER: + str = "RH"; + break; + case SSL_ST_READ_BODY: + str = "RB"; + break; + case SSL_ST_READ_DONE: + str = "RD"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +/* + * SSL_rstate_string_long - get the statement long string where SSL is reading + * + * @param ssl - SSL point + * + * @return statement long string + */ +const char *SSL_rstate_string_long(SSL *ssl) +{ + const char *str = "unknown"; + + SSL_ASSERT(ssl); + + switch (ssl->rlayer.rstate) + { + case SSL_ST_READ_HEADER: + str = "read header"; + break; + case SSL_ST_READ_BODY: + str = "read body"; + break; + case SSL_ST_READ_DONE: + str = "read done"; + break; + default: + break; + } + + return str; +} + +/* + * SSL_state_string - get SSL statement string + * + * @param ssl - SSL point + * + * @return SSL statement string + */ +char *SSL_state_string(const SSL *ssl) +{ + char *str = "UNKWN "; + + SSL_ASSERT(ssl); + + if (ossl_state_in_error(ssl)) + str = "SSLERR"; + else + { + switch (SSL_get_state(ssl)) + { + case TLS_ST_BEFORE: + str = "PINIT "; + break; + case TLS_ST_OK: + str = "SSLOK "; + break; + case TLS_ST_CW_CLNT_HELLO: + str = "TWCH"; + break; + case TLS_ST_CR_SRVR_HELLO: + str = "TRSH"; + break; + case TLS_ST_CR_CERT: + str = "TRSC"; + break; + case TLS_ST_CR_KEY_EXCH: + str = "TRSKE"; + break; + case TLS_ST_CR_CERT_REQ: + str = "TRCR"; + break; + case TLS_ST_CR_SRVR_DONE: + str = "TRSD"; + break; + case TLS_ST_CW_CERT: + str = "TWCC"; + break; + case TLS_ST_CW_KEY_EXCH: + str = "TWCKE"; + break; + case TLS_ST_CW_CERT_VRFY: + str = "TWCV"; + break; + case TLS_ST_SW_CHANGE: + case TLS_ST_CW_CHANGE: + str = "TWCCS"; + break; + case TLS_ST_SW_FINISHED: + case TLS_ST_CW_FINISHED: + str = "TWFIN"; + break; + case TLS_ST_SR_CHANGE: + case TLS_ST_CR_CHANGE: + str = "TRCCS"; + break; + case TLS_ST_SR_FINISHED: + case TLS_ST_CR_FINISHED: + str = "TRFIN"; + break; + case TLS_ST_SW_HELLO_REQ: + str = "TWHR"; + break; + case TLS_ST_SR_CLNT_HELLO: + str = "TRCH"; + break; + case TLS_ST_SW_SRVR_HELLO: + str = "TWSH"; + break; + case TLS_ST_SW_CERT: + str = "TWSC"; + break; + case TLS_ST_SW_KEY_EXCH: + str = "TWSKE"; + break; + case TLS_ST_SW_CERT_REQ: + str = "TWCR"; + break; + case TLS_ST_SW_SRVR_DONE: + str = "TWSD"; + break; + case TLS_ST_SR_CERT: + str = "TRCC"; + break; + case TLS_ST_SR_KEY_EXCH: + str = "TRCKE"; + break; + case TLS_ST_SR_CERT_VRFY: + str = "TRCV"; + break; + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + str = "DRCHV"; + break; + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + str = "DWCHV"; + break; + default: + break; + } + } + + return str; +} + +/* + * SSL_state_string_long - get SSL statement long string + * + * @param ssl - SSL point + * + * @return SSL statement long string + */ +char *SSL_state_string_long(const SSL *ssl) +{ + char *str = "UNKWN "; + + SSL_ASSERT(ssl); + + if (ossl_statem_in_error(ssl)) + str = "SSLERR"; + else + { + switch (SSL_get_state(ssl)) + { + case TLS_ST_BEFORE: + str = "before SSL initialization"; + break; + case TLS_ST_OK: + str = "SSL negotiation finished successfully"; + break; + case TLS_ST_CW_CLNT_HELLO: + str = "SSLv3/TLS write client hello"; + break; + case TLS_ST_CR_SRVR_HELLO: + str = "SSLv3/TLS read server hello"; + break; + case TLS_ST_CR_CERT: + str = "SSLv3/TLS read server certificate"; + break; + case TLS_ST_CR_KEY_EXCH: + str = "SSLv3/TLS read server key exchange"; + break; + case TLS_ST_CR_CERT_REQ: + str = "SSLv3/TLS read server certificate request"; + break; + case TLS_ST_CR_SESSION_TICKET: + str = "SSLv3/TLS read server session ticket"; + break; + case TLS_ST_CR_SRVR_DONE: + str = "SSLv3/TLS read server done"; + break; + case TLS_ST_CW_CERT: + str = "SSLv3/TLS write client certificate"; + break; + case TLS_ST_CW_KEY_EXCH: + str = "SSLv3/TLS write client key exchange"; + break; + case TLS_ST_CW_CERT_VRFY: + str = "SSLv3/TLS write certificate verify"; + break; + case TLS_ST_CW_CHANGE: + case TLS_ST_SW_CHANGE: + str = "SSLv3/TLS write change cipher spec"; + break; + case TLS_ST_CW_FINISHED: + case TLS_ST_SW_FINISHED: + str = "SSLv3/TLS write finished"; + break; + case TLS_ST_CR_CHANGE: + case TLS_ST_SR_CHANGE: + str = "SSLv3/TLS read change cipher spec"; + break; + case TLS_ST_CR_FINISHED: + case TLS_ST_SR_FINISHED: + str = "SSLv3/TLS read finished"; + break; + case TLS_ST_SR_CLNT_HELLO: + str = "SSLv3/TLS read client hello"; + break; + case TLS_ST_SW_HELLO_REQ: + str = "SSLv3/TLS write hello request"; + break; + case TLS_ST_SW_SRVR_HELLO: + str = "SSLv3/TLS write server hello"; + break; + case TLS_ST_SW_CERT: + str = "SSLv3/TLS write certificate"; + break; + case TLS_ST_SW_KEY_EXCH: + str = "SSLv3/TLS write key exchange"; + break; + case TLS_ST_SW_CERT_REQ: + str = "SSLv3/TLS write certificate request"; + break; + case TLS_ST_SW_SESSION_TICKET: + str = "SSLv3/TLS write session ticket"; + break; + case TLS_ST_SW_SRVR_DONE: + str = "SSLv3/TLS write server done"; + break; + case TLS_ST_SR_CERT: + str = "SSLv3/TLS read client certificate"; + break; + case TLS_ST_SR_KEY_EXCH: + str = "SSLv3/TLS read client key exchange"; + break; + case TLS_ST_SR_CERT_VRFY: + str = "SSLv3/TLS read certificate verify"; + break; + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + str = "DTLS1 read hello verify request"; + break; + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + str = "DTLS1 write hello verify request"; + break; + default: + break; + } + } + + return str; +} + +/* + * SSL_CTX_set_default_read_buffer_len - set the SSL context read buffer length + * + * @param ctx - SSL context point + * @param len - read buffer length + * + * @return none + */ +void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(len); + + ctx->method->func->ssl_set_bufflen(NULL, len); +} + +/* + * SSL_set_default_read_buffer_len - set the SSL read buffer length + * + * @param ssl - SSL point + * @param len - read buffer length + * + * @return none + */ +void SSL_set_default_read_buffer_len(SSL *ssl, size_t len) +{ + SSL_ASSERT(ssl); + SSL_ASSERT(len); + + ssl->method->func->ssl_set_bufflen(ssl, len); +} + +/* + * SSL_set_info_callback - set the SSL information callback function + * + * @param ssl - SSL point + * @param cb - information callback function + * + * @return none + */ +void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)) +{ + SSL_ASSERT(ssl); + + ssl->info_callback = cb; +} + +/* + * SSL_CTX_up_ref - add SSL context reference count by '1' + * + * @param ctx - SSL context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_up_ref(SSL_CTX *ctx) +{ + SSL_ASSERT(ctx); + + /* no support multi-thread SSL here */ + ctx->references++; + + return 1; +} + +/* + * SSL_set_security_level - set the SSL security level + * + * @param ssl - SSL point + * @param level - security level + * + * @return none + */ +void SSL_set_security_level(SSL *ssl, int level) +{ + SSL_ASSERT(ssl); + + ssl->cert->sec_level = level; +} + +/* + * SSL_get_security_level - get the SSL security level + * + * @param ssl - SSL point + * + * @return security level + */ +int SSL_get_security_level(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->cert->sec_level; +} + +/* + * SSL_CTX_get_verify_mode - get the SSL verifying mode of the SSL context + * + * @param ctx - SSL context point + * + * @return verifying mode + */ +int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) +{ + SSL_ASSERT(ctx); + + return ctx->verify_mode; +} + +/* + * SSL_CTX_set_timeout - set the session timeout time + * + * @param ctx - SSL context point + * @param t - new session timeout time + * + * @return old session timeout time + */ +long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) +{ + long l; + + SSL_ASSERT(ctx); + + l = ctx->session_timeout; + ctx->session_timeout = t; + + return l; +} + +/* + * SSL_CTX_get_timeout - get the session timeout time + * + * @param ctx - SSL context point + * + * @return current session timeout time + */ +long SSL_CTX_get_timeout(const SSL_CTX *ctx) +{ + SSL_ASSERT(ctx); + + return ctx->session_timeout; +} + +/* + * SSL_set_read_ahead - set the SSL if we can read as many as data + * + * @param ssl - SSL point + * @param yes - enable the function + * + * @return none + */ +void SSL_set_read_ahead(SSL *ssl, int yes) +{ + SSL_ASSERT(ssl); + + ssl->rlayer.read_ahead = yes; +} + +/* + * SSL_set_read_ahead - set the SSL context if we can read as many as data + * + * @param ctx - SSL context point + * @param yes - enable the function + * + * @return none + */ +void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) +{ + SSL_ASSERT(ctx); + + ctx->read_ahead = yes; +} + +/* + * SSL_set_read_ahead - get the SSL ahead signal if we can read as many as data + * + * @param ssl - SSL point + * + * @return SSL context ahead signal + */ +int SSL_get_read_ahead(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->rlayer.read_ahead; +} + +/* + * SSL_set_read_ahead - get the SSL context ahead signal if we can read as many as data + * + * @param ctx - SSL context point + * + * @return SSL context ahead signal + */ +long SSL_CTX_get_read_ahead(SSL_CTX *ctx) +{ + SSL_ASSERT(ctx); + + return ctx->read_ahead; +} + +/* + * SSL_CTX_get_ciphers - check if the SSL context can read as many as data + * + * @param ctx - SSL context point + * + * @return + * 1 : Yes + * 0 : No + */ +long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx) +{ + SSL_ASSERT(ctx); + + return ctx->read_ahead; +} + +/* + * SSL_set_time - set SSL session time + * + * @param ssl - SSL point + * @param t - session time + * + * @return session time + */ +long SSL_set_time(SSL *ssl, long t) +{ + SSL_ASSERT(ssl); + + ssl->session.time = t; + + return t; +} + +/* + * SSL_set_time - set SSL session timeout time + * + * @param ssl - SSL point + * @param t - session timeout time + * + * @return session timeout time + */ +long SSL_set_timeout(SSL *ssl, long t) +{ + SSL_ASSERT(ssl); + + ssl->session.timeout = t; + + return t; +} diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c new file mode 100644 index 0000000000..502262f7e9 --- /dev/null +++ b/components/openssl/library/ssl_methods.c @@ -0,0 +1,43 @@ +#include "ssl_lib.h" +#include "ssl_methods.h" +#include "ssl_pm.h" + +IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, + ssl_pm_new, ssl_pm_free, + ssl_pm_handshake, ssl_pm_shutdown, ssl_pm_clear, + ssl_pm_read, ssl_pm_send, ssl_pm_pending, + ssl_pm_set_fd, ssl_pm_get_fd, + ssl_pm_set_bufflen, + ssl_pm_get_state); + +IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 0, TLS_method_func, TLS_client_method); + +IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 0, TLS_method_func, TLSv1_2_client_method); + +IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 0, TLS_method_func, TLSv1_1_client_method); + +IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_client_method); + +IMPLEMENT_SSL_METHOD(SSL3_VERSION, 0, TLS_method_func, SSLv3_client_method); + + +IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 1, TLS_method_func, TLS_server_method); + +IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 1, TLS_method_func, TLSv1_1_server_method); + +IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 1, TLS_method_func, TLSv1_2_server_method); + +IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_server_method); + +IMPLEMENT_SSL_METHOD(SSL3_VERSION, 1, TLS_method_func, SSLv3_server_method); + + +IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, -1, TLS_method_func, TLS_method); + +IMPLEMENT_SSL_METHOD(TLS1_2_VERSION, -1, TLS_method_func, TLSv1_2_method); + +IMPLEMENT_SSL_METHOD(TLS1_1_VERSION, -1, TLS_method_func, TLSv1_1_method); + +IMPLEMENT_SSL_METHOD(TLS1_VERSION, -1, TLS_method_func, TLSv1_method); + +IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c new file mode 100644 index 0000000000..2a170716c0 --- /dev/null +++ b/components/openssl/library/ssl_pkey.c @@ -0,0 +1,50 @@ +#include "ssl_lib.h" +#include "ssl_pkey.h" +#include "ssl_dbg.h" +#include "ssl_pm.h" + +EVP_PKEY *d2i_PrivateKey(int type, + EVP_PKEY **a, + const unsigned char **pp, + long length) +{ + EVP_PKEY *pkey; + void *pkey_pm; + int ret; + + SSL_ASSERT(pp); + SSL_ASSERT(*pp); + SSL_ASSERT(length); + + pkey = ssl_malloc(sizeof(EVP_PKEY)); + if (!pkey) + SSL_RET(failed1, "ssl_malloc\n"); + + pkey_pm = pkey_pm_new(); + if (!pkey_pm) + SSL_RET(failed2, "pkey_pm_new\n"); + + ret = pkey_pm_load_crt(pkey_pm, *pp, length); + if (ret) + SSL_RET(failed3, "pkey_pm_load_crt\n"); + + pkey->pkey_pm = pkey_pm; + if (a) + *a = pkey; + + return pkey; + +failed3: + pkey_pm_free(pkey_pm); +failed2: + ssl_free(pkey); +failed1: + return NULL; +} + +void EVP_PKEY_free(EVP_PKEY *x) +{ + pkey_pm_unload_crt(x->pkey_pm); + pkey_pm_free(x->pkey_pm); + ssl_free(x); +} diff --git a/components/openssl/library/ssl_rsa.c b/components/openssl/library/ssl_rsa.c new file mode 100644 index 0000000000..9088f67f57 --- /dev/null +++ b/components/openssl/library/ssl_rsa.c @@ -0,0 +1,70 @@ +#include "ssl_lib.h" +#include "ssl_rsa.h" +#include "ssl_pkey.h" +#include "ssl_x509.h" +#include "ssl_dbg.h" +#include "ssl_pm.h" + +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(x); + + ctx->cert->x509 = x; + + return 1; +} + +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, + const unsigned char *d) +{ + int ret; + X509 *cert; + + cert = d2i_X509(NULL, d, len); + if (!cert) + SSL_RET(failed1, "d2i_X509\n"); + + ret = SSL_CTX_use_certificate(ctx, cert); + if (!ret) + SSL_RET(failed2, "SSL_CTX_use_certificate\n"); + + return 1; + +failed2: + X509_free(cert); +failed1: + return 0; +} + +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(pkey); + + ctx->cert->pkey = pkey; + + return 1; +} + +int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, + const unsigned char *d, long len) +{ + int ret; + EVP_PKEY *pkey; + + pkey = d2i_PrivateKey(0, NULL, &d, len); + if (!pkey) + SSL_RET(failed1, "d2i_PrivateKey\n"); + + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + if (!ret) + SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); + + return 1; + +failed2: + EVP_PKEY_free(pkey); +failed1: + return 0; +} diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c new file mode 100644 index 0000000000..23aa00681e --- /dev/null +++ b/components/openssl/library/ssl_x509.c @@ -0,0 +1,54 @@ +#include "ssl_x509.h" +#include "ssl_dbg.h" +#include "ssl_pm.h" + +X509* sk_X509_NAME_new_null(void) +{ + return ssl_malloc(sizeof(X509)); +} + +X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) +{ + X509 *x509_crt; + void *x509_pm; + int ret; + + SSL_ASSERT(cert); + SSL_ASSERT(buffer); + SSL_ASSERT(len); + + x509_crt = sk_X509_NAME_new_null(); + if (!x509_crt) + SSL_RET(failed1, ""); + + x509_pm = x509_pm_new(); + if (!x509_pm) + SSL_RET(failed2, ""); + + ret = x509_pm_load_crt(x509_pm, buffer, len); + if (ret) + SSL_RET(failed3, ""); + + x509_crt->x509_pm = x509_pm; + if (cert) + *cert = x509_crt; + + return x509_crt; + +failed3: + x509_pm_free(x509_pm); +failed2: + ssl_free(x509_crt); +failed1: + return NULL; +} + +void X509_free(X509 *cert) +{ + if (cert->x509_pm) { + x509_pm_unload_crt(cert->x509_pm); + x509_pm_free(cert->x509_pm); + } + ssl_free(cert); +}; + diff --git a/components/openssl/platform/Makefile b/components/openssl/platform/Makefile new file mode 100644 index 0000000000..749b4787ca --- /dev/null +++ b/components/openssl/platform/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libplatform.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c new file mode 100644 index 0000000000..831420180c --- /dev/null +++ b/components/openssl/platform/ssl_pm.c @@ -0,0 +1,422 @@ +#include "ssl_pm.h" +#include "ssl_dbg.h" + +/* mbedtls include */ +#include "mbedtls/platform.h" +#include "mbedtls/net.h" +#include "mbedtls/debug.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" + +struct ssl_pm +{ + /* local socket file description */ + mbedtls_net_context fd; + /* remote client socket file description */ + mbedtls_net_context cl_fd; + + mbedtls_ssl_config conf; + + mbedtls_ctr_drbg_context ctr_drbg; + + mbedtls_ssl_context ssl; + + mbedtls_entropy_context entropy; +}; + +struct x509_pm +{ + mbedtls_x509_crt x509_crt; +}; + +struct pkey_pm +{ + mbedtls_pk_context pkey; +}; + + +unsigned int max_content_len; + + +/*********************************************************************************************/ +/********************************* SSL general interface *************************************/ + +void* ssl_zalloc(size_t size) +{ + void *p = malloc(size); + + if (p) + memset(p, 0, size); + + return p; +} + +void *ssl_malloc(size_t size) +{ + return zalloc(size); +} + +void ssl_free(void *p) +{ + free(p); +} + +void* ssl_memcpy(void *to, const void *from, size_t size) +{ + return memcpy(to, from, size); +} + +void ssl_speed_up_enter(void) +{ + +} + +void ssl_speed_up_exit(void) +{ + +} + +/*********************************************************************************************/ +/************************************ SSL arch interface *************************************/ + +int ssl_pm_new(SSL *ssl) +{ + struct ssl_pm *ssl_pm; + int ret; + + char *pers; + int endpoint; + + SSL_CTX *ctx = ssl->ctx; + const SSL_METHOD *method = ssl->method; + + ssl_pm = malloc(sizeof(struct ssl_pm)); + if (!ssl_pm) + return -1; + + if (method->endpoint) { + pers = "server"; + endpoint = MBEDTLS_SSL_IS_SERVER; + } else { + pers = "client"; + endpoint = MBEDTLS_SSL_IS_CLIENT; + } + + //max_content_len = 4096; + + mbedtls_net_init(&ssl_pm->fd); + mbedtls_net_init(&ssl_pm->cl_fd); + + mbedtls_ssl_config_init(&ssl_pm->conf); + mbedtls_ctr_drbg_init(&ssl_pm->ctr_drbg); + mbedtls_entropy_init(&ssl_pm->entropy); + mbedtls_ssl_init(&ssl_pm->ssl); + + ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, (const unsigned char *)pers, strlen(pers)); + if (ret) + SSL_ERR(ret, failed1, "mbedtls_ctr_drbg_seed:[-0x%x]\n", -ret); + + ret = mbedtls_ssl_config_defaults(&ssl_pm->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (ret) + SSL_ERR(ret, failed2, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); + + mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); + mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL); + + if (ctx->client_CA->x509_pm) { + struct x509_pm *x509_pm = (struct x509_pm *)ctx->client_CA->x509_pm; + + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &x509_pm->x509_crt, NULL); + mbedtls_ssl_conf_authmode(&ssl_pm->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + } else { + mbedtls_ssl_conf_authmode(&ssl_pm->conf, MBEDTLS_SSL_VERIFY_NONE); + } + if (ctx->cert->x509 && + ctx->cert->pkey) { + struct x509_pm *x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm; + struct pkey_pm *pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm; + + ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); + if (ret) + SSL_ERR(ret, failed4, "mbedtls_ssl_conf_own_cert:[%d]\n", ret); + } + + ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); + if (ret) + SSL_ERR(ret, failed4, "mbedtls_ssl_setup:[-0x%x]\n", -ret); + + mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + ssl->ssl_pm = ssl_pm; + + return 0; + +failed4: + mbedtls_ssl_config_free(&ssl_pm->conf); +failed3: + mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); +failed2: + mbedtls_entropy_free(&ssl_pm->entropy); +failed1: + return -1; +} + +void ssl_pm_free(SSL *ssl) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + mbedtls_ssl_config_free(&ssl_pm->conf); + mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); + mbedtls_entropy_free(&ssl_pm->entropy); + mbedtls_ssl_free(&ssl_pm->ssl); + + mbedtls_net_free(&ssl_pm->fd); + mbedtls_net_free(&ssl_pm->cl_fd); +} + +int ssl_pm_handshake(SSL *ssl) +{ + int ret, mbed_ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ssl_speed_up_enter(); + while((mbed_ret = mbedtls_ssl_handshake(&ssl_pm->ssl)) != 0) { + if (mbed_ret != MBEDTLS_ERR_SSL_WANT_READ && mbed_ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + break; + } + } + ssl_speed_up_exit(); + + if (!mbed_ret) + ret = 1; + else { + ret = 0; + SSL_DEBUG(1, "mbedtls_ssl_handshake [-0x%x]\n", -mbed_ret); + } + + return ret; +} + +int ssl_pm_shutdown(SSL *ssl) +{ + int ret, mbed_ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + mbed_ret = mbedtls_ssl_close_notify(&ssl_pm->ssl); + if (!mbed_ret) + ret = 0; + else + ret = -1; + + return ret; +} + +int ssl_pm_clear(SSL *ssl) +{ + return ssl_pm_shutdown(ssl); +} + + +int ssl_pm_read(SSL *ssl, void *buffer, int len) +{ + int ret, mbed_ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + mbed_ret = mbedtls_ssl_read(&ssl_pm->ssl, buffer, len); + if (mbed_ret < 0) + ret = -1; + else if (mbed_ret == 0) + ret = 0; + else + ret = mbed_ret; + + return ret; +} + +int ssl_pm_send(SSL *ssl, const void *buffer, int len) +{ + int ret, mbed_ret; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + mbed_ret = mbedtls_ssl_write(&ssl_pm->ssl, buffer, len); + if (mbed_ret < 0) + ret = -1; + else if (mbed_ret == 0) + ret = 0; + else + ret = mbed_ret; + + return ret; +} + +int ssl_pm_pending(const SSL *ssl) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + return mbedtls_ssl_get_bytes_avail(&ssl_pm->ssl); +} + +void ssl_pm_set_fd(SSL *ssl, int fd, int mode) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ssl_pm->fd.fd = fd; +} + +int ssl_pm_get_fd(const SSL *ssl, int mode) +{ + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + return ssl_pm->fd.fd; +} + +OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl) +{ + OSSL_HANDSHAKE_STATE state; + + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + switch (ssl_pm->ssl.state) + { + case MBEDTLS_SSL_CLIENT_HELLO: + state = TLS_ST_CW_CLNT_HELLO; + break; + case MBEDTLS_SSL_SERVER_HELLO: + state = TLS_ST_SW_SRVR_HELLO; + break; + case MBEDTLS_SSL_SERVER_CERTIFICATE: + state = TLS_ST_SW_CERT; + break; + case MBEDTLS_SSL_SERVER_HELLO_DONE: + state = TLS_ST_SW_SRVR_DONE; + break; + case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE: + state = TLS_ST_CW_KEY_EXCH; + break; + case MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC: + state = TLS_ST_CW_CHANGE; + break; + case MBEDTLS_SSL_CLIENT_FINISHED: + state = TLS_ST_CW_FINISHED; + break; + case MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC: + state = TLS_ST_SW_CHANGE; + break; + case MBEDTLS_SSL_SERVER_FINISHED: + state = TLS_ST_SW_FINISHED; + break; + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + state = TLS_ST_CW_CERT; + break; + case MBEDTLS_SSL_SERVER_KEY_EXCHANGE: + state = TLS_ST_SR_KEY_EXCH; + break; + case MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET: + state = TLS_ST_SW_SESSION_TICKET; + break; + case MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT: + state = TLS_ST_SW_CERT_REQ; + break; + case MBEDTLS_SSL_HANDSHAKE_OVER: + state = TLS_ST_OK; + break; + default : + state = TLS_ST_BEFORE; + break; + } + + return state; +} + +void* x509_pm_new(void) +{ + return ssl_malloc(sizeof(struct x509_pm)); +} + +void x509_pm_free(void *pm) +{ + ssl_free(pm); +} + +int x509_pm_load_crt(void *pm, const unsigned char *buffer, int len) +{ + int ret; + unsigned char *load_buf; + struct x509_pm *x509_pm = (struct x509_pm *)pm; + + load_buf = ssl_malloc(len + 1); + if (!load_buf) + SSL_RET(failed1, ""); + + ssl_memcpy(load_buf, buffer, len); + load_buf[len] = '\0'; + + mbedtls_x509_crt_init(&x509_pm->x509_crt); + ret = mbedtls_x509_crt_parse(&x509_pm->x509_crt, load_buf, len); + ssl_free(load_buf); + + if (ret) + SSL_RET(failed1, ""); + + return 0; + +failed1: + return -1; +} + +void x509_pm_unload_crt(void *pm) +{ + struct x509_pm *x509_pm = (struct x509_pm *)pm; + + mbedtls_x509_crt_free(&x509_pm->x509_crt); +} + +void* pkey_pm_new(void) +{ + return ssl_malloc(sizeof(struct pkey_pm)); +} + +void pkey_pm_free(void *pm) +{ + ssl_free(pm); +} + +int pkey_pm_load_crt(void *pm, const unsigned char *buffer, int len) +{ + int ret; + unsigned char *load_buf; + struct pkey_pm *pkey_pm = (struct pkey_pm *)pm; + + load_buf = ssl_malloc(len + 1); + if (!load_buf) + SSL_RET(failed1, ""); + + ssl_memcpy(load_buf, buffer, len); + load_buf[len] = '\0'; + + mbedtls_pk_init(&pkey_pm->pkey); + ret = mbedtls_pk_parse_key(&pkey_pm->pkey, load_buf, len, NULL, 0); + ssl_free(load_buf); + + if (ret) + SSL_RET(failed1, ""); + + return 0; + +failed1: + return -1; +} + +void pkey_pm_unload_crt(void *pm) +{ + struct pkey_pm *pkey_pm = (struct pkey_pm *)pm; + + mbedtls_pk_free(&pkey_pm->pkey); +} + +void ssl_pm_set_bufflen(SSL *ssl, int len) +{ + max_content_len = len; +} From 5adc661d05ea0ab75e68674aae3ba6b2a7795824 Mon Sep 17 00:00:00 2001 From: dongheng Date: Wed, 21 Sep 2016 09:23:29 +0800 Subject: [PATCH 003/285] components/openssl: add more interface for application --- components/openssl/Makefile | 50 ------------- components/openssl/Makefile.projbuild | 4 ++ components/openssl/component.mk | 9 +++ components/openssl/include/internal/ssl_lib.h | 5 -- .../openssl/include/internal/ssl_pkey.h | 2 + components/openssl/include/platform/ssl_pm.h | 3 +- components/openssl/library/Makefile | 46 ------------ components/openssl/library/ssl_cert.c | 1 + components/openssl/library/ssl_lib.c | 72 ++++++++++++++++++- components/openssl/platform/Makefile | 46 ------------ components/openssl/platform/ssl_pm.c | 6 +- 11 files changed, 92 insertions(+), 152 deletions(-) delete mode 100644 components/openssl/Makefile create mode 100644 components/openssl/Makefile.projbuild create mode 100644 components/openssl/component.mk delete mode 100644 components/openssl/library/Makefile delete mode 100644 components/openssl/platform/Makefile diff --git a/components/openssl/Makefile b/components/openssl/Makefile deleted file mode 100644 index bdd8a0e932..0000000000 --- a/components/openssl/Makefile +++ /dev/null @@ -1,50 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -UP_EXTRACT_DIR = .. -GEN_LIBS = libopenssl.a -COMPONENTS_libopenssl = library/liblibrary.a platform/libplatform.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -I $(PDIR)include/platform -I $(PDIR)include/internal -INCLUDES += -I ./inlcude -INCLUDES += -I $(SDK_PATH)/include/openssl/internal -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/components/openssl/Makefile.projbuild b/components/openssl/Makefile.projbuild new file mode 100644 index 0000000000..51300efd11 --- /dev/null +++ b/components/openssl/Makefile.projbuild @@ -0,0 +1,4 @@ +# Anyone compiling mbedTLS code needs the name of the +# alternative config file +CFLAGS += -DMBEDTLS_CONFIG_FILE='"mbedtls/esp_config.h"' + diff --git a/components/openssl/component.mk b/components/openssl/component.mk new file mode 100644 index 0000000000..97de6975c9 --- /dev/null +++ b/components/openssl/component.mk @@ -0,0 +1,9 @@ +# +# Component Makefile +# + +COMPONENT_ADD_INCLUDEDIRS := include include/internal include/platform include/oepnssl + +COMPONENT_SRCDIRS := library platform + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/openssl/include/internal/ssl_lib.h b/components/openssl/include/internal/ssl_lib.h index d95d219556..0881fbbfdb 100644 --- a/components/openssl/include/internal/ssl_lib.h +++ b/components/openssl/include/internal/ssl_lib.h @@ -3,9 +3,4 @@ #include "ssl_types.h" -#define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING) -#define SSL_want_read(s) (SSL_want(s) == SSL_READING) -#define SSL_want_write(s) (SSL_want(s) == SSL_WRITING) -#define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_WRITING) - #endif diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h index cc870e18ed..c7170244a6 100644 --- a/components/openssl/include/internal/ssl_pkey.h +++ b/components/openssl/include/internal/ssl_pkey.h @@ -8,4 +8,6 @@ EVP_PKEY *d2i_PrivateKey(int type, const unsigned char **pp, long length); +void EVP_PKEY_free(EVP_PKEY *x); + #endif diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index c75ae95af5..a6731cff7f 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -2,7 +2,8 @@ #define _SSL_PM_H_ #include "ssl_types.h" -#include "esp_common.h" +#include "esp_types.h" +#include "esp_system.h" void* ssl_zalloc(size_t size); void *ssl_malloc(size_t size); diff --git a/components/openssl/library/Makefile b/components/openssl/library/Makefile deleted file mode 100644 index 10f4067c64..0000000000 --- a/components/openssl/library/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -GEN_LIBS = liblibrary.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c index 10f723bfcb..5332592460 100644 --- a/components/openssl/library/ssl_cert.c +++ b/components/openssl/library/ssl_cert.c @@ -1,4 +1,5 @@ #include "ssl_cert.h" +#include "ssl_dbg.h" #include "ssl_pm.h" CERT *ssl_cert_new(void) diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index ad78f3961a..e1e112cc7b 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -6,7 +6,7 @@ #define SSL_SEND_DATA_MAX_LENGTH 1460 -static int ossl_statem_in_error(const SSL *ssl) +int ossl_statem_in_error(const SSL *ssl) { if (ssl->statem.state == MSG_FLOW_ERROR) return 1; @@ -14,6 +14,74 @@ static int ossl_statem_in_error(const SSL *ssl) return 0; } +/* + * SSL_want - get the SSL specifical statement + * + * @param ssl - SSL point + * + * @return specifical statement + */ +int SSL_want(const SSL *ssl) +{ + return 0; +} + +/* + * SSL_want_nothing - check if SSL want nothing + * + * @param ssl - SSL point + * + * @return + * 1 : yes + * 0 : no + */ +int SSL_want_nothing(const SSL *ssl) +{ + return (SSL_want(ssl) == SSL_NOTHING); +} + +/* + * SSL_want_read - check if SSL want to read + * + * @param ssl - SSL point + * + * @return + * 1 : yes + * 0 : no + */ +int SSL_want_read(const SSL *ssl) +{ + return (SSL_want(ssl) == SSL_READING); +} + +/* + * SSL_want_read - check if SSL want to write + * + * @param ssl - SSL point + * + * @return + * 1 : yes + * 0 : no + */ +int SSL_want_write(const SSL *ssl) +{ + return (SSL_want(ssl) == SSL_WRITING); +} + +/* + * SSL_want_read - check if SSL want to lookup X509 certification + * + * @param ssl - SSL point + * + * @return + * 1 : yes + * 0 : no + */ +int SSL_want_x509_lookup(const SSL *ssl) +{ + return (SSL_want(ssl) == SSL_WRITING); +} + /* * SSL_get_error - get SSL error code * @@ -1153,7 +1221,7 @@ char *SSL_state_string(const SSL *ssl) SSL_ASSERT(ssl); - if (ossl_state_in_error(ssl)) + if (ossl_statem_in_error(ssl)) str = "SSLERR"; else { diff --git a/components/openssl/platform/Makefile b/components/openssl/platform/Makefile deleted file mode 100644 index 749b4787ca..0000000000 --- a/components/openssl/platform/Makefile +++ /dev/null @@ -1,46 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR - -GEN_LIBS = libplatform.a - -endif - - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -INCLUDES := $(INCLUDES) -I $(PDIR)include -INCLUDES += -I ./ -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 831420180c..9d207b3a0e 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -1,6 +1,8 @@ #include "ssl_pm.h" #include "ssl_dbg.h" +#include + /* mbedtls include */ #include "mbedtls/platform.h" #include "mbedtls/net.h" @@ -55,7 +57,7 @@ void* ssl_zalloc(size_t size) void *ssl_malloc(size_t size) { - return zalloc(size); + return ssl_zalloc(size); } void ssl_free(void *p) @@ -140,7 +142,7 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); if (ret) - SSL_ERR(ret, failed4, "mbedtls_ssl_conf_own_cert:[%d]\n", ret); + SSL_ERR(ret, failed3, "mbedtls_ssl_conf_own_cert:[%d]\n", ret); } ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); From b89168d0f1be251fce1b37bfe0684ae097da30f7 Mon Sep 17 00:00:00 2001 From: dongheng Date: Wed, 21 Sep 2016 17:51:12 +0800 Subject: [PATCH 004/285] components/openssl: add ssl_port.c & .h file --- .../openssl/include/platform/ssl_port.h | 29 ++++++++++ components/openssl/platform/ssl_port.c | 56 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 components/openssl/include/platform/ssl_port.h create mode 100644 components/openssl/platform/ssl_port.c diff --git a/components/openssl/include/platform/ssl_port.h b/components/openssl/include/platform/ssl_port.h new file mode 100644 index 0000000000..252e2566c3 --- /dev/null +++ b/components/openssl/include/platform/ssl_port.h @@ -0,0 +1,29 @@ +/* Copyright 2015-2016 Espressif Systems (Wuxi) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SSL_PORT_H_ +#define _SSL_PORT_H_ + +#include "esp_types.h" + +void* ssl_zalloc(size_t size); +void *ssl_malloc(size_t size); +void ssl_free(void *p); +void* ssl_memcpy(void *to, const void *from, size_t size); + +void ssl_speed_up_enter(void); +void ssl_speed_up_exit(void); + +#endif diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c new file mode 100644 index 0000000000..66aac5f6d9 --- /dev/null +++ b/components/openssl/platform/ssl_port.c @@ -0,0 +1,56 @@ +/* Copyright 2015-2016 Espressif Systems (Wuxi) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "ssl_port.h" +#include "malloc.h" + +/*********************************************************************************************/ +/********************************* SSL general interface *************************************/ + +void* ssl_zalloc(size_t size) +{ + void *p = malloc(size); + + if (p) + memset(p, 0, size); + + return p; +} + +void *ssl_malloc(size_t size) +{ + return ssl_zalloc(size); +} + +void ssl_free(void *p) +{ + free(p); +} + +void* ssl_memcpy(void *to, const void *from, size_t size) +{ + return memcpy(to, from, size); +} + +void ssl_speed_up_enter(void) +{ + +} + +void ssl_speed_up_exit(void) +{ + +} From 6bd3d62d7ceadb11c814459dfbe68160aa511409 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 10:28:08 +0800 Subject: [PATCH 005/285] components/openssl: add license header --- components/openssl/Makefile.projbuild | 1 - components/openssl/include/internal/ssl3.h | 14 ++ .../openssl/include/internal/ssl_cert.h | 20 ++- .../openssl/include/internal/ssl_code.h | 14 ++ components/openssl/include/internal/ssl_dbg.h | 20 ++- components/openssl/include/internal/ssl_lib.h | 14 ++ .../openssl/include/internal/ssl_methods.h | 53 +++++- .../openssl/include/internal/ssl_pkey.h | 18 +- components/openssl/include/internal/ssl_rsa.h | 14 ++ .../openssl/include/internal/ssl_types.h | 53 +++++- .../openssl/include/internal/ssl_x509.h | 14 ++ components/openssl/include/internal/tls1.h | 14 ++ components/openssl/include/openssl/ssl.h | 19 +- components/openssl/include/platform/ssl_pm.h | 40 +++-- .../openssl/include/platform/ssl_port.h | 27 ++- components/openssl/library/ssl_cert.c | 75 ++++++-- components/openssl/library/ssl_lib.c | 101 ++++++++--- components/openssl/library/ssl_methods.c | 42 ++++- components/openssl/library/ssl_pkey.c | 111 +++++++++--- components/openssl/library/ssl_rsa.c | 82 ++++++++- components/openssl/library/ssl_x509.c | 118 +++++++++---- components/openssl/platform/ssl_pm.c | 163 ++++++++++-------- components/openssl/platform/ssl_port.c | 27 ++- 23 files changed, 819 insertions(+), 235 deletions(-) diff --git a/components/openssl/Makefile.projbuild b/components/openssl/Makefile.projbuild index 51300efd11..b1d5641231 100644 --- a/components/openssl/Makefile.projbuild +++ b/components/openssl/Makefile.projbuild @@ -1,4 +1,3 @@ # Anyone compiling mbedTLS code needs the name of the # alternative config file -CFLAGS += -DMBEDTLS_CONFIG_FILE='"mbedtls/esp_config.h"' diff --git a/components/openssl/include/internal/ssl3.h b/components/openssl/include/internal/ssl3.h index d7c254563b..c90d546df0 100644 --- a/components/openssl/include/internal/ssl3.h +++ b/components/openssl/include/internal/ssl3.h @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL3_H_ #define _SSL3_H_ diff --git a/components/openssl/include/internal/ssl_cert.h b/components/openssl/include/internal/ssl_cert.h index e0b3ea75dc..109012a194 100644 --- a/components/openssl/include/internal/ssl_cert.h +++ b/components/openssl/include/internal/ssl_cert.h @@ -1,11 +1,23 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_CERT_H_ #define _SSL_CERT_H_ -#include "ssl_pkey.h" -#include "ssl_x509.h" - -CERT *ssl_cert_new(void); +#include "ssl_types.h" +CERT* ssl_cert_new(void); void ssl_cert_free(CERT *c); #endif diff --git a/components/openssl/include/internal/ssl_code.h b/components/openssl/include/internal/ssl_code.h index d45abff680..1510ce6ff4 100644 --- a/components/openssl/include/internal/ssl_code.h +++ b/components/openssl/include/internal/ssl_code.h @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_CODE_H_ #define _SSL_CODE_H_ diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 436d33132f..27a192b28f 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -1,13 +1,29 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_DEBUG_H_ #define _SSL_DEBUG_H_ -#define SSL_DEBUG_ENBALE 0 +#define SSL_DEBUG_ENBALE 1 #define SSL_DEBUG_LEVEL 0 #define SSL_ASSERT_ENABLE 1 #define SSL_DEBUG_LOCATION_ENABLE 1 #if SSL_DEBUG_ENBALE - #define SSL_PRINT os_printf + extern int ets_printf(const char *fmt, ...); + + #define SSL_PRINT ets_printf #else #define SSL_PRINT(...) #endif diff --git a/components/openssl/include/internal/ssl_lib.h b/components/openssl/include/internal/ssl_lib.h index 0881fbbfdb..6ea547a7c5 100644 --- a/components/openssl/include/internal/ssl_lib.h +++ b/components/openssl/include/internal/ssl_lib.h @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_LIB_H_ #define _SSL_LIB_H_ diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index e2806f177a..b72b17ad3d 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_METHODS_H_ #define _SSL_METHODS_H_ @@ -8,7 +22,7 @@ set_fd, get_fd, \ set_bufflen, \ get_state) \ - static const SSL_METHOD_FUNC func_name = { \ + static const SSL_METHOD_FUNC func_name LOCAL_ATRR = { \ new, \ free, \ handshake, \ @@ -25,7 +39,7 @@ #define IMPLEMENT_TLS_METHOD(ver, mode, fun, func_name) \ const SSL_METHOD* func_name(void) { \ - static const SSL_METHOD func_name##_data = { \ + static const SSL_METHOD func_name##_data LOCAL_ATRR = { \ ver, \ mode, \ &(fun), \ @@ -35,7 +49,7 @@ #define IMPLEMENT_SSL_METHOD(ver, mode, fun, func_name) \ const SSL_METHOD* func_name(void) { \ - static const SSL_METHOD func_name##_data = { \ + static const SSL_METHOD func_name##_data LOCAL_ATRR = { \ ver, \ mode, \ &(fun), \ @@ -43,4 +57,37 @@ return &func_name##_data; \ } +#define IMPLEMENT_X509_METHOD(func_name, \ + new, \ + free, \ + load, \ + unload) \ + const X509_METHOD* func_name(void) { \ + static const X509_METHOD func_name##_data LOCAL_ATRR = { \ + new, \ + free, \ + load, \ + unload, \ + }; \ + return &func_name##_data; \ + } + +#define IMPLEMENT_PKEY_METHOD(func_name, \ + new, \ + free, \ + load, \ + unload) \ + const PKEY_METHOD* func_name(void) { \ + static const PKEY_METHOD func_name##_data LOCAL_ATRR = { \ + new, \ + free, \ + load, \ + unload, \ + }; \ + return &func_name##_data; \ + } + +const X509_METHOD* X509_method(void); +const PKEY_METHOD* EVP_PKEY_method(void); + #endif diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h index c7170244a6..34be294efe 100644 --- a/components/openssl/include/internal/ssl_pkey.h +++ b/components/openssl/include/internal/ssl_pkey.h @@ -1,9 +1,25 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_PKEY_H_ #define _SSL_PKEY_H_ #include "ssl_types.h" -EVP_PKEY *d2i_PrivateKey(int type, +EVP_PKEY* EVP_PKEY_new(void); + +EVP_PKEY* d2i_PrivateKey(int type, EVP_PKEY **a, const unsigned char **pp, long length); diff --git a/components/openssl/include/internal/ssl_rsa.h b/components/openssl/include/internal/ssl_rsa.h index 7530bde734..d0ce40312c 100644 --- a/components/openssl/include/internal/ssl_rsa.h +++ b/components/openssl/include/internal/ssl_rsa.h @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_RSA_H_ #define _SSL_RSA_H_ diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 19eb6cb165..417350627c 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -1,8 +1,21 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_TYPES_H_ #define _SSL_TYPES_H_ #include "ssl_code.h" -#include typedef void SSL_CIPHER; @@ -47,15 +60,25 @@ typedef struct x509_st X509; struct evp_pkey_st; typedef struct evp_pkey_st EVP_PKEY; +struct x509_method_st; +typedef struct x509_method_st X509_METHOD; + +struct pkey_method_st; +typedef struct pkey_method_st PKEY_METHOD; + struct evp_pkey_st { void *pkey_pm; + + const PKEY_METHOD *method; }; struct x509_st { /* X509 certification platform private point */ void *x509_pm; + + const X509_METHOD *method; }; struct cert_st { @@ -111,6 +134,8 @@ struct ssl_ctx_st long session_timeout; int read_ahead; + + int read_buffer_len; }; struct ssl_st @@ -183,8 +208,34 @@ struct ssl_method_func_st { OSSL_HANDSHAKE_STATE (*ssl_get_state)(const SSL *ssl); }; +struct x509_method_st { + + int (*x509_new)(X509 *x); + + void (*x509_free)(X509 *x); + + int (*x509_load)(X509 *x, const unsigned char *buf, int len); + + void (*x509_unload)(X509 *x); +}; + +struct pkey_method_st { + + int (*pkey_new)(EVP_PKEY *pkey); + + void (*pkey_free)(EVP_PKEY *pkey); + + int (*pkey_load)(EVP_PKEY *pkey, const unsigned char *buf, int len); + + void (*pkey_unload)(EVP_PKEY *pkey); +}; + typedef int (*next_proto_cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg); +#define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__) +#define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__) +#define EVP_PKEY_METHOD_CALL(f, k, ...) k->method->pkey_##f(k, ##__VA_ARGS__) + #endif diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index 28a7baf513..a169352bac 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_X509_H_ #define _SSL_X509_H_ diff --git a/components/openssl/include/internal/tls1.h b/components/openssl/include/internal/tls1.h index 70de22bb5b..b2da639194 100644 --- a/components/openssl/include/internal/tls1.h +++ b/components/openssl/include/internal/tls1.h @@ -1,3 +1,17 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _TLS1_H_ #define _TLS1_H_ diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 89cb5bb9f7..0d4c9c2080 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -1,6 +1,21 @@ -#ifndef HEADER_SSL_H -#define HEADER_SSL_H +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_H_ +#define _SSL_H_ + +#include "ssl_port.h" #include "internal/ssl_types.h" /* diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index a6731cff7f..783ba5445e 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -1,14 +1,24 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #ifndef _SSL_PM_H_ #define _SSL_PM_H_ #include "ssl_types.h" -#include "esp_types.h" -#include "esp_system.h" +#include "ssl_port.h" -void* ssl_zalloc(size_t size); -void *ssl_malloc(size_t size); -void ssl_free(void *p); -void* ssl_memcpy(void *to, const void *from, size_t size); +#define LOCAL_ATRR int ssl_pm_new(SSL *ssl); void ssl_pm_free(SSL *ssl); @@ -28,15 +38,15 @@ OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl); void ssl_pm_set_bufflen(SSL *ssl, int len); -void* x509_pm_new(void); -void x509_pm_free(void *pm); -int x509_pm_load_crt(void *pm, const unsigned char *buffer, int len); -void x509_pm_unload_crt(void *pm); -void x509_pm_start_ca(void *ssl, void *pm); +int x509_pm_new(X509 *x); +void x509_pm_free(X509 *x); +int x509_pm_load(X509 *x, const unsigned char *buffer, int len); +void x509_pm_unload(X509 *x); +void x509_pm_start_ca(X509 *x); -void* pkey_pm_new(void); -void pkey_pm_free(void *pm); -int pkey_pm_load_crt(void *pm, const unsigned char *buffer, int len); -void pkey_pm_unload_crt(void *pm); +int pkey_pm_new(EVP_PKEY *pkey); +void pkey_pm_free(EVP_PKEY *pkey); +int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len); +void pkey_pm_unload(EVP_PKEY *pkey); #endif diff --git a/components/openssl/include/platform/ssl_port.h b/components/openssl/include/platform/ssl_port.h index 252e2566c3..48a7c7ca97 100644 --- a/components/openssl/include/platform/ssl_port.h +++ b/components/openssl/include/platform/ssl_port.h @@ -1,17 +1,16 @@ -/* Copyright 2015-2016 Espressif Systems (Wuxi) PTE LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef _SSL_PORT_H_ #define _SSL_PORT_H_ diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c index 5332592460..0bdba459d3 100644 --- a/components/openssl/library/ssl_cert.c +++ b/components/openssl/library/ssl_cert.c @@ -1,29 +1,68 @@ -#include "ssl_cert.h" -#include "ssl_dbg.h" -#include "ssl_pm.h" +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_cert.h" +#include "ssl_pkey.h" +#include "ssl_x509.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +/* + * ssl_cert_new - create a certification object include private key object + * + * @param none + * + * @return certification object point or NULL if failed + */ CERT *ssl_cert_new(void) { - return ssl_zalloc(sizeof(CERT)); + CERT *cert; + + cert = ssl_zalloc(sizeof(CERT)); + if (!cert) + SSL_RET(failed1, "ssl_zalloc\n"); + + cert->pkey = EVP_PKEY_new(); + if (!cert->pkey) + SSL_RET(failed2, "EVP_PKEY_new\n"); + + cert->x509 = sk_X509_NAME_new_null(); + if (!cert->x509) + SSL_RET(failed3, "sk_X509_NAME_new_null\n"); + + return cert; + +failed3: + EVP_PKEY_free(cert->pkey); +failed2: + ssl_free(cert); +failed1: + return NULL; } +/* + * ssl_cert_free - free a certification object + * + * @param c - certification object point + * + * @return none + */ void ssl_cert_free(CERT *c) { - if (c->x509) - X509_free(c->x509); + X509_free(c->x509); - if (c->pkey) - EVP_PKEY_free(c->pkey); + EVP_PKEY_free(c->pkey); ssl_free(c); } - -int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) -{ - SSL_ASSERT(ctx); - SSL_ASSERT(x); - - ctx->client_CA = x; - - return 1; -} diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index e1e112cc7b..331ed17bd5 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -1,11 +1,35 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ssl_lib.h" #include "ssl_pkey.h" +#include "ssl_x509.h" #include "ssl_cert.h" #include "ssl_dbg.h" -#include "ssl_pm.h" +#include "ssl_port.h" #define SSL_SEND_DATA_MAX_LENGTH 1460 +/* + * ossl_statem_in_error - Discover whether the current connection is in the error state + * + * @param ssl - SSL point + * + * @return + * 1 : Yes + * 0 : no + */ int ossl_statem_in_error(const SSL *ssl) { if (ssl->statem.state == MSG_FLOW_ERROR) @@ -23,7 +47,7 @@ int ossl_statem_in_error(const SSL *ssl) */ int SSL_want(const SSL *ssl) { - return 0; + return ssl->rwstate; } /* @@ -131,7 +155,7 @@ OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) SSL_ASSERT(ssl); - state = ssl->method->func->ssl_get_state(ssl); + state = SSL_METHOD_CALL(get_state, ssl); return state; } @@ -154,15 +178,15 @@ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) client_ca = sk_X509_NAME_new_null(); if (!client_ca) - SSL_ERR(-2, go_failed1, "ssl_ctx_new:ctx:[%d]\n", ret); + SSL_ERR(-2, go_failed1, "sk_X509_NAME_new_null\n"); cert = ssl_cert_new(); if (!cert) - SSL_ERR(-2, go_failed2, "ssl_ctx_new:ctx:[%d]\n", ret); + SSL_ERR(-2, go_failed2, "ssl_cert_new\n"); ctx = (SSL_CTX *)ssl_zalloc(sizeof(SSL_CTX)); if (!ctx) - SSL_ERR(-2, go_failed3, "ssl_ctx_new:ctx:[%d]\n", ret); + SSL_ERR(-2, go_failed3, "ssl_ctx_new:ctx\n"); ctx->method = method; ctx->cert = cert; @@ -215,6 +239,8 @@ int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth) ctx->method = meth; + ctx->version = meth->version; + return 1; } @@ -258,10 +284,12 @@ SSL *SSL_new(SSL_CTX *ctx) ssl->version = ctx->version; ssl->options = ctx->options; - ret = ssl->method->func->ssl_new(ssl); + ret = SSL_METHOD_CALL(new, ssl); if (ret) SSL_RET(failed2, "ssl_new\n"); + ssl->rwstate = SSL_NOTHING; + return ssl; failed2: @@ -281,7 +309,7 @@ void SSL_free(SSL *ssl) { SSL_ASSERT(ssl); - ssl->method->func->ssl_free(ssl); + SSL_METHOD_CALL(free, ssl); ssl_free(ssl); } @@ -302,7 +330,7 @@ int SSL_do_handshake(SSL *ssl) SSL_ASSERT(ssl); - ret = ssl->method->func->ssl_handshake(ssl); + ret = SSL_METHOD_CALL(handshake, ssl); return ret; } @@ -357,7 +385,7 @@ int SSL_shutdown(SSL *ssl) if (SSL_get_state(ssl) != TLS_ST_OK) return 0; - ret = ssl->method->func->ssl_shutdown(ssl); + ret = SSL_METHOD_CALL(shutdown, ssl); return ret; } @@ -381,9 +409,9 @@ int SSL_clear(SSL *ssl) if (1 != ret) SSL_ERR(0, go_failed1, "SSL_shutdown\n"); - ssl->method->func->ssl_free(ssl); + SSL_METHOD_CALL(free, ssl); - ret = ssl->method->func->ssl_new(ssl); + ret = SSL_METHOD_CALL(new, ssl); if (!ret) SSL_ERR(0, go_failed1, "ssl_new\n"); @@ -413,7 +441,11 @@ int SSL_read(SSL *ssl, void *buffer, int len) SSL_ASSERT(buffer); SSL_ASSERT(len); - ret = ssl->method->func->ssl_read(ssl, buffer, len); + ssl->rwstate = SSL_READING; + + ret = SSL_METHOD_CALL(read, ssl, buffer, len); + + ssl->rwstate = SSL_NOTHING; return ret; } @@ -440,6 +472,8 @@ int SSL_write(SSL *ssl, const void *buffer, int len) SSL_ASSERT(buffer); SSL_ASSERT(len); + ssl->rwstate = SSL_WRITING; + send_bytes = len; pbuf = (const unsigned char *)buffer; @@ -451,13 +485,15 @@ int SSL_write(SSL *ssl, const void *buffer, int len) else bytes = send_bytes; - ret = ssl->method->func->ssl_send(ssl, buffer, len); + ret = SSL_METHOD_CALL(send, ssl, buffer, len); if (ret > 0) { pbuf += ret; send_bytes -= ret; } } while (ret > 0 && send_bytes); + ssl->rwstate = SSL_NOTHING; + send_bytes = len - send_bytes; if (send_bytes >= 0) ret = send_bytes; @@ -518,11 +554,11 @@ int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method) if (1 != ret) SSL_ERR(0, go_failed1, "SSL_shutdown\n"); - ssl->method->func->ssl_free(ssl); + SSL_METHOD_CALL(free, ssl); ssl->method = method; - ret = ssl->method->func->ssl_new(ssl); + ret = SSL_METHOD_CALL(new, ssl); if (!ret) SSL_ERR(0, go_failed1, "ssl_new\n"); } else { @@ -579,7 +615,7 @@ int SSL_pending(const SSL *ssl) SSL_ASSERT(ssl); - ret = ssl->method->func->ssl_pending(ssl); + ret = SSL_METHOD_CALL(pending, ssl); return ret; } @@ -705,7 +741,7 @@ int SSL_get_fd(const SSL *ssl) SSL_ASSERT(ssl); - ret = ssl->method->func->ssl_get_fd(ssl, 0); + ret = SSL_METHOD_CALL(get_fd, ssl, 0); return ret; } @@ -725,7 +761,7 @@ int SSL_get_rfd(const SSL *ssl) SSL_ASSERT(ssl); - ret = ssl->method->func->ssl_get_fd(ssl, 0); + ret = SSL_METHOD_CALL(get_fd, ssl, 0); return ret; } @@ -745,7 +781,7 @@ int SSL_get_wfd(const SSL *ssl) SSL_ASSERT(ssl); - ret = ssl->method->func->ssl_get_fd(ssl, 0); + ret = SSL_METHOD_CALL(get_fd, ssl, 0); return ret; } @@ -767,7 +803,7 @@ int SSL_set_fd(SSL *ssl, int fd) SSL_ASSERT(ssl); SSL_ASSERT(fd >= 0); - ssl->method->func->ssl_set_fd(ssl, fd, 0); + SSL_METHOD_CALL(set_fd, ssl, fd, 0); return 1; } @@ -789,7 +825,7 @@ int SSL_set_rfd(SSL *ssl, int fd) SSL_ASSERT(ssl); SSL_ASSERT(fd >= 0); - ssl->method->func->ssl_set_fd(ssl, fd, 0); + SSL_METHOD_CALL(set_fd, ssl, fd, 0); return 1; } @@ -811,7 +847,7 @@ int SSL_set_wfd(SSL *ssl, int fd) SSL_ASSERT(ssl); SSL_ASSERT(fd >= 0); - ssl->method->func->ssl_set_fd(ssl, fd, 0); + SSL_METHOD_CALL(set_fd, ssl, fd, 0); return 1; } @@ -1451,7 +1487,7 @@ void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len) SSL_ASSERT(ctx); SSL_ASSERT(len); - ctx->method->func->ssl_set_bufflen(NULL, len); + ctx->read_buffer_len = len; } /* @@ -1467,7 +1503,7 @@ void SSL_set_default_read_buffer_len(SSL *ssl, size_t len) SSL_ASSERT(ssl); SSL_ASSERT(len); - ssl->method->func->ssl_set_bufflen(ssl, len); + SSL_METHOD_CALL(set_bufflen, ssl, len); } /* @@ -1688,3 +1724,18 @@ long SSL_set_timeout(SSL *ssl, long t) return t; } + +/* + * SSL_set_verify - set the SSL verifying of the SSL context + * + * @param ctx - SSL point + * @param mode - verifying mode + * @param verify_callback - verifying callback function + * + * @return none + */ +void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) +{ + SSL_ASSERT(ssl); + SSL_ASSERT(verify_callback); +} diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index 502262f7e9..0c5c6e7fa4 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -1,7 +1,24 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ssl_lib.h" #include "ssl_methods.h" #include "ssl_pm.h" +/* + * TLS method function collection + */ IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, ssl_pm_new, ssl_pm_free, ssl_pm_handshake, ssl_pm_shutdown, ssl_pm_clear, @@ -10,6 +27,9 @@ IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, ssl_pm_set_bufflen, ssl_pm_get_state); +/* + * TLS or SSL client method collection + */ IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 0, TLS_method_func, TLS_client_method); IMPLEMENT_TLS_METHOD(TLS1_2_VERSION, 0, TLS_method_func, TLSv1_2_client_method); @@ -20,7 +40,9 @@ IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_client_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, 0, TLS_method_func, SSLv3_client_method); - +/* + * TLS or SSL server method collection + */ IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 1, TLS_method_func, TLS_server_method); IMPLEMENT_TLS_METHOD(TLS1_1_VERSION, 1, TLS_method_func, TLSv1_1_server_method); @@ -31,7 +53,9 @@ IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_server_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, 1, TLS_method_func, SSLv3_server_method); - +/* + * TLS or SSL method collection + */ IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, -1, TLS_method_func, TLS_method); IMPLEMENT_SSL_METHOD(TLS1_2_VERSION, -1, TLS_method_func, TLSv1_2_method); @@ -41,3 +65,17 @@ IMPLEMENT_SSL_METHOD(TLS1_1_VERSION, -1, TLS_method_func, TLSv1_1_method); IMPLEMENT_SSL_METHOD(TLS1_VERSION, -1, TLS_method_func, TLSv1_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); + +/* + * X509 certification method collection + */ +IMPLEMENT_X509_METHOD(X509_method, + x509_pm_new, x509_pm_free, + x509_pm_load, x509_pm_unload); + +/* + * private key method collection + */ +IMPLEMENT_PKEY_METHOD(EVP_PKEY_method, + pkey_pm_new, pkey_pm_free, + pkey_pm_load, pkey_pm_unload); diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 2a170716c0..785ebf41db 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -1,50 +1,109 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ssl_lib.h" #include "ssl_pkey.h" +#include "ssl_methods.h" #include "ssl_dbg.h" -#include "ssl_pm.h" +#include "ssl_port.h" -EVP_PKEY *d2i_PrivateKey(int type, - EVP_PKEY **a, - const unsigned char **pp, - long length) +/* + * EVP_PKEY_new - create a private key object + * + * @param none + * + * @return private key object point or NULL if failed + */ +EVP_PKEY* EVP_PKEY_new(void) { - EVP_PKEY *pkey; - void *pkey_pm; int ret; - - SSL_ASSERT(pp); - SSL_ASSERT(*pp); - SSL_ASSERT(length); + EVP_PKEY *pkey; pkey = ssl_malloc(sizeof(EVP_PKEY)); if (!pkey) SSL_RET(failed1, "ssl_malloc\n"); - pkey_pm = pkey_pm_new(); - if (!pkey_pm) - SSL_RET(failed2, "pkey_pm_new\n"); + pkey->method = EVP_PKEY_method(); - ret = pkey_pm_load_crt(pkey_pm, *pp, length); + ret = EVP_PKEY_METHOD_CALL(new, pkey); if (ret) - SSL_RET(failed3, "pkey_pm_load_crt\n"); - - pkey->pkey_pm = pkey_pm; - if (a) - *a = pkey; + SSL_RET(failed2, "pkey_new\n"); return pkey; -failed3: - pkey_pm_free(pkey_pm); failed2: ssl_free(pkey); failed1: return NULL; } -void EVP_PKEY_free(EVP_PKEY *x) +/* + * EVP_PKEY_free - free a private key object + * + * @param pkey - private key object point + * + * @return none + */ +void EVP_PKEY_free(EVP_PKEY *pkey) { - pkey_pm_unload_crt(x->pkey_pm); - pkey_pm_free(x->pkey_pm); - ssl_free(x); + EVP_PKEY_METHOD_CALL(free, pkey); + + ssl_free(pkey); +} + +/* + * d2i_PrivateKey - load a character key context into system context. If '*a' is pointed to the + * private key, then load key into it. Or create a new private key object + * + * @param type - private key type + * @param a - a point pointed to a private key point + * @param pp - a point pointed to the key context memory point + * @param length - key bytes + * + * @return private key object point or NULL if failed + */ +EVP_PKEY *d2i_PrivateKey(int type, + EVP_PKEY **a, + const unsigned char **pp, + long length) +{ + int ret; + EVP_PKEY *pkey; + + SSL_ASSERT(pp); + SSL_ASSERT(*pp); + SSL_ASSERT(length); + + if (a && *a) { + pkey = *a; + } else { + pkey = EVP_PKEY_new();; + if (!pkey) + SSL_RET(failed1, "ssl_malloc\n"); + } + + ret = EVP_PKEY_METHOD_CALL(load, pkey, *pp, length); + if (ret) + SSL_RET(failed2, "pkey_pm_load_crt\n"); + + if (a) + *a = pkey; + + return pkey; + +failed2: + EVP_PKEY_free(pkey); +failed1: + return NULL; } diff --git a/components/openssl/library/ssl_rsa.c b/components/openssl/library/ssl_rsa.c index 9088f67f57..75a2d3baa7 100644 --- a/components/openssl/library/ssl_rsa.c +++ b/components/openssl/library/ssl_rsa.c @@ -1,10 +1,33 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ssl_lib.h" #include "ssl_rsa.h" #include "ssl_pkey.h" #include "ssl_x509.h" #include "ssl_dbg.h" -#include "ssl_pm.h" +/* + * SSL_CTX_use_certificate - set the SSL context certification + * + * @param ctx - SSL context point + * @param x - X509 certification point + * + * @return + * 1 : OK + * 0 : failed + */ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) { SSL_ASSERT(ctx); @@ -15,13 +38,24 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) return 1; } +/* + * SSL_CTX_use_certificate_ASN1 - load certification into the SSL context + * + * @param ctx - SSL context point + * @param len - certification context bytes + * @param d - certification context point + * + * @return + * 1 : OK + * 0 : failed + */ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d) { int ret; X509 *cert; - cert = d2i_X509(NULL, d, len); + cert = d2i_X509(&ctx->cert->x509, d, len); if (!cert) SSL_RET(failed1, "d2i_X509\n"); @@ -37,6 +71,16 @@ failed1: return 0; } +/* + * SSL_CTX_use_certificate - set the SSL context private key + * + * @param ctx - SSL context point + * @param x - private key point + * + * @return + * 1 : OK + * 0 : failed + */ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) { SSL_ASSERT(ctx); @@ -47,13 +91,25 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) return 1; } +/* + * SSL_CTX_use_PrivateKey_ASN1 - load private key into the SSL context + * + * @param type - private key type + * @param ctx - SSL context point + * @param d - private key context point + * @param len - private key context bytes + * + * @return + * 1 : OK + * 0 : failed + */ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const unsigned char *d, long len) { int ret; EVP_PKEY *pkey; - pkey = d2i_PrivateKey(0, NULL, &d, len); + pkey = d2i_PrivateKey(0, &ctx->cert->pkey, &d, len); if (!pkey) SSL_RET(failed1, "d2i_PrivateKey\n"); @@ -68,3 +124,23 @@ failed2: failed1: return 0; } + +/* + * SSL_CTX_add_client_CA - set SSL context client CA certification + * + * @param ctx - SSL context point + * @param x - client CA certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(x); + + ctx->client_CA = x; + + return 1; +} diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 23aa00681e..fd2643e6b7 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -1,54 +1,100 @@ -#include "ssl_x509.h" -#include "ssl_dbg.h" -#include "ssl_pm.h" +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_x509.h" +#include "ssl_methods.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +/* + * sk_X509_NAME_new_null - create a X509 certification object + * + * @param none + * + * @return X509 certification object point or NULL if failed + */ X509* sk_X509_NAME_new_null(void) { - return ssl_malloc(sizeof(X509)); -} - -X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) -{ - X509 *x509_crt; - void *x509_pm; int ret; + X509 *x; - SSL_ASSERT(cert); - SSL_ASSERT(buffer); - SSL_ASSERT(len); + x = ssl_malloc(sizeof(X509)); + if (!x) + SSL_RET(failed1, "ssl_malloc\n"); - x509_crt = sk_X509_NAME_new_null(); - if (!x509_crt) - SSL_RET(failed1, ""); + x->method = X509_method(); - x509_pm = x509_pm_new(); - if (!x509_pm) - SSL_RET(failed2, ""); - - ret = x509_pm_load_crt(x509_pm, buffer, len); + ret = x->method->x509_new(x); if (ret) - SSL_RET(failed3, ""); + SSL_RET(failed2, "x509_new\n"); - x509_crt->x509_pm = x509_pm; - if (cert) - *cert = x509_crt; + return x; - return x509_crt; - -failed3: - x509_pm_free(x509_pm); failed2: - ssl_free(x509_crt); + ssl_free(x); failed1: return NULL; } -void X509_free(X509 *cert) +/* + * X509_free - free a X509 certification object + * + * @param x - X509 certification object point + * + * @return none + */ +void X509_free(X509 *x) { - if (cert->x509_pm) { - x509_pm_unload_crt(cert->x509_pm); - x509_pm_free(cert->x509_pm); - } - ssl_free(cert); + X509_METHOD_CALL(free, x); + + ssl_free(x); }; +/* + * d2i_X509 - load a character certification context into system context. If '*cert' is pointed to the + * certification, then load certification into it. Or create a new X509 certification object + * + * @param cert - a point pointed to X509 certification + * @param buffer - a point pointed to the certification context memory point + * @param length - certification bytes + * + * @return X509 certification object point or NULL if failed + */ +X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) +{ + int ret; + X509 *x; + + SSL_ASSERT(buffer); + SSL_ASSERT(len); + + if (cert && *cert) { + x = *cert; + } else { + x = sk_X509_NAME_new_null(); + if (!x) + SSL_RET(failed1, "sk_X509_NAME_new_null\n"); + } + + ret = X509_METHOD_CALL(load, x, buffer, len); + if (ret) + SSL_RET(failed2, "x509_load\n"); + + return x; + +failed2: + X509_free(x); +failed1: + return NULL; +} diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 9d207b3a0e..b8a046aa1e 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -1,4 +1,19 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ssl_pm.h" +#include "ssl_port.h" #include "ssl_dbg.h" #include @@ -30,11 +45,15 @@ struct ssl_pm struct x509_pm { + int load; + mbedtls_x509_crt x509_crt; }; struct pkey_pm { + int load; + mbedtls_pk_context pkey; }; @@ -42,44 +61,6 @@ struct pkey_pm unsigned int max_content_len; -/*********************************************************************************************/ -/********************************* SSL general interface *************************************/ - -void* ssl_zalloc(size_t size) -{ - void *p = malloc(size); - - if (p) - memset(p, 0, size); - - return p; -} - -void *ssl_malloc(size_t size) -{ - return ssl_zalloc(size); -} - -void ssl_free(void *p) -{ - free(p); -} - -void* ssl_memcpy(void *to, const void *from, size_t size) -{ - return memcpy(to, from, size); -} - -void ssl_speed_up_enter(void) -{ - -} - -void ssl_speed_up_exit(void) -{ - -} - /*********************************************************************************************/ /************************************ SSL arch interface *************************************/ @@ -90,13 +71,18 @@ int ssl_pm_new(SSL *ssl) char *pers; int endpoint; + int mode; + int version; SSL_CTX *ctx = ssl->ctx; const SSL_METHOD *method = ssl->method; - ssl_pm = malloc(sizeof(struct ssl_pm)); + struct x509_pm *x509_pm; + struct pkey_pm *pkey_pm; + + ssl_pm = ssl_malloc(sizeof(struct ssl_pm)); if (!ssl_pm) - return -1; + SSL_ERR(ret, failed1, "ssl_malloc\n"); if (method->endpoint) { pers = "server"; @@ -124,21 +110,34 @@ int ssl_pm_new(SSL *ssl) if (ret) SSL_ERR(ret, failed2, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); + if (TLS1_2_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_3; + else if (TLS1_1_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_2; + else if (TLS1_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_1; + else + version = MBEDTLS_SSL_MINOR_VERSION_0; + + mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); + mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL); - if (ctx->client_CA->x509_pm) { - struct x509_pm *x509_pm = (struct x509_pm *)ctx->client_CA->x509_pm; + x509_pm = (struct x509_pm *)ctx->client_CA->x509_pm; + if (x509_pm->load) { + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &x509_pm->x509_crt, NULL); - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &x509_pm->x509_crt, NULL); - mbedtls_ssl_conf_authmode(&ssl_pm->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + mode = MBEDTLS_SSL_VERIFY_REQUIRED; } else { - mbedtls_ssl_conf_authmode(&ssl_pm->conf, MBEDTLS_SSL_VERIFY_NONE); + mode = MBEDTLS_SSL_VERIFY_NONE; } - if (ctx->cert->x509 && - ctx->cert->pkey) { - struct x509_pm *x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm; - struct pkey_pm *pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm; + mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); + + pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm; + if (pkey_pm->load) { + x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm; ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); if (ret) @@ -332,21 +331,24 @@ OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl) return state; } -void* x509_pm_new(void) +int x509_pm_new(X509 *x) { - return ssl_malloc(sizeof(struct x509_pm)); + struct x509_pm *x509_pm; + + x509_pm = ssl_malloc(sizeof(struct x509_pm)); + if (!x509_pm) + return -1; + + x->x509_pm = x509_pm; + + return 0; } -void x509_pm_free(void *pm) -{ - ssl_free(pm); -} - -int x509_pm_load_crt(void *pm, const unsigned char *buffer, int len) +int x509_pm_load(X509 *x, const unsigned char *buffer, int len) { int ret; unsigned char *load_buf; - struct x509_pm *x509_pm = (struct x509_pm *)pm; + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; load_buf = ssl_malloc(len + 1); if (!load_buf) @@ -362,34 +364,48 @@ int x509_pm_load_crt(void *pm, const unsigned char *buffer, int len) if (ret) SSL_RET(failed1, ""); + x509_pm->load = 1; + return 0; failed1: return -1; } -void x509_pm_unload_crt(void *pm) +void x509_pm_unload(X509 *x) { - struct x509_pm *x509_pm = (struct x509_pm *)pm; + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; mbedtls_x509_crt_free(&x509_pm->x509_crt); + + x509_pm->load = 0; } -void* pkey_pm_new(void) +void x509_pm_free(X509 *x) { - return ssl_malloc(sizeof(struct pkey_pm)); + x509_pm_unload(x); + + ssl_free(x->x509_pm); } -void pkey_pm_free(void *pm) +int pkey_pm_new(EVP_PKEY *pkey) { - ssl_free(pm); + struct pkey_pm *pkey_pm; + + pkey_pm = ssl_malloc(sizeof(struct pkey_pm)); + if (!pkey_pm) + return -1; + + pkey->pkey_pm = pkey_pm; + + return 0; } -int pkey_pm_load_crt(void *pm, const unsigned char *buffer, int len) +int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len) { int ret; unsigned char *load_buf; - struct pkey_pm *pkey_pm = (struct pkey_pm *)pm; + struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; load_buf = ssl_malloc(len + 1); if (!load_buf) @@ -405,17 +421,28 @@ int pkey_pm_load_crt(void *pm, const unsigned char *buffer, int len) if (ret) SSL_RET(failed1, ""); + pkey_pm->load = 1; + return 0; failed1: return -1; } -void pkey_pm_unload_crt(void *pm) +void pkey_pm_unload(EVP_PKEY *pkey) { - struct pkey_pm *pkey_pm = (struct pkey_pm *)pm; + struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; mbedtls_pk_free(&pkey_pm->pkey); + + pkey_pm->load = 0; +} + +void pkey_pm_free(EVP_PKEY *pkey) +{ + pkey_pm_unload(pkey); + + ssl_free(pkey->pkey_pm); } void ssl_pm_set_bufflen(SSL *ssl, int len) diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c index 66aac5f6d9..4045e29116 100644 --- a/components/openssl/platform/ssl_port.c +++ b/components/openssl/platform/ssl_port.c @@ -1,17 +1,16 @@ -/* Copyright 2015-2016 Espressif Systems (Wuxi) PTE LTD - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include #include "ssl_port.h" From 845ca8b34ffce008f8992b5799964da14209739f Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 11:43:59 +0800 Subject: [PATCH 006/285] components/openssl: delete ssl_rsa.c & .h file --- components/openssl/include/internal/ssl_rsa.h | 28 ---- .../openssl/include/platform/ssl_port.h | 2 + components/openssl/library/ssl_pkey.c | 55 +++++++ components/openssl/library/ssl_rsa.c | 146 ------------------ components/openssl/library/ssl_x509.c | 74 +++++++++ components/openssl/platform/ssl_pm.c | 23 ++- components/openssl/platform/ssl_port.c | 7 +- 7 files changed, 146 insertions(+), 189 deletions(-) delete mode 100644 components/openssl/include/internal/ssl_rsa.h delete mode 100644 components/openssl/library/ssl_rsa.c diff --git a/components/openssl/include/internal/ssl_rsa.h b/components/openssl/include/internal/ssl_rsa.h deleted file mode 100644 index d0ce40312c..0000000000 --- a/components/openssl/include/internal/ssl_rsa.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef _SSL_RSA_H_ -#define _SSL_RSA_H_ - -#include "ssl_lib.h" - -int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); -int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, - const unsigned char *d); - -int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); -int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, - const unsigned char *d, long len); - -#endif diff --git a/components/openssl/include/platform/ssl_port.h b/components/openssl/include/platform/ssl_port.h index 48a7c7ca97..23ef5a8757 100644 --- a/components/openssl/include/platform/ssl_port.h +++ b/components/openssl/include/platform/ssl_port.h @@ -20,7 +20,9 @@ void* ssl_zalloc(size_t size); void *ssl_malloc(size_t size); void ssl_free(void *p); + void* ssl_memcpy(void *to, const void *from, size_t size); +size_t ssl_strlen(const char *src); void ssl_speed_up_enter(void); void ssl_speed_up_exit(void); diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 785ebf41db..0c8d9de8fa 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -107,3 +107,58 @@ failed2: failed1: return NULL; } + +/* + * SSL_CTX_use_certificate - set the SSL context private key + * + * @param ctx - SSL context point + * @param x - private key point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(pkey); + + ctx->cert->pkey = pkey; + + return 1; +} + +/* + * SSL_CTX_use_PrivateKey_ASN1 - load private key into the SSL context + * + * @param type - private key type + * @param ctx - SSL context point + * @param d - private key context point + * @param len - private key context bytes + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, + const unsigned char *d, long len) +{ + int ret; + EVP_PKEY *pkey; + + pkey = d2i_PrivateKey(0, &ctx->cert->pkey, &d, len); + if (!pkey) + SSL_RET(failed1, "d2i_PrivateKey\n"); + + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + if (!ret) + SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); + + return 1; + +failed2: + EVP_PKEY_free(pkey); +failed1: + return 0; +} + diff --git a/components/openssl/library/ssl_rsa.c b/components/openssl/library/ssl_rsa.c deleted file mode 100644 index 75a2d3baa7..0000000000 --- a/components/openssl/library/ssl_rsa.c +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ssl_lib.h" -#include "ssl_rsa.h" -#include "ssl_pkey.h" -#include "ssl_x509.h" -#include "ssl_dbg.h" - -/* - * SSL_CTX_use_certificate - set the SSL context certification - * - * @param ctx - SSL context point - * @param x - X509 certification point - * - * @return - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) -{ - SSL_ASSERT(ctx); - SSL_ASSERT(x); - - ctx->cert->x509 = x; - - return 1; -} - -/* - * SSL_CTX_use_certificate_ASN1 - load certification into the SSL context - * - * @param ctx - SSL context point - * @param len - certification context bytes - * @param d - certification context point - * - * @return - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, - const unsigned char *d) -{ - int ret; - X509 *cert; - - cert = d2i_X509(&ctx->cert->x509, d, len); - if (!cert) - SSL_RET(failed1, "d2i_X509\n"); - - ret = SSL_CTX_use_certificate(ctx, cert); - if (!ret) - SSL_RET(failed2, "SSL_CTX_use_certificate\n"); - - return 1; - -failed2: - X509_free(cert); -failed1: - return 0; -} - -/* - * SSL_CTX_use_certificate - set the SSL context private key - * - * @param ctx - SSL context point - * @param x - private key point - * - * @return - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) -{ - SSL_ASSERT(ctx); - SSL_ASSERT(pkey); - - ctx->cert->pkey = pkey; - - return 1; -} - -/* - * SSL_CTX_use_PrivateKey_ASN1 - load private key into the SSL context - * - * @param type - private key type - * @param ctx - SSL context point - * @param d - private key context point - * @param len - private key context bytes - * - * @return - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, - const unsigned char *d, long len) -{ - int ret; - EVP_PKEY *pkey; - - pkey = d2i_PrivateKey(0, &ctx->cert->pkey, &d, len); - if (!pkey) - SSL_RET(failed1, "d2i_PrivateKey\n"); - - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - if (!ret) - SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); - - return 1; - -failed2: - EVP_PKEY_free(pkey); -failed1: - return 0; -} - -/* - * SSL_CTX_add_client_CA - set SSL context client CA certification - * - * @param ctx - SSL context point - * @param x - client CA certification point - * - * @return - * 1 : OK - * 0 : failed - */ -int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) -{ - SSL_ASSERT(ctx); - SSL_ASSERT(x); - - ctx->client_CA = x; - - return 1; -} diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index fd2643e6b7..219f283991 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -98,3 +98,77 @@ failed2: failed1: return NULL; } + +/* + * SSL_CTX_add_client_CA - set SSL context client CA certification + * + * @param ctx - SSL context point + * @param x - client CA certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(x); + + ctx->client_CA = x; + + return 1; +} + +/* + * SSL_CTX_use_certificate - set the SSL context certification + * + * @param ctx - SSL context point + * @param x - X509 certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(x); + + ctx->cert->x509 = x; + + return 1; +} + +/* + * SSL_CTX_use_certificate_ASN1 - load certification into the SSL context + * + * @param ctx - SSL context point + * @param len - certification context bytes + * @param d - certification context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, + const unsigned char *d) +{ + int ret; + X509 *cert; + + cert = d2i_X509(&ctx->cert->x509, d, len); + if (!cert) + SSL_RET(failed1, "d2i_X509\n"); + + ret = SSL_CTX_use_certificate(ctx, cert); + if (!ret) + SSL_RET(failed2, "SSL_CTX_use_certificate\n"); + + return 1; + +failed2: + X509_free(cert); +failed1: + return 0; +} + diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index b8a046aa1e..948c1bc4ee 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -16,8 +16,6 @@ #include "ssl_port.h" #include "ssl_dbg.h" -#include - /* mbedtls include */ #include "mbedtls/platform.h" #include "mbedtls/net.h" @@ -69,7 +67,9 @@ int ssl_pm_new(SSL *ssl) struct ssl_pm *ssl_pm; int ret; - char *pers; + const unsigned char pers[] = "OpenSSL PM"; + size_t pers_len = sizeof(pers); + int endpoint; int mode; int version; @@ -84,16 +84,6 @@ int ssl_pm_new(SSL *ssl) if (!ssl_pm) SSL_ERR(ret, failed1, "ssl_malloc\n"); - if (method->endpoint) { - pers = "server"; - endpoint = MBEDTLS_SSL_IS_SERVER; - } else { - pers = "client"; - endpoint = MBEDTLS_SSL_IS_CLIENT; - } - - //max_content_len = 4096; - mbedtls_net_init(&ssl_pm->fd); mbedtls_net_init(&ssl_pm->cl_fd); @@ -102,10 +92,15 @@ int ssl_pm_new(SSL *ssl) mbedtls_entropy_init(&ssl_pm->entropy); mbedtls_ssl_init(&ssl_pm->ssl); - ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, (const unsigned char *)pers, strlen(pers)); + ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, pers, pers_len); if (ret) SSL_ERR(ret, failed1, "mbedtls_ctr_drbg_seed:[-0x%x]\n", -ret); + if (method->endpoint) { + endpoint = MBEDTLS_SSL_IS_SERVER; + } else { + endpoint = MBEDTLS_SSL_IS_CLIENT; + } ret = mbedtls_ssl_config_defaults(&ssl_pm->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret) SSL_ERR(ret, failed2, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c index 4045e29116..3e6ada5cc9 100644 --- a/components/openssl/platform/ssl_port.c +++ b/components/openssl/platform/ssl_port.c @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include "ssl_port.h" +#include "string.h" #include "malloc.h" /*********************************************************************************************/ @@ -44,6 +44,11 @@ void* ssl_memcpy(void *to, const void *from, size_t size) return memcpy(to, from, size); } +size_t ssl_strlen(const char *src) +{ + return strlen(src); +} + void ssl_speed_up_enter(void) { From c504fe4856ed259b995dc1eb3cfa72b7dade21aa Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 12:57:39 +0800 Subject: [PATCH 007/285] components/openssl: 1. add stack_st structure and its advanced defination including its derived child defination 2. add SSL_add_client_CA & SSL_get_certificate --- .../openssl/include/internal/ssl_types.h | 19 +++++++++++-- .../openssl/include/internal/ssl_x509.h | 2 ++ components/openssl/library/ssl_x509.c | 27 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 417350627c..7f8503e2ab 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -20,7 +20,6 @@ typedef void SSL_CIPHER; typedef void X509_STORE_CTX; -typedef void X509_NAME; typedef void X509_STORE; typedef void RSA; @@ -28,7 +27,19 @@ typedef void RSA; typedef void STACK; typedef void BIO; -#define STACK_OF(x) x +#define STACK_OF(type) struct stack_st_##type + +#define SKM_DEFINE_STACK_OF(t1, t2, t3) \ + STACK_OF(t1); \ + static ossl_inline STACK_OF(t1) *sk_##t1##_new_null(void) \ + { \ + return (STACK_OF(t1) *)OPENSSL_sk_new_null(); \ + } \ + +#define DEFINE_STACK_OF(t) SKM_DEFINE_STACK_OF(t, t, t) + +struct stack_st; +typedef struct stack_st OPENSSL_STACK; struct ssl_method_st; typedef struct ssl_method_st SSL_METHOD; @@ -66,6 +77,10 @@ typedef struct x509_method_st X509_METHOD; struct pkey_method_st; typedef struct pkey_method_st PKEY_METHOD; +struct stack_st { + char *data; +}; + struct evp_pkey_st { void *pkey_pm; diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index a169352bac..0583cd94e4 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -17,6 +17,8 @@ #include "ssl_types.h" +DEFINE_STACK_OF(X509_NAME) + X509* sk_X509_NAME_new_null(void); X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 219f283991..e322b6ad3d 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -119,6 +119,21 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) return 1; } +/* + * SSL_add_client_CA - add CA client certification into the SSL + * + * @param ssl - SSL point + * @param x - CA certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_add_client_CA(SSL *ssl, X509 *x) +{ + +} + /* * SSL_CTX_use_certificate - set the SSL context certification * @@ -139,6 +154,18 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) return 1; } +/* + * SSL_get_certificate - get the SSL certification point + * + * @param ssl - SSL point + * + * @return SSL certification point + */ +X509 *SSL_get_certificate(const SSL *ssl) +{ + return ssl->cert->x509; +} + /* * SSL_CTX_use_certificate_ASN1 - load certification into the SSL context * From 2cc32db52dd4d4de8a0521153f5dbd598106ec19 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 14:42:49 +0800 Subject: [PATCH 008/285] component/openssl: add openssl stack function and clear unused variate 1. add openssl 'new' and 'free' function 2. add clear unused variate to void warning to appear when compile 3. add internal function 'X509_new' to take the place of 'sk_X509_NAME_new_null' function whitch is openssl stack function --- components/openssl/include/internal/ssl_dbg.h | 6 ++--- .../openssl/include/internal/ssl_types.h | 21 ++++++++++++---- .../openssl/include/internal/ssl_x509.h | 10 +++++++- components/openssl/include/openssl/ssl.h | 4 ++-- components/openssl/library/ssl_cert.c | 2 +- components/openssl/library/ssl_lib.c | 20 +++++----------- components/openssl/library/ssl_pkey.c | 5 +++- components/openssl/library/ssl_x509.c | 24 ++++++++++++++++--- 8 files changed, 62 insertions(+), 30 deletions(-) diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 27a192b28f..745de536ff 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -15,10 +15,10 @@ #ifndef _SSL_DEBUG_H_ #define _SSL_DEBUG_H_ -#define SSL_DEBUG_ENBALE 1 +#define SSL_DEBUG_ENBALE 0 #define SSL_DEBUG_LEVEL 0 -#define SSL_ASSERT_ENABLE 1 -#define SSL_DEBUG_LOCATION_ENABLE 1 +#define SSL_ASSERT_ENABLE 0 +#define SSL_DEBUG_LOCATION_ENABLE 0 #if SSL_DEBUG_ENBALE extern int ets_printf(const char *fmt, ...); diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 7f8503e2ab..133feb9dc1 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -27,6 +27,12 @@ typedef void RSA; typedef void STACK; typedef void BIO; +#define ossl_inline inline + +#define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__) +#define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__) +#define EVP_PKEY_METHOD_CALL(f, k, ...) k->method->pkey_##f(k, ##__VA_ARGS__) + #define STACK_OF(type) struct stack_st_##type #define SKM_DEFINE_STACK_OF(t1, t2, t3) \ @@ -38,6 +44,8 @@ typedef void BIO; #define DEFINE_STACK_OF(t) SKM_DEFINE_STACK_OF(t, t, t) +typedef int (*OPENSSL_sk_compfunc)(const void *, const void *); + struct stack_st; typedef struct stack_st OPENSSL_STACK; @@ -78,7 +86,12 @@ struct pkey_method_st; typedef struct pkey_method_st PKEY_METHOD; struct stack_st { - char *data; + + char **data; + + int num_alloc; + + OPENSSL_sk_compfunc c; }; struct evp_pkey_st { @@ -178,6 +191,8 @@ struct ssl_st int rwstate; + X509 *client_CA; + int err; void (*info_callback) (const SSL *ssl, int type, int val); @@ -249,8 +264,4 @@ typedef int (*next_proto_cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg); -#define SSL_METHOD_CALL(f, s, ...) s->method->func->ssl_##f(s, ##__VA_ARGS__) -#define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__) -#define EVP_PKEY_METHOD_CALL(f, k, ...) k->method->pkey_##f(k, ##__VA_ARGS__) - #endif diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index 0583cd94e4..ee3448544b 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -16,10 +16,18 @@ #define _SSL_X509_H_ #include "ssl_types.h" +#include "ssl_stack.h" DEFINE_STACK_OF(X509_NAME) -X509* sk_X509_NAME_new_null(void); +/* + * sk_X509_NAME_new_null - create a X509 certification object + * + * @param none + * + * @return X509 certification object point or NULL if failed + */ +X509* X509_new(void); X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 0d4c9c2080..b7506c8fb0 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -15,8 +15,8 @@ #ifndef _SSL_H_ #define _SSL_H_ -#include "ssl_port.h" -#include "internal/ssl_types.h" +#include "platform/ssl_port.h" +#include "internal/ssl_x509.h" /* { diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c index 0bdba459d3..caa901b660 100644 --- a/components/openssl/library/ssl_cert.c +++ b/components/openssl/library/ssl_cert.c @@ -37,7 +37,7 @@ CERT *ssl_cert_new(void) if (!cert->pkey) SSL_RET(failed2, "EVP_PKEY_new\n"); - cert->x509 = sk_X509_NAME_new_null(); + cert->x509 = X509_new(); if (!cert->x509) SSL_RET(failed3, "sk_X509_NAME_new_null\n"); diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 331ed17bd5..36e8cdf794 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -169,28 +169,27 @@ OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) */ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) { - int ret; SSL_CTX *ctx; CERT *cert; X509 *client_ca; if (!method) SSL_RET(go_failed1, "method\n"); - client_ca = sk_X509_NAME_new_null(); + client_ca = X509_new(); if (!client_ca) - SSL_ERR(-2, go_failed1, "sk_X509_NAME_new_null\n"); + SSL_RET(go_failed1, "sk_X509_NAME_new_null\n"); cert = ssl_cert_new(); if (!cert) - SSL_ERR(-2, go_failed2, "ssl_cert_new\n"); + SSL_RET(go_failed2, "ssl_cert_new\n"); ctx = (SSL_CTX *)ssl_zalloc(sizeof(SSL_CTX)); if (!ctx) - SSL_ERR(-2, go_failed3, "ssl_ctx_new:ctx\n"); + SSL_RET(go_failed3, "ssl_ctx_new:ctx\n"); ctx->method = method; - ctx->cert = cert; ctx->client_CA = client_ca; + ctx->cert = cert; ctx->version = method->version; @@ -268,7 +267,6 @@ const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) SSL *SSL_new(SSL_CTX *ctx) { int ret; - void *ssl_pm; SSL *ssl; if (!ctx) @@ -485,7 +483,7 @@ int SSL_write(SSL *ssl, const void *buffer, int len) else bytes = send_bytes; - ret = SSL_METHOD_CALL(send, ssl, buffer, len); + ret = SSL_METHOD_CALL(send, ssl, buffer, bytes); if (ret > 0) { pbuf += ret; send_bytes -= ret; @@ -798,8 +796,6 @@ int SSL_get_wfd(const SSL *ssl) */ int SSL_set_fd(SSL *ssl, int fd) { - int ret; - SSL_ASSERT(ssl); SSL_ASSERT(fd >= 0); @@ -820,8 +816,6 @@ int SSL_set_fd(SSL *ssl, int fd) */ int SSL_set_rfd(SSL *ssl, int fd) { - int ret; - SSL_ASSERT(ssl); SSL_ASSERT(fd >= 0); @@ -842,8 +836,6 @@ int SSL_set_rfd(SSL *ssl, int fd) */ int SSL_set_wfd(SSL *ssl, int fd) { - int ret; - SSL_ASSERT(ssl); SSL_ASSERT(fd >= 0); diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 0c8d9de8fa..c9866e27b5 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -78,6 +78,7 @@ EVP_PKEY *d2i_PrivateKey(int type, const unsigned char **pp, long length) { + int m = 0; int ret; EVP_PKEY *pkey; @@ -91,6 +92,7 @@ EVP_PKEY *d2i_PrivateKey(int type, pkey = EVP_PKEY_new();; if (!pkey) SSL_RET(failed1, "ssl_malloc\n"); + m = 1; } ret = EVP_PKEY_METHOD_CALL(load, pkey, *pp, length); @@ -103,7 +105,8 @@ EVP_PKEY *d2i_PrivateKey(int type, return pkey; failed2: - EVP_PKEY_free(pkey); + if (m) + EVP_PKEY_free(pkey); failed1: return NULL; } diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index e322b6ad3d..9c38849dd6 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -24,7 +24,7 @@ * * @return X509 certification object point or NULL if failed */ -X509* sk_X509_NAME_new_null(void) +X509* X509_new(void) { int ret; X509 *x; @@ -73,6 +73,7 @@ void X509_free(X509 *x) */ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) { + int m = 0; int ret; X509 *x; @@ -82,9 +83,10 @@ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) if (cert && *cert) { x = *cert; } else { - x = sk_X509_NAME_new_null(); + x = X509_new(); if (!x) SSL_RET(failed1, "sk_X509_NAME_new_null\n"); + m = 1; } ret = X509_METHOD_CALL(load, x, buffer, len); @@ -94,7 +96,8 @@ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) return x; failed2: - X509_free(x); + if (m) + X509_free(x); failed1: return NULL; } @@ -111,9 +114,14 @@ failed1: */ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) { + int ret; + SSL_ASSERT(ctx); SSL_ASSERT(x); + if (ctx->client_CA) + X509_free(ctx->client_CA); + ctx->client_CA = x; return 1; @@ -131,7 +139,17 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) */ int SSL_add_client_CA(SSL *ssl, X509 *x) { + int ret; + SSL_ASSERT(ssl); + SSL_ASSERT(x); + + if (ssl->client_CA) + X509_free(ssl->client_CA); + + ssl->client_CA = x; + + return 1; } /* From b3145446aa31118a3ab8d58f771ebc3998cd10cb Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 15:15:16 +0800 Subject: [PATCH 009/285] components/openssl: add function "ssl_pm_get_verify_result" 1. add function ssl_pm_get_verify_result 2. add its platform low-level interface --- components/openssl/include/internal/ssl_code.h | 1 + .../openssl/include/internal/ssl_methods.h | 2 ++ components/openssl/include/internal/ssl_types.h | 4 ++++ components/openssl/include/platform/ssl_pm.h | 2 ++ components/openssl/library/ssl_lib.c | 14 ++++++++++++++ components/openssl/library/ssl_methods.c | 1 + components/openssl/library/ssl_x509.c | 4 ---- components/openssl/platform/ssl_pm.c | 16 ++++++++++++++++ 8 files changed, 40 insertions(+), 4 deletions(-) diff --git a/components/openssl/include/internal/ssl_code.h b/components/openssl/include/internal/ssl_code.h index 1510ce6ff4..de86e07df1 100644 --- a/components/openssl/include/internal/ssl_code.h +++ b/components/openssl/include/internal/ssl_code.h @@ -17,6 +17,7 @@ #include "ssl3.h" #include "tls1.h" +#include "x509_vfy.h" /* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ # define SSL_SENT_SHUTDOWN 1 diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index b72b17ad3d..244eec38dd 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -21,6 +21,7 @@ read, send, pending, \ set_fd, get_fd, \ set_bufflen, \ + get_verify_result, \ get_state) \ static const SSL_METHOD_FUNC func_name LOCAL_ATRR = { \ new, \ @@ -34,6 +35,7 @@ set_fd, \ get_fd, \ set_bufflen, \ + get_verify_result, \ get_state \ }; diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 133feb9dc1..761250eef7 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -193,6 +193,8 @@ struct ssl_st X509 *client_CA; + long verify_result; + int err; void (*info_callback) (const SSL *ssl, int type, int val); @@ -235,6 +237,8 @@ struct ssl_method_func_st { void (*ssl_set_bufflen)(SSL *ssl, int len); + long (*ssl_get_verify_result)(const SSL *ssl); + OSSL_HANDSHAKE_STATE (*ssl_get_state)(const SSL *ssl); }; diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index 783ba5445e..3f64a4ae32 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -49,4 +49,6 @@ void pkey_pm_free(EVP_PKEY *pkey); int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len); void pkey_pm_unload(EVP_PKEY *pkey); +long ssl_pm_get_verify_result(const SSL *ssl); + #endif diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 36e8cdf794..ac41be6276 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -1731,3 +1731,17 @@ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_C SSL_ASSERT(ssl); SSL_ASSERT(verify_callback); } + +/* + * SSL_get_verify_result - get the verifying result of the SSL certification + * + * @param ssl - the SSL point + * + * @return the result of verifying + */ +long SSL_get_verify_result(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return SSL_METHOD_CALL(get_verify_result, ssl); +} diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index 0c5c6e7fa4..c6fb40e59c 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -25,6 +25,7 @@ IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, ssl_pm_read, ssl_pm_send, ssl_pm_pending, ssl_pm_set_fd, ssl_pm_get_fd, ssl_pm_set_bufflen, + ssl_pm_get_verify_result, ssl_pm_get_state); /* diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 9c38849dd6..102e5a543a 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -114,8 +114,6 @@ failed1: */ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) { - int ret; - SSL_ASSERT(ctx); SSL_ASSERT(x); @@ -139,8 +137,6 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) */ int SSL_add_client_CA(SSL *ssl, X509 *x) { - int ret; - SSL_ASSERT(ssl); SSL_ASSERT(x); diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 948c1bc4ee..ebb9687ea8 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -444,3 +444,19 @@ void ssl_pm_set_bufflen(SSL *ssl, int len) { max_content_len = len; } + +long ssl_pm_get_verify_result(const SSL *ssl) +{ + long ret; + long verify_result; + struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + + ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl); + + if (!ret) + verify_result = X509_V_OK; + else + verify_result = X509_V_ERR_UNSPECIFIED; + + return verify_result; +} From 6f07409d7c639bfa9a6eecbf97a74d4c8d762861 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 15:30:25 +0800 Subject: [PATCH 010/285] components/openssl: add function to set and get verify depth 1. add function to set and get SSL verify depth 2. add function to set and get SSL context verify depth 3. add X509_VERIFY_PARAM structure --- .../openssl/include/internal/ssl_types.h | 13 +++++ components/openssl/library/ssl_lib.c | 58 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 761250eef7..d001befdb9 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -76,6 +76,9 @@ typedef struct cert_st CERT; struct x509_st; typedef struct x509_st X509; +struct X509_VERIFY_PARAM_st; +typedef struct X509_VERIFY_PARAM_st X509_VERIFY_PARAM; + struct evp_pkey_st; typedef struct evp_pkey_st EVP_PKEY; @@ -139,6 +142,12 @@ struct ssl_session_st { long time; }; +struct X509_VERIFY_PARAM_st { + + int depth; + +}; + struct ssl_ctx_st { int version; @@ -164,6 +173,8 @@ struct ssl_ctx_st int read_ahead; int read_buffer_len; + + X509_VERIFY_PARAM param; }; struct ssl_st @@ -195,6 +206,8 @@ struct ssl_st long verify_result; + X509_VERIFY_PARAM param; + int err; void (*info_callback) (const SSL *ssl, int type, int val); diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index ac41be6276..442920f119 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -1745,3 +1745,61 @@ long SSL_get_verify_result(const SSL *ssl) return SSL_METHOD_CALL(get_verify_result, ssl); } + +/* + * SSL_CTX_get_verify_depth - get the SSL verifying depth of the SSL context + * + * @param ctx - SSL context point + * + * @return verifying depth + */ +int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) +{ + SSL_ASSERT(ctx); + + return ctx->param.depth; +} + +/* + * SSL_CTX_set_verify_depth - set the SSL verify depth of the SSL context + * + * @param ctx - SSL context point + * @param depth - verifying depth + * + * @return one + */ +void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) +{ + SSL_ASSERT(ctx); + + ctx->param.depth = depth; +} + +/* + * SSL_get_verify_depth - get the SSL verifying depth of the SSL + * + * @param ctx - SSL point + * + * @return verifying depth + */ +int SSL_get_verify_depth(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->param.depth; +} + +/* + * SSL_set_verify_depth - set the SSL verify depth of the SSL + * + * @param ctx - SSL point + * @param depth - verifying depth + * + * @return one + */ +void SSL_set_verify_depth(SSL *ssl, int depth) +{ + SSL_ASSERT(ssl); + + ssl->param.depth = depth; +} From 2faa2376a0de66af42ff2c55e00106472090677a Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 15:39:28 +0800 Subject: [PATCH 011/285] components/openssl: add empty function to load verify file into SSL context 1. add empty function to load private key into SSL context 2. add empty function to load certification into SSL context 3. add function to load RSA private key --- components/openssl/include/openssl/ssl.h | 4 +-- components/openssl/library/ssl_pkey.c | 31 ++++++++++++++++++++++++ components/openssl/library/ssl_x509.c | 15 ++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index b7506c8fb0..865405d868 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -927,7 +927,7 @@ int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, l * 1 : OK * 0 : failed */ -int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type);//adds the first private key found in file to ctx, The formatting type of the certificate must be specified from the known types SSL_FILETYPE_PEM, SSL_FILETYPE_ASN1. +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); /* * SSL_CTX_use_certificate - load the RSA private key into SSL context @@ -952,7 +952,7 @@ int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); * 1 : OK * 0 : failed */ -int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, unsigned char *d, long len); +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); /* * SSL_CTX_use_certificate_file - load the RSA private key file into SSL context diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index c9866e27b5..1ab080ac28 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -165,3 +165,34 @@ failed1: return 0; } +/* + * SSL_CTX_use_certificate_file - load the private key file into SSL context + * + * @param ctx - SSL context point + * @param file - private key file name + * @param type - private key encoding type + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) +{ + return 0; +} + +/* + * SSL_CTX_use_certificate_ASN1 - load the RSA ASN1 private key into SSL context + * + * @param ctx - SSL context point + * @param d - data point + * @param len - RSA private key length + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len) +{ + return SSL_CTX_use_PrivateKey_ASN1(0, ctx, d, len); +} diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 102e5a543a..b0ddd42593 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -213,3 +213,18 @@ failed1: return 0; } +/* + * SSL_CTX_use_certificate_file - load the certification file into SSL context + * + * @param ctx - SSL context point + * @param file - certification file name + * @param type - certification encoding type + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) +{ + return 0; +} From a99f6bd727cea8d8cdee32d2f28b09c6cf4fae99 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 15:56:56 +0800 Subject: [PATCH 012/285] components/openssl: add function load verify data into SSL 1. add function to load private key into SSL 1. add function to load certification into SSL --- components/openssl/library/ssl_pkey.c | 70 ++++++++++++++++++++++++++ components/openssl/library/ssl_x509.c | 71 +++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 1ab080ac28..a86a257e98 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -131,6 +131,26 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) return 1; } +/* + * SSL_CTX_use_certificate - set the SSL private key + * + * @param ctx - SSL point + * @param x - private key point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(pkey); + + ssl->cert->pkey = pkey; + + return 1; +} + /* * SSL_CTX_use_PrivateKey_ASN1 - load private key into the SSL context * @@ -165,6 +185,40 @@ failed1: return 0; } +/* + * SSL_use_PrivateKey_ASN1 - load private key into the SSL + * + * @param type - private key type + * @param ctx - SSL context point + * @param d - private key context point + * @param len - private key context bytes + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, + const unsigned char *d, long len) +{ + int ret; + EVP_PKEY *pkey; + + pkey = d2i_PrivateKey(0, &ssl->cert->pkey, &d, len); + if (!pkey) + SSL_RET(failed1, "d2i_PrivateKey\n"); + + ret = SSL_use_PrivateKey(ssl, pkey); + if (!ret) + SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); + + return 1; + +failed2: + EVP_PKEY_free(pkey); +failed1: + return 0; +} + /* * SSL_CTX_use_certificate_file - load the private key file into SSL context * @@ -181,6 +235,22 @@ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) return 0; } +/* + * SSL_use_PrivateKey_file - load the private key file into SSL + * + * @param ctx - SSL point + * @param file - private key file name + * @param type - private key encoding type + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) +{ + return 0; +} + /* * SSL_CTX_use_certificate_ASN1 - load the RSA ASN1 private key into SSL context * diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index b0ddd42593..ba5c924e75 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -168,6 +168,26 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) return 1; } +/* + * SSL_CTX_use_certificate - set the SSL certification + * + * @param ctx - SSL point + * @param x - X509 certification point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_use_certificate(SSL *ssl, X509 *x) +{ + SSL_ASSERT(ctx); + SSL_ASSERT(x); + + ssl->cert->x509 = x; + + return 1; +} + /* * SSL_get_certificate - get the SSL certification point * @@ -177,6 +197,8 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) */ X509 *SSL_get_certificate(const SSL *ssl) { + SSL_ASSERT(ssl); + return ssl->cert->x509; } @@ -213,6 +235,39 @@ failed1: return 0; } +/* + * SSL_use_certificate_ASN1 - load certification into the SSL + * + * @param ctx - SSL point + * @param len - certification context bytes + * @param d - certification context point + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_use_certificate_ASN1(SSL *ssl, int len, + const unsigned char *d) +{ + int ret; + X509 *cert; + + cert = d2i_X509(&ssl->cert->x509, d, len); + if (!cert) + SSL_RET(failed1, "d2i_X509\n"); + + ret = SSL_use_certificate(ssl, cert); + if (!ret) + SSL_RET(failed2, "SSL_use_certificate\n"); + + return 1; + +failed2: + X509_free(cert); +failed1: + return 0; +} + /* * SSL_CTX_use_certificate_file - load the certification file into SSL context * @@ -228,3 +283,19 @@ int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) { return 0; } + +/* + * SSL_use_certificate_file - load the certification file into SSL + * + * @param ctx - SSL point + * @param file - certification file name + * @param type - certification encoding type + * + * @return + * 1 : OK + * 0 : failed + */ +int SSL_use_certificate_file(SSL *ssl, const char *file, int type) +{ + return 0; +} From fa6f03f77f3ff81f676d6bad017194c50d7cc2a1 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 16:08:36 +0800 Subject: [PATCH 013/285] components/openssl: add function to load certification or private key more than one time --- components/openssl/platform/ssl_pm.c | 42 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index ebb9687ea8..17cc080bb6 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -339,6 +339,16 @@ int x509_pm_new(X509 *x) return 0; } +void x509_pm_unload(X509 *x) +{ + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; + + if (x509_pm->load) + mbedtls_x509_crt_free(&x509_pm->x509_crt); + + x509_pm->load = 0; +} + int x509_pm_load(X509 *x, const unsigned char *buffer, int len) { int ret; @@ -352,6 +362,8 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; + x509_pm_unload(x); + mbedtls_x509_crt_init(&x509_pm->x509_crt); ret = mbedtls_x509_crt_parse(&x509_pm->x509_crt, load_buf, len); ssl_free(load_buf); @@ -367,15 +379,6 @@ failed1: return -1; } -void x509_pm_unload(X509 *x) -{ - struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; - - mbedtls_x509_crt_free(&x509_pm->x509_crt); - - x509_pm->load = 0; -} - void x509_pm_free(X509 *x) { x509_pm_unload(x); @@ -396,6 +399,16 @@ int pkey_pm_new(EVP_PKEY *pkey) return 0; } +void pkey_pm_unload(EVP_PKEY *pkey) +{ + struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; + + if (pkey_pm->load) + mbedtls_pk_free(&pkey_pm->pkey); + + pkey_pm->load = 0; +} + int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len) { int ret; @@ -409,6 +422,8 @@ int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len) ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; + pkey_pm_unload(pkey); + mbedtls_pk_init(&pkey_pm->pkey); ret = mbedtls_pk_parse_key(&pkey_pm->pkey, load_buf, len, NULL, 0); ssl_free(load_buf); @@ -424,15 +439,6 @@ failed1: return -1; } -void pkey_pm_unload(EVP_PKEY *pkey) -{ - struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; - - mbedtls_pk_free(&pkey_pm->pkey); - - pkey_pm->load = 0; -} - void pkey_pm_free(EVP_PKEY *pkey) { pkey_pm_unload(pkey); From f796b4e58ef2311e11b51f15ef5e8807c84c5272 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 16:41:51 +0800 Subject: [PATCH 014/285] components/openssl: SSL load verify data from itself structure when "new" --- components/openssl/include/internal/ssl_types.h | 4 ++++ components/openssl/library/ssl_lib.c | 3 +++ components/openssl/library/ssl_pkey.c | 9 ++++++++- components/openssl/library/ssl_x509.c | 12 ++++++++++-- components/openssl/platform/ssl_pm.c | 6 +++--- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index d001befdb9..6f2fb5a2f2 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -99,6 +99,8 @@ struct stack_st { struct evp_pkey_st { + int ref; + void *pkey_pm; const PKEY_METHOD *method; @@ -106,6 +108,8 @@ struct evp_pkey_st { struct x509_st { + int ref; + /* X509 certification platform private point */ void *x509_pm; diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 442920f119..7e3b4554d6 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -282,6 +282,9 @@ SSL *SSL_new(SSL_CTX *ctx) ssl->version = ctx->version; ssl->options = ctx->options; + ssl->cert = ctx->cert; + ssl->client_CA = ctx->client_CA; + ret = SSL_METHOD_CALL(new, ssl); if (ret) SSL_RET(failed2, "ssl_new\n"); diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index a86a257e98..15c4977b0f 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -177,6 +177,8 @@ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, if (!ret) SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); + ctx->cert->pkey->ref++; + return 1; failed2: @@ -203,7 +205,10 @@ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, int ret; EVP_PKEY *pkey; - pkey = d2i_PrivateKey(0, &ssl->cert->pkey, &d, len); + if (ssl->cert->pkey->ref) + SSL_RET(failed1); + + pkey = d2i_PrivateKey(0, NULL, &d, len); if (!pkey) SSL_RET(failed1, "d2i_PrivateKey\n"); @@ -211,6 +216,8 @@ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, if (!ret) SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); + ssl->cert->pkey->ref++; + return 1; failed2: diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index ba5c924e75..6e249eef58 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -85,7 +85,7 @@ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) } else { x = X509_new(); if (!x) - SSL_RET(failed1, "sk_X509_NAME_new_null\n"); + SSL_RET(failed1, "X509_new\n"); m = 1; } @@ -218,6 +218,7 @@ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, { int ret; X509 *cert; + const unsigned char *pbuf; cert = d2i_X509(&ctx->cert->x509, d, len); if (!cert) @@ -227,6 +228,8 @@ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, if (!ret) SSL_RET(failed2, "SSL_CTX_use_certificate\n"); + ctx->cert->x509->ref++; + return 1; failed2: @@ -252,7 +255,10 @@ int SSL_use_certificate_ASN1(SSL *ssl, int len, int ret; X509 *cert; - cert = d2i_X509(&ssl->cert->x509, d, len); + if (ssl->cert->x509->ref) + SSL_RET(failed1); + + cert = d2i_X509(NULL, d, len); if (!cert) SSL_RET(failed1, "d2i_X509\n"); @@ -260,6 +266,8 @@ int SSL_use_certificate_ASN1(SSL *ssl, int len, if (!ret) SSL_RET(failed2, "SSL_use_certificate\n"); + ssl->cert->x509->ref++; + return 1; failed2: diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 17cc080bb6..d4ed2ececb 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -120,7 +120,7 @@ int ssl_pm_new(SSL *ssl) mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL); - x509_pm = (struct x509_pm *)ctx->client_CA->x509_pm; + x509_pm = (struct x509_pm *)ssl->client_CA->x509_pm; if (x509_pm->load) { mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &x509_pm->x509_crt, NULL); @@ -130,9 +130,9 @@ int ssl_pm_new(SSL *ssl) } mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); - pkey_pm = (struct pkey_pm *)ctx->cert->pkey->pkey_pm; + pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; if (pkey_pm->load) { - x509_pm = (struct x509_pm *)ctx->cert->x509->x509_pm; + x509_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); if (ret) From 18787fd4fc9e1a0defb66cb29541432b8c76c4a9 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 17:20:07 +0800 Subject: [PATCH 015/285] components/openssl: add empty fucntion to get peer certification and fix ref overflow --- .../openssl/include/internal/ssl_types.h | 2 ++ components/openssl/library/ssl_pkey.c | 4 ++-- components/openssl/library/ssl_x509.c | 19 ++++++++++++++++--- components/openssl/platform/ssl_pm.c | 5 ++--- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 6f2fb5a2f2..7a0bd0d76f 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -144,6 +144,8 @@ struct ssl_session_st { long timeout; long time; + + X509 *peer; }; struct X509_VERIFY_PARAM_st { diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 15c4977b0f..7278287a63 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -177,7 +177,7 @@ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, if (!ret) SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); - ctx->cert->pkey->ref++; + ctx->cert->pkey->ref = 1; return 1; @@ -216,7 +216,7 @@ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, if (!ret) SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); - ssl->cert->pkey->ref++; + ssl->cert->pkey->ref = 1; return 1; diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 6e249eef58..19c94c3eca 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -218,7 +218,6 @@ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, { int ret; X509 *cert; - const unsigned char *pbuf; cert = d2i_X509(&ctx->cert->x509, d, len); if (!cert) @@ -228,7 +227,7 @@ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, if (!ret) SSL_RET(failed2, "SSL_CTX_use_certificate\n"); - ctx->cert->x509->ref++; + ctx->cert->x509->ref = 1; return 1; @@ -266,7 +265,7 @@ int SSL_use_certificate_ASN1(SSL *ssl, int len, if (!ret) SSL_RET(failed2, "SSL_use_certificate\n"); - ssl->cert->x509->ref++; + ssl->cert->x509->ref = 1; return 1; @@ -307,3 +306,17 @@ int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { return 0; } + +/* + * SSL_get_peer_certificate - get peer certification + * + * @param ssl - SSL point + * + * @return certification + */ +X509 *SSL_get_peer_certificate(const SSL *ssl) +{ + SSL_ASSERT(ssl); + + return ssl->session.peer; +} diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index d4ed2ececb..1ddd1f30d2 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -74,7 +74,6 @@ int ssl_pm_new(SSL *ssl) int mode; int version; - SSL_CTX *ctx = ssl->ctx; const SSL_METHOD *method = ssl->method; struct x509_pm *x509_pm; @@ -185,9 +184,9 @@ int ssl_pm_handshake(SSL *ssl) } ssl_speed_up_exit(); - if (!mbed_ret) + if (!mbed_ret) { ret = 1; - else { + } else { ret = 0; SSL_DEBUG(1, "mbedtls_ssl_handshake [-0x%x]\n", -mbed_ret); } From 1bfedf9816a12581a35a053b964b40b8182c44d9 Mon Sep 17 00:00:00 2001 From: dongheng Date: Thu, 22 Sep 2016 18:33:55 +0800 Subject: [PATCH 016/285] components/openssl: fix the SSL_free memory leak --- components/openssl/library/ssl_lib.c | 2 +- components/openssl/platform/ssl_pm.c | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 7e3b4554d6..20c8931457 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -266,7 +266,7 @@ const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) */ SSL *SSL_new(SSL_CTX *ctx) { - int ret; + int ret = 0; SSL *ssl; if (!ctx) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 1ddd1f30d2..04e370f9fc 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -162,13 +162,13 @@ void ssl_pm_free(SSL *ssl) { struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; - mbedtls_ssl_config_free(&ssl_pm->conf); mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); mbedtls_entropy_free(&ssl_pm->entropy); + mbedtls_ssl_config_free(&ssl_pm->conf); mbedtls_ssl_free(&ssl_pm->ssl); - mbedtls_net_free(&ssl_pm->fd); - mbedtls_net_free(&ssl_pm->cl_fd); + ssl_free(ssl_pm); + ssl->ssl_pm = NULL; } int ssl_pm_handshake(SSL *ssl) @@ -383,6 +383,7 @@ void x509_pm_free(X509 *x) x509_pm_unload(x); ssl_free(x->x509_pm); + x->x509_pm = NULL; } int pkey_pm_new(EVP_PKEY *pkey) @@ -443,6 +444,7 @@ void pkey_pm_free(EVP_PKEY *pkey) pkey_pm_unload(pkey); ssl_free(pkey->pkey_pm); + pkey->pkey_pm = NULL; } void ssl_pm_set_bufflen(SSL *ssl, int len) From 9fc054bb553c3df68eb3f956c4496ac178422c3f Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 10:33:31 +0800 Subject: [PATCH 017/285] components/openssl: SSL load cert with creating new cert object 1. when 'SSL_new' SSL's cert is pointed to SSL context cert If SSL load new cert, it will create a new cert object 2. change some debug informaion --- .../openssl/include/internal/ssl_types.h | 13 ++--- components/openssl/library/ssl_cert.c | 2 +- components/openssl/library/ssl_lib.c | 6 +++ components/openssl/library/ssl_pkey.c | 44 +++++++++++----- components/openssl/library/ssl_x509.c | 50 +++++++++++++------ components/openssl/platform/ssl_pm.c | 32 +++++++----- 6 files changed, 100 insertions(+), 47 deletions(-) diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 7a0bd0d76f..c872c5191c 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -99,8 +99,6 @@ struct stack_st { struct evp_pkey_st { - int ref; - void *pkey_pm; const PKEY_METHOD *method; @@ -108,8 +106,6 @@ struct evp_pkey_st { struct x509_st { - int ref; - /* X509 certification platform private point */ void *x509_pm; @@ -127,6 +123,7 @@ struct cert_st { }; struct ossl_statem_st { + MSG_FLOW_STATE state; int hand_state; @@ -193,8 +190,14 @@ struct ssl_st /* shut things down(0x01 : sent, 0x02 : received) */ int shutdown; + int crt_reload; + CERT *cert; + int ca_reload; + + X509 *client_CA; + SSL_CTX *ctx; const SSL_METHOD *method; @@ -208,8 +211,6 @@ struct ssl_st int rwstate; - X509 *client_CA; - long verify_result; X509_VERIFY_PARAM param; diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c index caa901b660..2d82e62aaa 100644 --- a/components/openssl/library/ssl_cert.c +++ b/components/openssl/library/ssl_cert.c @@ -39,7 +39,7 @@ CERT *ssl_cert_new(void) cert->x509 = X509_new(); if (!cert->x509) - SSL_RET(failed3, "sk_X509_NAME_new_null\n"); + SSL_RET(failed3, "X509_new\n"); return cert; diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 20c8931457..cc218f9a26 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -312,6 +312,12 @@ void SSL_free(SSL *ssl) SSL_METHOD_CALL(free, ssl); + if (ssl->ca_reload) + X509_free(ssl->client_CA); + + if (ssl->crt_reload) + ssl_cert_free(ssl->cert); + ssl_free(ssl); } diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 7278287a63..c77785f473 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -14,6 +14,7 @@ #include "ssl_lib.h" #include "ssl_pkey.h" +#include "ssl_cert.h" #include "ssl_methods.h" #include "ssl_dbg.h" #include "ssl_port.h" @@ -38,7 +39,7 @@ EVP_PKEY* EVP_PKEY_new(void) ret = EVP_PKEY_METHOD_CALL(new, pkey); if (ret) - SSL_RET(failed2, "pkey_new\n"); + SSL_RET(failed2, "EVP_PKEY_METHOD_CALL\n"); return pkey; @@ -91,13 +92,13 @@ EVP_PKEY *d2i_PrivateKey(int type, } else { pkey = EVP_PKEY_new();; if (!pkey) - SSL_RET(failed1, "ssl_malloc\n"); + SSL_RET(failed1, "EVP_PKEY_new\n"); m = 1; } ret = EVP_PKEY_METHOD_CALL(load, pkey, *pp, length); if (ret) - SSL_RET(failed2, "pkey_pm_load_crt\n"); + SSL_RET(failed2, "EVP_PKEY_METHOD_CALL\n"); if (a) *a = pkey; @@ -177,8 +178,6 @@ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, if (!ret) SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); - ctx->cert->pkey->ref = 1; - return 1; failed2: @@ -203,25 +202,44 @@ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const unsigned char *d, long len) { int ret; + int reload; EVP_PKEY *pkey; + CERT *cert; + CERT *old_cert; - if (ssl->cert->pkey->ref) - SSL_RET(failed1); + if (!ssl->crt_reload) { + cert = ssl_cert_new(); + if (!cert) + SSL_RET(failed1, "ssl_cert_new\n"); - pkey = d2i_PrivateKey(0, NULL, &d, len); + old_cert = ssl->cert ; + ssl->cert = cert; + + ssl->crt_reload = 1; + + reload = 1; + } else { + reload = 0; + } + + pkey = d2i_PrivateKey(0, &ssl->cert->pkey, &d, len); if (!pkey) - SSL_RET(failed1, "d2i_PrivateKey\n"); + SSL_RET(failed2, "d2i_PrivateKey\n"); ret = SSL_use_PrivateKey(ssl, pkey); if (!ret) - SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); - - ssl->cert->pkey->ref = 1; + SSL_RET(failed3, "SSL_use_PrivateKey\n"); return 1; -failed2: +failed3: EVP_PKEY_free(pkey); +failed2: + if (reload) { + ssl->cert = old_cert; + ssl_cert_free(cert); + ssl->crt_reload = 0; + } failed1: return 0; } diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 19c94c3eca..9ca60d8b31 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -13,6 +13,7 @@ // limitations under the License. #include "ssl_x509.h" +#include "ssl_cert.h" #include "ssl_methods.h" #include "ssl_dbg.h" #include "ssl_port.h" @@ -91,7 +92,7 @@ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) ret = X509_METHOD_CALL(load, x, buffer, len); if (ret) - SSL_RET(failed2, "x509_load\n"); + SSL_RET(failed2, "X509_METHOD_CALL\n"); return x; @@ -140,7 +141,9 @@ int SSL_add_client_CA(SSL *ssl, X509 *x) SSL_ASSERT(ssl); SSL_ASSERT(x); - if (ssl->client_CA) + if (!ssl->ca_reload) + ssl->ca_reload = 1; + else X509_free(ssl->client_CA); ssl->client_CA = x; @@ -227,8 +230,6 @@ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, if (!ret) SSL_RET(failed2, "SSL_CTX_use_certificate\n"); - ctx->cert->x509->ref = 1; - return 1; failed2: @@ -252,25 +253,44 @@ int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d) { int ret; - X509 *cert; + int reload; + X509 *x; + CERT *cert; + CERT *old_cert; - if (ssl->cert->x509->ref) - SSL_RET(failed1); + if (!ssl->crt_reload) { + cert = ssl_cert_new(); + if (!cert) + SSL_RET(failed1, "ssl_cert_new\n"); - cert = d2i_X509(NULL, d, len); - if (!cert) - SSL_RET(failed1, "d2i_X509\n"); + old_cert = ssl->cert ; + ssl->cert = cert; - ret = SSL_use_certificate(ssl, cert); + ssl->crt_reload = 1; + + reload = 1; + } else { + reload = 0; + } + + x = d2i_X509(&ssl->cert->x509, d, len); + if (!x) + SSL_RET(failed2, "d2i_X509\n"); + + ret = SSL_use_certificate(ssl, x); if (!ret) - SSL_RET(failed2, "SSL_use_certificate\n"); - - ssl->cert->x509->ref = 1; + SSL_RET(failed3, "SSL_use_certificate\n"); return 1; +failed3: + X509_free(x); failed2: - X509_free(cert); + if (reload) { + ssl->cert = old_cert; + ssl_cert_free(cert); + ssl->crt_reload = 0; + } failed1: return 0; } diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 04e370f9fc..54e6cba25c 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -43,16 +43,16 @@ struct ssl_pm struct x509_pm { - int load; - mbedtls_x509_crt x509_crt; + + int load; }; struct pkey_pm { - int load; - mbedtls_pk_context pkey; + + int load; }; @@ -79,9 +79,13 @@ int ssl_pm_new(SSL *ssl) struct x509_pm *x509_pm; struct pkey_pm *pkey_pm; + ssl->session.peer = ssl_malloc(sizeof(X509)); + if (!ssl->session.peer) + SSL_ERR(ret, failed1, "ssl_malloc\n"); + ssl_pm = ssl_malloc(sizeof(struct ssl_pm)); if (!ssl_pm) - SSL_ERR(ret, failed1, "ssl_malloc\n"); + SSL_ERR(ret, failed2, "ssl_malloc\n"); mbedtls_net_init(&ssl_pm->fd); mbedtls_net_init(&ssl_pm->cl_fd); @@ -93,7 +97,7 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, pers, pers_len); if (ret) - SSL_ERR(ret, failed1, "mbedtls_ctr_drbg_seed:[-0x%x]\n", -ret); + SSL_ERR(ret, failed3, "mbedtls_ctr_drbg_seed:[-0x%x]\n", -ret); if (method->endpoint) { endpoint = MBEDTLS_SSL_IS_SERVER; @@ -102,7 +106,7 @@ int ssl_pm_new(SSL *ssl) } ret = mbedtls_ssl_config_defaults(&ssl_pm->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret) - SSL_ERR(ret, failed2, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); + SSL_ERR(ret, failed3, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); if (TLS1_2_VERSION == ssl->version) version = MBEDTLS_SSL_MINOR_VERSION_3; @@ -135,12 +139,12 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); if (ret) - SSL_ERR(ret, failed3, "mbedtls_ssl_conf_own_cert:[%d]\n", ret); + SSL_ERR(ret, failed4, "mbedtls_ssl_conf_own_cert:[%d]\n", ret); } ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); if (ret) - SSL_ERR(ret, failed4, "mbedtls_ssl_setup:[-0x%x]\n", -ret); + SSL_ERR(ret, failed5, "mbedtls_ssl_setup:[-0x%x]\n", -ret); mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, mbedtls_net_send, mbedtls_net_recv, NULL); @@ -148,12 +152,14 @@ int ssl_pm_new(SSL *ssl) return 0; -failed4: +failed5: mbedtls_ssl_config_free(&ssl_pm->conf); -failed3: +failed4: mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); -failed2: +failed3: mbedtls_entropy_free(&ssl_pm->entropy); +failed2: + ssl_free(ssl->session.peer); failed1: return -1; } @@ -186,6 +192,8 @@ int ssl_pm_handshake(SSL *ssl) if (!mbed_ret) { ret = 1; + + ssl->session.peer = (X509 *)mbedtls_ssl_get_peer_cert(&ssl_pm->ssl); } else { ret = 0; SSL_DEBUG(1, "mbedtls_ssl_handshake [-0x%x]\n", -mbed_ret); From 07c8bbca6c70836b46cf040a472eb348b1d9f59d Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 10:53:18 +0800 Subject: [PATCH 018/285] components/openssl: SSL low-level reload cert when user add new cert --- .../openssl/include/internal/ssl_methods.h | 2 ++ .../openssl/include/internal/ssl_types.h | 2 ++ components/openssl/include/platform/ssl_pm.h | 2 ++ components/openssl/library/ssl_methods.c | 1 + components/openssl/library/ssl_pkey.c | 19 +++++++++++- components/openssl/library/ssl_x509.c | 11 ++++++- components/openssl/platform/ssl_pm.c | 30 +++++++++++++++++++ 7 files changed, 65 insertions(+), 2 deletions(-) diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index 244eec38dd..2893db1888 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -22,6 +22,7 @@ set_fd, get_fd, \ set_bufflen, \ get_verify_result, \ + ssl_reload_crt, \ get_state) \ static const SSL_METHOD_FUNC func_name LOCAL_ATRR = { \ new, \ @@ -36,6 +37,7 @@ get_fd, \ set_bufflen, \ get_verify_result, \ + ssl_reload_crt, \ get_state \ }; diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index c872c5191c..47e6b0bf65 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -259,6 +259,8 @@ struct ssl_method_func_st { long (*ssl_get_verify_result)(const SSL *ssl); + int (*ssl_reload_crt)(SSL *ssl); + OSSL_HANDSHAKE_STATE (*ssl_get_state)(const SSL *ssl); }; diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index 3f64a4ae32..53bff0d80e 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -51,4 +51,6 @@ void pkey_pm_unload(EVP_PKEY *pkey); long ssl_pm_get_verify_result(const SSL *ssl); +int ssl_pm_reload_crt(SSL *ssl); + #endif diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index c6fb40e59c..0674f40587 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -26,6 +26,7 @@ IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, ssl_pm_set_fd, ssl_pm_get_fd, ssl_pm_set_bufflen, ssl_pm_get_verify_result, + ssl_pm_reload_crt, ssl_pm_get_state); /* diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index c77785f473..893a391dd8 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -127,6 +127,9 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) SSL_ASSERT(ctx); SSL_ASSERT(pkey); + if (ctx->cert->pkey) + EVP_PKEY_free(ctx->cert->pkey); + ctx->cert->pkey = pkey; return 1; @@ -144,12 +147,26 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) */ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { + int ret; + int ssl_ret; + SSL_ASSERT(ctx); SSL_ASSERT(pkey); + if (!ssl->ca_reload) + ssl->ca_reload = 1; + else + EVP_PKEY_free(ssl->cert->pkey); + ssl->cert->pkey = pkey; - return 1; + ssl_ret = SSL_METHOD_CALL(reload_crt, ssl); + if (ssl_ret) + ret = 0; + else + ret = 1; + + return ret; } /* diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 9ca60d8b31..2368344a7a 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -138,6 +138,9 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) */ int SSL_add_client_CA(SSL *ssl, X509 *x) { + int ret; + int ssl_ret; + SSL_ASSERT(ssl); SSL_ASSERT(x); @@ -148,7 +151,13 @@ int SSL_add_client_CA(SSL *ssl, X509 *x) ssl->client_CA = x; - return 1; + ssl_ret = SSL_METHOD_CALL(reload_crt, ssl); + if (ssl_ret) + ret = 0; + else + ret = 1; + + return ret; } /* diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 54e6cba25c..00c8f83020 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -475,3 +475,33 @@ long ssl_pm_get_verify_result(const SSL *ssl) return verify_result; } + +int ssl_pm_reload_crt(SSL *ssl) +{ + int ret; + int mode; + struct ssl_pm *ssl_pm = ssl->ssl_pm; + struct x509_pm *x509_pm; + struct pkey_pm *pkey_pm; + + x509_pm = (struct x509_pm *)ssl->client_CA->x509_pm; + if (x509_pm->load) { + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &x509_pm->x509_crt, NULL); + + mode = MBEDTLS_SSL_VERIFY_REQUIRED; + } else { + mode = MBEDTLS_SSL_VERIFY_NONE; + } + mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); + + pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; + if (pkey_pm->load) { + x509_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; + + ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); + if (ret) + return -1; + } + + return 0; +} From f5d9bfc7aecdda78f849e0f9bd7dd4b228feb8ab Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 11:03:13 +0800 Subject: [PATCH 019/285] components/openssl: fix SSL get peer cert struct point type error 1. fix SSL get peer cert struct point type error 2. some function use "zalloc" instead of "malloc" --- components/openssl/library/ssl_pkey.c | 2 +- components/openssl/library/ssl_x509.c | 2 +- components/openssl/platform/ssl_pm.c | 18 +++++++++--------- components/openssl/platform/ssl_port.c | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 893a391dd8..ab56fe789d 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -31,7 +31,7 @@ EVP_PKEY* EVP_PKEY_new(void) int ret; EVP_PKEY *pkey; - pkey = ssl_malloc(sizeof(EVP_PKEY)); + pkey = ssl_zalloc(sizeof(EVP_PKEY)); if (!pkey) SSL_RET(failed1, "ssl_malloc\n"); diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 2368344a7a..431f03caa9 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -30,7 +30,7 @@ X509* X509_new(void) int ret; X509 *x; - x = ssl_malloc(sizeof(X509)); + x = ssl_zalloc(sizeof(X509)); if (!x) SSL_RET(failed1, "ssl_malloc\n"); diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 00c8f83020..10b736aa9c 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -79,13 +79,13 @@ int ssl_pm_new(SSL *ssl) struct x509_pm *x509_pm; struct pkey_pm *pkey_pm; - ssl->session.peer = ssl_malloc(sizeof(X509)); + ssl->session.peer = ssl_zalloc(sizeof(X509)); if (!ssl->session.peer) - SSL_ERR(ret, failed1, "ssl_malloc\n"); + SSL_ERR(ret, failed1, "ssl_zalloc\n"); - ssl_pm = ssl_malloc(sizeof(struct ssl_pm)); + ssl_pm = ssl_zalloc(sizeof(struct ssl_pm)); if (!ssl_pm) - SSL_ERR(ret, failed2, "ssl_malloc\n"); + SSL_ERR(ret, failed2, "ssl_zalloc\n"); mbedtls_net_init(&ssl_pm->fd); mbedtls_net_init(&ssl_pm->cl_fd); @@ -193,7 +193,7 @@ int ssl_pm_handshake(SSL *ssl) if (!mbed_ret) { ret = 1; - ssl->session.peer = (X509 *)mbedtls_ssl_get_peer_cert(&ssl_pm->ssl); + ssl->session.peer->x509_pm = (struct x509_pm *)mbedtls_ssl_get_peer_cert(&ssl_pm->ssl); } else { ret = 0; SSL_DEBUG(1, "mbedtls_ssl_handshake [-0x%x]\n", -mbed_ret); @@ -337,7 +337,7 @@ int x509_pm_new(X509 *x) { struct x509_pm *x509_pm; - x509_pm = ssl_malloc(sizeof(struct x509_pm)); + x509_pm = ssl_zalloc(sizeof(struct x509_pm)); if (!x509_pm) return -1; @@ -364,7 +364,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) load_buf = ssl_malloc(len + 1); if (!load_buf) - SSL_RET(failed1, ""); + SSL_RET(failed1); ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; @@ -398,7 +398,7 @@ int pkey_pm_new(EVP_PKEY *pkey) { struct pkey_pm *pkey_pm; - pkey_pm = ssl_malloc(sizeof(struct pkey_pm)); + pkey_pm = ssl_zalloc(sizeof(struct pkey_pm)); if (!pkey_pm) return -1; @@ -425,7 +425,7 @@ int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len) load_buf = ssl_malloc(len + 1); if (!load_buf) - SSL_RET(failed1, ""); + SSL_RET(failed1); ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c index 3e6ada5cc9..b57e703a17 100644 --- a/components/openssl/platform/ssl_port.c +++ b/components/openssl/platform/ssl_port.c @@ -31,7 +31,7 @@ void* ssl_zalloc(size_t size) void *ssl_malloc(size_t size) { - return ssl_zalloc(size); + return malloc(size); } void ssl_free(void *p) From e475d0539eb8c960f7996d0ec6c0842f4534a235 Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 11:41:57 +0800 Subject: [PATCH 020/285] components/openssl: add SSL and SSL context verify mode selection --- .../openssl/include/internal/ssl_code.h | 5 + .../openssl/include/internal/ssl_methods.h | 2 - .../openssl/include/internal/ssl_types.h | 8 +- components/openssl/include/platform/ssl_pm.h | 2 - components/openssl/library/ssl_lib.c | 50 +++++++--- components/openssl/library/ssl_methods.c | 1 - components/openssl/library/ssl_pkey.c | 8 +- components/openssl/library/ssl_x509.c | 11 +-- components/openssl/platform/ssl_pm.c | 96 ++++++++----------- 9 files changed, 88 insertions(+), 95 deletions(-) diff --git a/components/openssl/include/internal/ssl_code.h b/components/openssl/include/internal/ssl_code.h index de86e07df1..e76b35abe9 100644 --- a/components/openssl/include/internal/ssl_code.h +++ b/components/openssl/include/internal/ssl_code.h @@ -23,6 +23,11 @@ # define SSL_SENT_SHUTDOWN 1 # define SSL_RECEIVED_SHUTDOWN 2 +# define SSL_VERIFY_NONE 0x00 +# define SSL_VERIFY_PEER 0x01 +# define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 +# define SSL_VERIFY_CLIENT_ONCE 0x04 + /* * The following 3 states are kept in ssl->rlayer.rstate when reads fail, you * should not need these diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index 2893db1888..244eec38dd 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -22,7 +22,6 @@ set_fd, get_fd, \ set_bufflen, \ get_verify_result, \ - ssl_reload_crt, \ get_state) \ static const SSL_METHOD_FUNC func_name LOCAL_ATRR = { \ new, \ @@ -37,7 +36,6 @@ get_fd, \ set_bufflen, \ get_verify_result, \ - ssl_reload_crt, \ get_state \ }; diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 47e6b0bf65..6da6076148 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -171,6 +171,8 @@ struct ssl_ctx_st int verify_mode; + int (*default_verify_callback) (int ok, X509_STORE_CTX *ctx); + long session_timeout; int read_ahead; @@ -209,6 +211,10 @@ struct ssl_st SSL_SESSION session; + int verify_mode; + + int (*verify_callback) (int ok, X509_STORE_CTX *ctx); + int rwstate; long verify_result; @@ -259,8 +265,6 @@ struct ssl_method_func_st { long (*ssl_get_verify_result)(const SSL *ssl); - int (*ssl_reload_crt)(SSL *ssl); - OSSL_HANDSHAKE_STATE (*ssl_get_state)(const SSL *ssl); }; diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index 53bff0d80e..3f64a4ae32 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -51,6 +51,4 @@ void pkey_pm_unload(EVP_PKEY *pkey); long ssl_pm_get_verify_result(const SSL *ssl); -int ssl_pm_reload_crt(SSL *ssl); - #endif diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index cc218f9a26..ae517b0a40 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -284,6 +284,7 @@ SSL *SSL_new(SSL_CTX *ctx) ssl->cert = ctx->cert; ssl->client_CA = ctx->client_CA; + ssl->verify_mode = ctx->verify_mode; ret = SSL_METHOD_CALL(new, ssl); if (ret) @@ -1726,21 +1727,6 @@ long SSL_set_timeout(SSL *ssl, long t) return t; } -/* - * SSL_set_verify - set the SSL verifying of the SSL context - * - * @param ctx - SSL point - * @param mode - verifying mode - * @param verify_callback - verifying callback function - * - * @return none - */ -void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) -{ - SSL_ASSERT(ssl); - SSL_ASSERT(verify_callback); -} - /* * SSL_get_verify_result - get the verifying result of the SSL certification * @@ -1812,3 +1798,37 @@ void SSL_set_verify_depth(SSL *ssl, int depth) ssl->param.depth = depth; } + +/* + * SSL_CTX_set_verify - set the SSL context verifying of the SSL context + * + * @param ctx - SSL context point + * @param mode - verifying mode + * @param verify_callback - verifying callback function + * + * @return none + */ +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) +{ + SSL_ASSERT(ctx); + + ctx->verify_mode = mode; + ctx->default_verify_callback = verify_callback; +} + +/* + * SSL_set_verify - set the SSL verifying of the SSL context + * + * @param ctx - SSL point + * @param mode - verifying mode + * @param verify_callback - verifying callback function + * + * @return none + */ +void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) +{ + SSL_ASSERT(ctx); + + ssl->verify_mode = mode; + ssl->verify_callback = verify_callback; +} diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index 0674f40587..c6fb40e59c 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -26,7 +26,6 @@ IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, ssl_pm_set_fd, ssl_pm_get_fd, ssl_pm_set_bufflen, ssl_pm_get_verify_result, - ssl_pm_reload_crt, ssl_pm_get_state); /* diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index ab56fe789d..e13870344f 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -160,13 +160,7 @@ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) ssl->cert->pkey = pkey; - ssl_ret = SSL_METHOD_CALL(reload_crt, ssl); - if (ssl_ret) - ret = 0; - else - ret = 1; - - return ret; + return 1; } /* diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 431f03caa9..6eb3c1d461 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -138,9 +138,6 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) */ int SSL_add_client_CA(SSL *ssl, X509 *x) { - int ret; - int ssl_ret; - SSL_ASSERT(ssl); SSL_ASSERT(x); @@ -151,13 +148,7 @@ int SSL_add_client_CA(SSL *ssl, X509 *x) ssl->client_CA = x; - ssl_ret = SSL_METHOD_CALL(reload_crt, ssl); - if (ssl_ret) - ret = 0; - else - ret = 1; - - return ret; + return 1; } /* diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 10b736aa9c..cd29882dfa 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -71,14 +71,10 @@ int ssl_pm_new(SSL *ssl) size_t pers_len = sizeof(pers); int endpoint; - int mode; int version; const SSL_METHOD *method = ssl->method; - struct x509_pm *x509_pm; - struct pkey_pm *pkey_pm; - ssl->session.peer = ssl_zalloc(sizeof(X509)); if (!ssl->session.peer) SSL_ERR(ret, failed1, "ssl_zalloc\n"); @@ -123,28 +119,9 @@ int ssl_pm_new(SSL *ssl) mbedtls_ssl_conf_dbg(&ssl_pm->conf, NULL, NULL); - x509_pm = (struct x509_pm *)ssl->client_CA->x509_pm; - if (x509_pm->load) { - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &x509_pm->x509_crt, NULL); - - mode = MBEDTLS_SSL_VERIFY_REQUIRED; - } else { - mode = MBEDTLS_SSL_VERIFY_NONE; - } - mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); - - pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; - if (pkey_pm->load) { - x509_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; - - ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); - if (ret) - SSL_ERR(ret, failed4, "mbedtls_ssl_conf_own_cert:[%d]\n", ret); - } - ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); if (ret) - SSL_ERR(ret, failed5, "mbedtls_ssl_setup:[-0x%x]\n", -ret); + SSL_ERR(ret, failed4, "mbedtls_ssl_setup:[-0x%x]\n", -ret); mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, mbedtls_net_send, mbedtls_net_recv, NULL); @@ -152,9 +129,8 @@ int ssl_pm_new(SSL *ssl) return 0; -failed5: - mbedtls_ssl_config_free(&ssl_pm->conf); failed4: + mbedtls_ssl_config_free(&ssl_pm->conf); mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); failed3: mbedtls_entropy_free(&ssl_pm->entropy); @@ -177,11 +153,49 @@ void ssl_pm_free(SSL *ssl) ssl->ssl_pm = NULL; } +static int ssl_pm_reload_crt(SSL *ssl) +{ + int ret; + int mode; + struct ssl_pm *ssl_pm = ssl->ssl_pm; + struct x509_pm *ca_pm = (struct x509_pm *)ssl->client_CA->x509_pm; + + struct pkey_pm *pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; + struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; + + if (ssl->verify_mode == SSL_VERIFY_PEER) + mode = MBEDTLS_SSL_VERIFY_REQUIRED; + else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) + mode = MBEDTLS_SSL_VERIFY_NONE; + else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE) + mode = MBEDTLS_SSL_VERIFY_UNSET; + else + mode = MBEDTLS_SSL_VERIFY_NONE; + + mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); + + if (ca_pm->load) { + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &ca_pm->x509_crt, NULL); + } + + if (pkey_pm->load) { + ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &crt_pm->x509_crt, &pkey_pm->pkey); + if (ret) + return -1; + } + + return 0; +} + int ssl_pm_handshake(SSL *ssl) { int ret, mbed_ret; struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; + mbed_ret = ssl_pm_reload_crt(ssl); + if (mbed_ret) + return 0; + ssl_speed_up_enter(); while((mbed_ret = mbedtls_ssl_handshake(&ssl_pm->ssl)) != 0) { if (mbed_ret != MBEDTLS_ERR_SSL_WANT_READ && mbed_ret != MBEDTLS_ERR_SSL_WANT_WRITE) { @@ -475,33 +489,3 @@ long ssl_pm_get_verify_result(const SSL *ssl) return verify_result; } - -int ssl_pm_reload_crt(SSL *ssl) -{ - int ret; - int mode; - struct ssl_pm *ssl_pm = ssl->ssl_pm; - struct x509_pm *x509_pm; - struct pkey_pm *pkey_pm; - - x509_pm = (struct x509_pm *)ssl->client_CA->x509_pm; - if (x509_pm->load) { - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &x509_pm->x509_crt, NULL); - - mode = MBEDTLS_SSL_VERIFY_REQUIRED; - } else { - mode = MBEDTLS_SSL_VERIFY_NONE; - } - mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); - - pkey_pm = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; - if (pkey_pm->load) { - x509_pm = (struct x509_pm *)ssl->cert->x509->x509_pm; - - ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &x509_pm->x509_crt, &pkey_pm->pkey); - if (ret) - return -1; - } - - return 0; -} From db9becfa744b651080c435eefe48adc0cf24cdba Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 13:38:11 +0800 Subject: [PATCH 021/285] components/openssl: free peer cert X509 object when SSL_free --- components/openssl/platform/ssl_pm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index cd29882dfa..b03aee3e37 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -149,6 +149,9 @@ void ssl_pm_free(SSL *ssl) mbedtls_ssl_config_free(&ssl_pm->conf); mbedtls_ssl_free(&ssl_pm->ssl); + ssl_free(ssl->session.peer); + ssl->session.peer = NULL; + ssl_free(ssl_pm); ssl->ssl_pm = NULL; } From 59bb9a9a011050c2a9af55c6da2f37a22337b527 Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 14:50:27 +0800 Subject: [PATCH 022/285] components/openssl: [TW7411] supply doxygen type note --- .../openssl/include/internal/ssl_cert.h | 17 +- .../openssl/include/internal/ssl_code.h | 1 + .../openssl/include/internal/ssl_methods.h | 18 + .../openssl/include/internal/ssl_pkey.h | 25 + .../openssl/include/internal/ssl_x509.h | 25 +- components/openssl/include/openssl/ssl.h | 953 +++++++++--------- components/openssl/library/ssl_cert.c | 24 +- components/openssl/library/ssl_lib.c | 677 +++---------- components/openssl/library/ssl_methods.c | 16 +- components/openssl/library/ssl_pkey.c | 113 +-- components/openssl/library/ssl_x509.c | 136 +-- components/openssl/platform/ssl_pm.c | 9 + 12 files changed, 782 insertions(+), 1232 deletions(-) diff --git a/components/openssl/include/internal/ssl_cert.h b/components/openssl/include/internal/ssl_cert.h index 109012a194..b0bd09d480 100644 --- a/components/openssl/include/internal/ssl_cert.h +++ b/components/openssl/include/internal/ssl_cert.h @@ -17,7 +17,22 @@ #include "ssl_types.h" +/** + * @brief create a certification object include private key object + * + * @param none + * + * @return certification object point + */ CERT* ssl_cert_new(void); -void ssl_cert_free(CERT *c); + +/** + * @brief free a certification object + * + * @param cert - certification object point + * + * @return none + */ +void ssl_cert_free(CERT *cert); #endif diff --git a/components/openssl/include/internal/ssl_code.h b/components/openssl/include/internal/ssl_code.h index e76b35abe9..34107d432d 100644 --- a/components/openssl/include/internal/ssl_code.h +++ b/components/openssl/include/internal/ssl_code.h @@ -72,6 +72,7 @@ typedef enum { MSG_FLOW_FINISHED } MSG_FLOW_STATE; +/* SSL subsystem states */ typedef enum { TLS_ST_BEFORE, TLS_ST_OK, diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index 244eec38dd..68737b4381 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -15,6 +15,9 @@ #ifndef _SSL_METHODS_H_ #define _SSL_METHODS_H_ +/** + * TLS method function implement + */ #define IMPLEMENT_TLS_METHOD_FUNC(func_name, \ new, free, \ handshake, shutdown, clear, \ @@ -89,7 +92,22 @@ return &func_name##_data; \ } +/** + * @brief get X509 object method + * + * @param none + * + * @return X509 object method point + */ const X509_METHOD* X509_method(void); + +/** + * @brief get private key object method + * + * @param none + * + * @return private key object method point + */ const PKEY_METHOD* EVP_PKEY_method(void); #endif diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h index 34be294efe..d9a22ee02c 100644 --- a/components/openssl/include/internal/ssl_pkey.h +++ b/components/openssl/include/internal/ssl_pkey.h @@ -17,13 +17,38 @@ #include "ssl_types.h" +/** + * @brief create a private key object + * + * @param none + * + * @return private key object point + */ EVP_PKEY* EVP_PKEY_new(void); +/** + * @brief load a character key context into system context. If '*a' is pointed to the + * private key, then load key into it. Or create a new private key object + * + * @param type - private key type + * @param a - a point pointed to a private key point + * @param pp - a point pointed to the key context memory point + * @param length - key bytes + * + * @return private key object point + */ EVP_PKEY* d2i_PrivateKey(int type, EVP_PKEY **a, const unsigned char **pp, long length); +/** + * @brief free a private key object + * + * @param pkey - private key object point + * + * @return none + */ void EVP_PKEY_free(EVP_PKEY *x); #endif diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index ee3448544b..9359073b69 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -20,17 +20,34 @@ DEFINE_STACK_OF(X509_NAME) -/* - * sk_X509_NAME_new_null - create a X509 certification object +/** + * @brief create a X509 certification object * * @param none * - * @return X509 certification object point or NULL if failed + * @return X509 certification object point */ X509* X509_new(void); +/** + * @brief load a character certification context into system context. If '*cert' is pointed to the + * certification, then load certification into it. Or create a new X509 certification object + * + * @param cert - a point pointed to X509 certification + * @param buffer - a point pointed to the certification context memory point + * @param length - certification bytes + * + * @return X509 certification object point + */ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); -void X509_free(X509 *cert); +/** + * @brief free a X509 certification object + * + * @param x - X509 certification object point + * + * @return none + */ +void X509_free(X509 *x); #endif diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 865405d868..3f92a68d70 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -22,17 +22,17 @@ { */ -/* - * SSL_CTX_new - create a SSL context +/** + * @brief create a SSL context * * @param method - the SSL context method point * - * @return the context point, if create failed return NULL + * @return the context point */ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); -/* - * SSL_CTX_free - free a SSL context +/** + * @brief free a SSL context * * @param method - the SSL context point * @@ -40,17 +40,17 @@ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); */ void SSL_CTX_free(SSL_CTX *ctx); -/* - * SSL_new - create a SSL +/** + * @brief create a SSL * * @param ctx - the SSL context point * - * @return the SSL point or NULL if failed + * @return the SSL point */ SSL* SSL_new(SSL_CTX *ctx); -/* - * SSL_free - free the SSL +/** + * @brief free the SSL * * @param ssl - the SSL point * @@ -58,58 +58,58 @@ SSL* SSL_new(SSL_CTX *ctx); */ void SSL_free(SSL *ssl); -/* - * SSL_connect - connect to the remote SSL server +/** + * @brief connect to the remote SSL server * * @param ssl - the SSL point * - * @return - * 1 : OK - * -1 : failed + * @return result + * 1 : OK + * -1 : failed */ int SSL_connect(SSL *ssl); -/* - * SSL_accept - accept the remote connection +/** + * @brief accept the remote connection * * @param ssl - the SSL point * - * @return - * 1 : OK - * -1 : failed + * @return result + * 1 : OK + * -1 : failed */ int SSL_accept(SSL *ssl); -/* - * SSL_read - read data from to remote +/** + * @brief read data from to remote * * @param ssl - the SSL point which has been connected * @param buffer - the received data buffer point * @param len - the received data length * - * @return - * > 0 : OK, and return received data bytes - * = 0 : connection is closed - * < 0 : an error catch + * @return result + * > 0 : OK, and return received data bytes + * = 0 : connection is closed + * < 0 : an error catch */ int SSL_read(SSL *ssl, void *buffer, int len); -/* - * SSL_write - send the data to remote +/** + * @brief send the data to remote * * @param ssl - the SSL point which has been connected * @param buffer - the send data buffer point * @param len - the send data length * - * @return - * > 0 : OK, and return sent data bytes - * = 0 : connection is closed - * < 0 : an error catch + * @return result + * > 0 : OK, and return sent data bytes + * = 0 : connection is closed + * < 0 : an error catch */ int SSL_write(SSL *ssl, const void *buffer, int len); -/* - * SSL_get_verify_result - get the verifying result of the SSL certification +/** + * @brief get the verifying result of the SSL certification * * @param ssl - the SSL point * @@ -117,56 +117,56 @@ int SSL_write(SSL *ssl, const void *buffer, int len); */ long SSL_get_verify_result(const SSL *ssl); -/* - * SSL_shutdown - shutdown the connection +/** + * @brief shutdown the connection * * @param ssl - the SSL point * - * @return - * 1 : OK - * 0 : shutdown is not finished - * -1 : an error catch + * @return result + * 1 : OK + * 0 : shutdown is not finished + * -1 : an error catch */ int SSL_shutdown(SSL *ssl); -/* - * SSL_set_fd - bind the socket file description into the SSL +/** + * @brief bind the socket file description into the SSL * * @param ssl - the SSL point * @param fd - socket handle * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_set_fd(SSL *ssl, int fd); -/* - * SSL_CTX_use_PrivateKey - These functions load the private key into the SSL_CTX or SSL object +/** + * @brief These functions load the private key into the SSL_CTX or SSL object * * @param ctx - the SSL context point * @param pkey - private key object point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); -/* - * SSL_CTX_use_PrivateKey - These functions load the certification into the SSL_CTX or SSL object +/** + * @brief These functions load the certification into the SSL_CTX or SSL object * * @param ctx - the SSL context point * @param pkey - certification object point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); -/* - * SSLv23_client_method - create the target SSL context client method +/** + * @brief create the target SSL context client method * * @param none * @@ -174,8 +174,8 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); */ const SSL_METHOD* SSLv23_client_method(void); -/* - * TLSv1_client_method - create the target SSL context client method +/** + * @brief create the target SSL context client method * * @param none * @@ -183,8 +183,8 @@ const SSL_METHOD* SSLv23_client_method(void); */ const SSL_METHOD* TLSv1_client_method(void); -/* - * SSLv3_client_method - create the target SSL context client method +/** + * @brief create the target SSL context client method * * @param none * @@ -192,8 +192,8 @@ const SSL_METHOD* TLSv1_client_method(void); */ const SSL_METHOD* SSLv3_client_method(void); -/* - * TLSv1_1_client_method - create the target SSL context client method +/** + * @brief create the target SSL context client method * * @param none * @@ -201,8 +201,8 @@ const SSL_METHOD* SSLv3_client_method(void); */ const SSL_METHOD* TLSv1_1_client_method(void); -/* - * TLSv1_1_client_method - create the target SSL context client method +/** + * @brief create the target SSL context client method * * @param none * @@ -211,8 +211,8 @@ const SSL_METHOD* TLSv1_1_client_method(void); const SSL_METHOD* TLSv1_2_client_method(void); -/* - * SSLv23_server_method - create the target SSL context server method +/** + * @brief create the target SSL context server method * * @param none * @@ -220,8 +220,8 @@ const SSL_METHOD* TLSv1_2_client_method(void); */ const SSL_METHOD* SSLv23_server_method(void); -/* - * TLSv1_1_server_method - create the target SSL context server method +/** + * @brief create the target SSL context server method * * @param none * @@ -229,8 +229,8 @@ const SSL_METHOD* SSLv23_server_method(void); */ const SSL_METHOD* TLSv1_1_server_method(void); -/* - * TLSv1_1_server_method - create the target SSL context server method +/** + * @brief create the target SSL context server method * * @param none * @@ -238,8 +238,8 @@ const SSL_METHOD* TLSv1_1_server_method(void); */ const SSL_METHOD* TLSv1_2_server_method(void); -/* - * TLSv1_server_method - create the target SSL context server method +/** + * @brief create the target SSL context server method * * @param none * @@ -247,8 +247,8 @@ const SSL_METHOD* TLSv1_2_server_method(void); */ const SSL_METHOD* TLSv1_server_method(void); -/* - * SSLv3_server_method - create the target SSL context server method +/** + * @brief create the target SSL context server method * * @param none * @@ -256,8 +256,8 @@ const SSL_METHOD* TLSv1_server_method(void); */ const SSL_METHOD* SSLv3_server_method(void); -/* - * SSL_CTX_set_alpn_select_cb - set the SSL context ALPN select callback function +/** + * @brief set the SSL context ALPN select callback function * * @param ctx - SSL context point * @param cb - ALPN select callback function @@ -275,21 +275,21 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, void *arg); -/* - * SSL_CTX_set_alpn_protos - set the SSL context ALPN select protocol +/** + * @brief set the SSL context ALPN select protocol * * @param ctx - SSL context point * @param protos - ALPN protocol name * @param protos_len - ALPN protocol name bytes * - * @return - * 0 : OK - * 1 : failed + * @return result + * 0 : OK + * 1 : failed */ int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, unsigned int protos_len); -/* - * SSL_CTX_set_next_proto_select_cb - set the SSL context next ALPN select callback function +/** + * @brief set the SSL context next ALPN select callback function * * @param ctx - SSL context point * @param cb - ALPN select callback function @@ -306,8 +306,8 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, void *arg), void *arg); -/* - * SSL_get_error - get SSL error code +/** + * @brief get SSL error code * * @param ssl - SSL point * @param ret_code - SSL return code @@ -316,8 +316,8 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, */ int SSL_get_error(const SSL *ssl, int ret_code); -/* - * ERR_clear_error - clear the SSL error code +/** + * @brief clear the SSL error code * * @param none * @@ -325,8 +325,8 @@ int SSL_get_error(const SSL *ssl, int ret_code); */ void ERR_clear_error(void); -/* - * ERR_get_error - get the current SSL error code +/** + * @brief get the current SSL error code * * @param none * @@ -334,8 +334,8 @@ void ERR_clear_error(void); */ int ERR_get_error(void); -/* - * ERR_load_SSL_strings - register the SSL error strings +/** + * @brief register the SSL error strings * * @param none * @@ -343,8 +343,8 @@ int ERR_get_error(void); */ void ERR_load_SSL_strings(void); -/* - * SSL_library_init - initialize the SSL library +/** + * @brief initialize the SSL library * * @param none * @@ -352,9 +352,9 @@ void ERR_load_SSL_strings(void); */ void SSL_library_init(void); -/* - * ERR_error_string - generates a human-readable string representing the error code e - * and store it into the "ret" point memory +/** + * @brief generates a human-readable string representing the error code e + * and store it into the "ret" point memory * * @param e - error code * @param ret - memory point to store the string @@ -363,8 +363,8 @@ void SSL_library_init(void); */ char *ERR_error_string(unsigned long e, char *ret); -/* - * SSL_CTX_set_options - add the SSL context option +/** + * @brief add the SSL context option * * @param ctx - SSL context point * @param opt - new SSL context option @@ -373,15 +373,15 @@ char *ERR_error_string(unsigned long e, char *ret); */ unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt); -/* - * SSL_CTX_set_options - add the SSL context mode +/** + * @brief add the SSL context mode * * @param ctx - SSL context point * @param mod - new SSL context mod * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_mode(SSL_CTX *ctx, int mod); @@ -389,20 +389,20 @@ int SSL_CTX_set_mode(SSL_CTX *ctx, int mod); } */ -/* - * SSL_do_handshake - perform the SSL handshake +/** + * @brief perform the SSL handshake * * @param ssl - SSL point * - * @return - * 1 : OK - * 0 : failed - * -1 : a error catch + * @return result + * 1 : OK + * 0 : failed + * -1 : a error catch */ int SSL_do_handshake(SSL *ssl); -/* - * SSL_get_version - get the SSL current version +/** + * @brief get the SSL current version * * @param ssl - SSL point * @@ -410,20 +410,20 @@ int SSL_do_handshake(SSL *ssl); */ const char *SSL_get_version(const SSL *ssl); -/* - * SSL_CTX_set_ssl_version - set the SSL context version +/** + * @brief set the SSL context version * * @param ctx - SSL context point * @param meth - SSL method point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); -/* - * SSL_CTX_get_ssl_method - get the SSL context current method +/** + * @brief get the SSL context current method * * @param ctx - SSL context point * @@ -431,8 +431,8 @@ int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); */ const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); -/* - * SSL_CTX_get_ssl_method - get the SSL current method +/** + * @brief get the SSL current method * * @param ssl - SSL point * @@ -440,44 +440,44 @@ const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); */ const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); -/* - * SSL_set_ssl_method - set the SSL method +/** + * @brief set the SSL method * * @param ssl - SSL point * @param meth - SSL method point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); -/* - * SSL_add_client_CA - add CA client certification into the SSL +/** + * @brief add CA client certification into the SSL * * @param ssl - SSL point * @param x - CA certification point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_add_client_CA(SSL *ssl, X509 *x); -/* - * SSL_add_client_CA - add CA client certification into the SSL context +/** + * @brief add CA client certification into the SSL context * * @param ctx - SSL context point * @param x - CA certification point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); -/* - * SSL_set_client_CA_list - set the SSL CA certification list +/** + * @brief set the SSL CA certification list * * @param ssl - SSL point * @param name_list - CA certification list @@ -486,8 +486,8 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); */ void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list); -/* - * SSL_CTX_set_client_CA_list - set the SSL context CA certification list +/** + * @brief set the SSL context CA certification list * * @param ctx - SSL context point * @param name_list - CA certification list @@ -496,8 +496,8 @@ void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list); */ void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); -/* - * SSL_get_client_CA_list - get the SSL CA certification list +/** + * @briefget the SSL CA certification list * * @param ssl - SSL point * @@ -505,8 +505,8 @@ void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); */ STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl); -/* - * SSL_CTX_get_client_CA_list - get the SSL context CA certification list +/** + * @brief get the SSL context CA certification list * * @param ctx - SSL context point * @@ -514,8 +514,8 @@ STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl); */ STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx); -/* - * SSL_get_certificate - get the SSL certification point +/** + * @brief get the SSL certification point * * @param ssl - SSL point * @@ -523,8 +523,8 @@ STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx); */ X509 *SSL_get_certificate(const SSL *ssl); -/* - * SSL_get_privatekey - get the SSL private key point +/** + * @brief get the SSL private key point * * @param ssl - SSL point * @@ -532,8 +532,8 @@ X509 *SSL_get_certificate(const SSL *ssl); */ EVP_PKEY *SSL_get_privatekey(const SSL *ssl); -/* - * SSL_set_info_callback - set the SSL information callback function +/** + * @brief set the SSL information callback function * * @param ssl - SSL point * @param cb - information callback function @@ -542,8 +542,8 @@ EVP_PKEY *SSL_get_privatekey(const SSL *ssl); */ void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)); -/* - * SSL_get_state - get the SSL state +/** + * @brief get the SSL state * * @param ssl - SSL point * @@ -551,8 +551,8 @@ void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int v */ OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); -/* - * SSL_CTX_set_default_read_buffer_len - set the SSL context read buffer length +/** + * @brief set the SSL context read buffer length * * @param ctx - SSL context point * @param len - read buffer length @@ -561,8 +561,8 @@ OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); */ void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); -/* - * SSL_set_default_read_buffer_len - set the SSL read buffer length +/** + * @brief set the SSL read buffer length * * @param ssl - SSL point * @param len - read buffer length @@ -571,8 +571,8 @@ void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); */ void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); -/* - * SSL_set_security_level - set the SSL security level +/** + * @brief set the SSL security level * * @param ssl - SSL point * @param level - security level @@ -581,8 +581,8 @@ void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); */ void SSL_set_security_level(SSL *ssl, int level); -/* - * SSL_get_security_level - get the SSL security level +/** + * @brief get the SSL security level * * @param ssl - SSL point * @@ -590,8 +590,8 @@ void SSL_set_security_level(SSL *ssl, int level); */ int SSL_get_security_level(const SSL *ssl); -/* - * SSL_CTX_get_verify_mode - get the SSL verifying mode of the SSL context +/** + * @brief get the SSL verifying mode of the SSL context * * @param ctx - SSL context point * @@ -599,8 +599,8 @@ int SSL_get_security_level(const SSL *ssl); */ int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); -/* - * SSL_CTX_get_verify_depth - get the SSL verifying depth of the SSL context +/** + * @brief get the SSL verifying depth of the SSL context * * @param ctx - SSL context point * @@ -608,8 +608,8 @@ int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); */ int SSL_CTX_get_verify_depth(const SSL_CTX *ctx); -/* - * SSL_CTX_set_verify - set the SSL context verifying of the SSL context +/** + * @brief set the SSL context verifying of the SSL context * * @param ctx - SSL context point * @param mode - verifying mode @@ -619,8 +619,8 @@ int SSL_CTX_get_verify_depth(const SSL_CTX *ctx); */ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); -/* - * SSL_set_verify - set the SSL verifying of the SSL context +/** + * @brief set the SSL verifying of the SSL context * * @param ctx - SSL point * @param mode - verifying mode @@ -630,18 +630,18 @@ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509 */ void SSL_set_verify(SSL *s, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); -/* - * SSL_CTX_set_verify_depth - set the SSL verify depth of the SSL context +/** + * @brief set the SSL verify depth of the SSL context * * @param ctx - SSL context point * @param depth - verifying depth * - * @return one + * @return none */ void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); -/* - * verify_callback - certification verifying callback function +/** + * @brief certification verifying callback function * * @param preverify_ok - verifying result * @param x509_ctx - X509 certification point @@ -650,8 +650,8 @@ void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth); */ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx); -/* - * SSL_CTX_set_timeout - set the session timeout time +/** + * @brief set the session timeout time * * @param ctx - SSL context point * @param t - new session timeout time @@ -660,8 +660,8 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx); */ long SSL_CTX_set_timeout(SSL_CTX *ctx, long t); -/* - * SSL_CTX_get_timeout - get the session timeout time +/** + * @brief get the session timeout time * * @param ctx - SSL context point * @@ -669,32 +669,32 @@ long SSL_CTX_set_timeout(SSL_CTX *ctx, long t); */ long SSL_CTX_get_timeout(const SSL_CTX *ctx); -/* - * SSL_CTX_set_cipher_list - set the SSL context cipher through the list string +/** + * @brief set the SSL context cipher through the list string * * @param ctx - SSL context point * @param str - cipher controller list string * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); -/* - * SSL_set_cipher_list - set the SSL cipher through the list string +/** + * @brief set the SSL cipher through the list string * * @param ssl - SSL point * @param str - cipher controller list string * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_set_cipher_list(SSL *ssl, const char *str); -/* - * SSL_get_cipher_list - get the SSL cipher list string +/** + * @brief get the SSL cipher list string * * @param ssl - SSL point * @@ -702,8 +702,8 @@ int SSL_set_cipher_list(SSL *ssl, const char *str); */ const char *SSL_get_cipher_list(const SSL *ssl, int n); -/* - * SSL_get_current_cipher - get the SSL cipher +/** + * @brief get the SSL cipher * * @param ssl - SSL point * @@ -711,8 +711,8 @@ const char *SSL_get_cipher_list(const SSL *ssl, int n); */ const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl); -/* - * SSL_get_cipher - get the SSL cipher string +/** + * @brief get the SSL cipher string * * @param ssl - SSL point * @@ -720,8 +720,8 @@ const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl); */ const char *SSL_get_cipher(const SSL *ssl); -/* - * SSL_CTX_get_cert_store - get the SSL context object X509 certification storage +/** + * @brief get the SSL context object X509 certification storage * * @param ctx - SSL context point * @@ -729,8 +729,8 @@ const char *SSL_get_cipher(const SSL *ssl); */ X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx); -/* - * SSL_CTX_set_cert_store - set the SSL context object X509 certification store +/** + * @brief set the SSL context object X509 certification store * * @param ctx - SSL context point * @param store - X509 certification store @@ -739,8 +739,8 @@ X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx); */ void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store); -/* - * SSL_want - get the SSL specifical statement +/** + * @brief get the SSL specifical statement * * @param ssl - SSL point * @@ -748,63 +748,63 @@ void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store); */ int SSL_want(const SSL *ssl); -/* - * SSL_want_x509_lookup - check if the SSL is SSL_X509_LOOKUP state +/** + * @brief check if the SSL is SSL_X509_LOOKUP state * * @param ssl - SSL point * - * @return - * 1 : yes - * 0 : no + * @return result + * 1 : OK + * 0 : failed */ int SSL_want_x509_lookup(const SSL *ssl); -/* - * SSL_clear - reset the SSL +/** + * @brief reset the SSL * * @param ssl - SSL point * - * @return - * 1 : yes - * 0 : no + * @return result + * 1 : OK + * 0 : failed */ int SSL_clear(SSL *ssl); -/* - * SSL_get_fd - get the socket handle of the SSL +/** + * @brief get the socket handle of the SSL * * @param ssl - SSL point * - * @return - * >= 0 : yes, and return socket handle - * < 0 : a error catch + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch */ int SSL_get_fd(const SSL *ssl); -/* - * SSL_get_rfd - get the read only socket handle of the SSL +/** + * @brief get the read only socket handle of the SSL * * @param ssl - SSL point * - * @return - * >= 0 : yes, and return socket handle - * < 0 : a error catch + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch */ int SSL_get_rfd(const SSL *ssl); -/* - * SSL_get_wfd - get the write only socket handle of the SSL +/** + * @brief get the write only socket handle of the SSL * * @param ssl - SSL point * - * @return - * >= 0 : yes, and return socket handle - * < 0 : a error catch + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch */ int SSL_get_wfd(const SSL *ssl); -/* - * SSL_set_read_ahead - set the SSL if we can read as many as data +/** + * @brief set the SSL if we can read as many as data * * @param ssl - SSL point * @param yes - enable the function @@ -813,8 +813,8 @@ int SSL_get_wfd(const SSL *ssl); */ void SSL_set_read_ahead(SSL *s, int yes); -/* - * SSL_set_read_ahead - set the SSL context if we can read as many as data +/** + * @brief set the SSL context if we can read as many as data * * @param ctx - SSL context point * @param yes - enbale the function @@ -823,8 +823,8 @@ void SSL_set_read_ahead(SSL *s, int yes); */ void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes); -/* - * SSL_set_read_ahead - get the SSL ahead signal if we can read as many as data +/** + * @brief get the SSL ahead signal if we can read as many as data * * @param ssl - SSL point * @@ -832,8 +832,8 @@ void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes); */ int SSL_get_read_ahead(const SSL *ssl); -/* - * SSL_set_read_ahead - get the SSL context ahead signal if we can read as many as data +/** + * @brief get the SSL context ahead signal if we can read as many as data * * @param ctx - SSL context point * @@ -841,8 +841,8 @@ int SSL_get_read_ahead(const SSL *ssl); */ long SSL_CTX_get_read_ahead(SSL_CTX *ctx); -/* - * SSL_has_pending - check if some data can be read +/** + * @brief check if some data can be read * * @param ssl - SSL point * @@ -852,160 +852,160 @@ long SSL_CTX_get_read_ahead(SSL_CTX *ctx); */ int SSL_has_pending(const SSL *ssl); -/* - * SSL_CTX_use_certificate - load the X509 certification into SSL context +/** + * @brief load the X509 certification into SSL context * * @param ctx - SSL context point * @param x - X509 certification point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x);//loads the certificate x into ctx -/* - * SSL_CTX_use_certificate_ASN1 - load the ASN1 certification into SSL context +/** + * @brief load the ASN1 certification into SSL context * * @param ctx - SSL context point * @param len - certification length * @param d - data point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); -/* - * SSL_CTX_use_certificate_file - load the certification file into SSL context +/** + * @brief load the certification file into SSL context * * @param ctx - SSL context point * @param file - certification file name * @param type - certification encoding type * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); -/* - * SSL_CTX_use_certificate_chain_file - load the certification chain file into SSL context +/** + * @brief load the certification chain file into SSL context * * @param ctx - SSL context point * @param file - certification chain file name * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); -/* - * SSL_CTX_use_certificate_ASN1 - load the ASN1 private key into SSL context +/** + * @brief load the ASN1 private key into SSL context * * @param ctx - SSL context point * @param d - data point * @param len - private key length * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len);//adds the private key of type pk stored at memory location d (length len) to ctx -/* - * SSL_CTX_use_certificate_file - load the private key file into SSL context +/** + * @brief load the private key file into SSL context * * @param ctx - SSL context point * @param file - private key file name * @param type - private key encoding type * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); -/* - * SSL_CTX_use_certificate - load the RSA private key into SSL context +/** + * @brief load the RSA private key into SSL context * * @param ctx - SSL context point * @param x - RSA private key point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); -/* - * SSL_CTX_use_certificate_ASN1 - load the RSA ASN1 private key into SSL context +/** + * @brief load the RSA ASN1 private key into SSL context * * @param ctx - SSL context point * @param d - data point * @param len - RSA private key length * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); -/* - * SSL_CTX_use_certificate_file - load the RSA private key file into SSL context +/** + * @brief load the RSA private key file into SSL context * * @param ctx - SSL context point * @param file - RSA private key file name * @param type - private key encoding type * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type); -/* - * SSL_CTX_check_private_key - check if the private key and certification is matched +/** + * @brief check if the private key and certification is matched * * @param ctx - SSL context point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_check_private_key(const SSL_CTX *ctx); -/* - * SSL_CTX_use_serverinfo - set the SSL context server information +/** + * @brief set the SSL context server information * * @param ctx - SSL context point * @param serverinfo - server information string * @param serverinfo_length - server information length * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_serverinfo(SSL_CTX *ctx, const unsigned char *serverinfo, size_t serverinfo_length); -/* - * SSL_CTX_use_serverinfo - load the SSL context server infomation file into SSL context +/** + * @brief load the SSL context server infomation file into SSL context * * @param ctx - SSL context point * @param file - server information file * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); -/* - * SSL_select_next_proto - SSL select next function +/** + * @brief SSL select next function * * @param out - point of output data point * @param outlen - output data length @@ -1014,7 +1014,7 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file); * @param client - client data point * @param client_len -client data length * - * @return + * @return NPN state * OPENSSL_NPN_UNSUPPORTED : not support * OPENSSL_NPN_NEGOTIATED : negotiated * OPENSSL_NPN_NO_OVERLAP : no overlap @@ -1023,34 +1023,34 @@ int SSL_select_next_proto(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, const unsigned char *client, unsigned int client_len); -/* - * SSL_CTX_add_extra_chain_cert - load the extra certification chain into the SSL context +/** + * @brief load the extra certification chain into the SSL context * * @param ctx - SSL context point * @param x509 - X509 certification * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ long SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *); -/* - * SSL_CTX_ctrl - control the SSL context +/** + * @brief control the SSL context * * @param ctx - SSL context point * @param cmd - command * @param larg - parameter length * @param parg - parameter point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, char *parg); -/* - * SSL_CTX_get_ciphers - get the SSL context cipher +/** + * @brief get the SSL context cipher * * @param ctx - SSL context point * @@ -1058,19 +1058,19 @@ long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, char *parg); */ STACK *SSL_CTX_get_ciphers(const SSL_CTX *ctx); -/* - * SSL_CTX_get_ciphers - check if the SSL context can read as many as data +/** + * @brief check if the SSL context can read as many as data * * @param ctx - SSL context point * - * @return - * 1 : Yes - * 0 : No + * @return result + * 1 : OK + * 0 : failed */ long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx); -/* - * SSL_CTX_get_ex_data - get the SSL context extra data +/** + * @brief get the SSL context extra data * * @param ctx - SSL context point * @param idx - index @@ -1079,8 +1079,8 @@ long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx); */ char *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx); -/* - * SSL_CTX_get_quiet_shutdown - get the SSL context quiet shutdown option +/** + * @brief get the SSL context quiet shutdown option * * @param ctx - SSL context point * @@ -1088,44 +1088,44 @@ char *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx); */ int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx); -/* - * SSL_CTX_get_quiet_shutdown - load the SSL context CA file +/** + * @brief load the SSL context CA file * * @param ctx - SSL context point * @param CAfile - CA certification file * @param CApath - CA certification file path * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath); -/* - * SSL_CTX_up_ref - add SSL context reference count by '1' +/** + * @brief add SSL context reference count by '1' * * @param ctx - SSL context point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_up_ref(SSL_CTX *ctx); -/* - * SSL_CTX_set_app_data - set SSL context application private data +/** + * @brief set SSL context application private data * * @param ctx - SSL context point * @param arg - private data * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg); -/* - * SSL_CTX_set_client_cert_cb - set SSL context client certification callback function +/** + * @brief set SSL context client certification callback function * * @param ctx - SSL context point * @param cb - callback function @@ -1134,8 +1134,8 @@ int SSL_CTX_set_app_data(SSL_CTX *ctx, void *arg); */ void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey)); -/* - * SSL_CTX_set_default_read_ahead - set the SSL context if we can read as many as data +/** + * @brief set the SSL context if we can read as many as data * * @param ctx - SSL context point * @param m - enable the fuction @@ -1144,54 +1144,54 @@ void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, X509 **x509, E */ void SSL_CTX_set_default_read_ahead(SSL_CTX *ctx, int m); -/* - * SSL_CTX_set_default_verify_paths - set SSL context default verifying path +/** + * @brief set SSL context default verifying path * * @param ctx - SSL context point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); -/* - * SSL_CTX_set_default_verify_paths - set SSL context default verifying directory +/** + * @brief set SSL context default verifying directory * * @param ctx - SSL context point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_default_verify_dir(SSL_CTX *ctx); -/* - * SSL_CTX_set_default_verify_paths - set SSL context default verifying file +/** + * @brief set SSL context default verifying file * * @param ctx - SSL context point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_default_verify_file(SSL_CTX *ctx); -/* - * SSL_CTX_set_ex_data - set SSL context extra data +/** + * @brief set SSL context extra data * * @param ctx - SSL context point * @param idx - data index * @param arg - data point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, char *arg); -/* - * SSL_CTX_clear_options - clear the SSL context option bit of "op" +/** + * @brief clear the SSL context option bit of "op" * * @param ctx - SSL context point * @param op - option @@ -1200,8 +1200,8 @@ int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, char *arg); */ unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op); -/* - * SSL_CTX_clear_options - get the SSL context option +/** + * @brief get the SSL context option * * @param ctx - SSL context point * @param op - option @@ -1210,8 +1210,8 @@ unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op); */ unsigned long SSL_CTX_get_options(SSL_CTX *ctx); -/* - * SSL_CTX_set_quiet_shutdown - set the SSL context quiet shutdown mode +/** + * @brief set the SSL context quiet shutdown mode * * @param ctx - SSL context point * @param mode - mode @@ -1220,8 +1220,8 @@ unsigned long SSL_CTX_get_options(SSL_CTX *ctx); */ void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode); -/* - * SSL_CTX_get0_certificate - get the SSL context X509 certification +/** + * @brief get the SSL context X509 certification * * @param ctx - SSL context point * @@ -1229,8 +1229,8 @@ void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode); */ X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx); -/* - * SSL_CTX_get0_certificate - get the SSL context private key +/** + * @brief get the SSL context private key * * @param ctx - SSL context point * @@ -1238,32 +1238,33 @@ X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx); */ EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx); -/* - * SSL_CTX_use_psk_identity_hint - set SSL context PSK identity hint +/** + * @brief set SSL context PSK identity hint * * @param ctx - SSL context point * @param hint - PSK identity hint * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint); -/* - * SSL_CTX_set_psk_server_callback - set SSL context PSK server callback function +/** + * @brief set SSL context PSK server callback function * * @param ctx - SSL context point * @param callback - callback function * + * @return none */ void SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, unsigned int (*callback)(SSL *ssl, const char *identity, unsigned char *psk, int max_psk_len)); -/* - * SSL_alert_desc_string - get alert description string +/** + * @brief get alert description string * * @param value - alert value * @@ -1271,8 +1272,8 @@ void SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, */ const char *SSL_alert_desc_string(int value); -/* - * SSL_alert_desc_string - get alert description long string +/** + * @brief get alert description long string * * @param value - alert value * @@ -1280,8 +1281,8 @@ const char *SSL_alert_desc_string(int value); */ const char *SSL_alert_desc_string_long(int value); -/* - * SSL_alert_type_string - get alert type string +/** + * @brief get alert type string * * @param value - alert value * @@ -1289,8 +1290,8 @@ const char *SSL_alert_desc_string_long(int value); */ const char *SSL_alert_type_string(int value); -/* - * SSL_alert_type_string_long - get alert type long string +/** + * @brief get alert type long string * * @param value - alert value * @@ -1298,8 +1299,8 @@ const char *SSL_alert_type_string(int value); */ const char *SSL_alert_type_string_long(int value); -/* - * SSL_get_SSL_CTX - get SSL context of the SSL +/** + * @brief get SSL context of the SSL * * @param ssl - SSL point * @@ -1307,8 +1308,8 @@ const char *SSL_alert_type_string_long(int value); */ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); -/* - * SSL_get_app_data - get SSL application data +/** + * @brief get SSL application data * * @param ssl - SSL point * @@ -1316,8 +1317,8 @@ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); */ char *SSL_get_app_data(SSL *ssl); -/* - * SSL_get_cipher_bits - get SSL cipher bits +/** + * @brief get SSL cipher bits * * @param ssl - SSL point * @param alg_bits - algorithm bits @@ -1326,8 +1327,8 @@ char *SSL_get_app_data(SSL *ssl); */ int SSL_get_cipher_bits(const SSL *ssl, int *alg_bits); -/* - * SSL_get_cipher_name - get SSL cipher name +/** + * @brief get SSL cipher name * * @param ssl - SSL point * @@ -1335,8 +1336,8 @@ int SSL_get_cipher_bits(const SSL *ssl, int *alg_bits); */ char *SSL_get_cipher_name(const SSL *ssl); -/* - * SSL_get_cipher_version - get SSL cipher version +/** + * @brief get SSL cipher version * * @param ssl - SSL point * @@ -1344,8 +1345,8 @@ char *SSL_get_cipher_name(const SSL *ssl); */ char *SSL_get_cipher_version(const SSL *ssl); -/* - * SSL_get_ex_data - get SSL extra data +/** + * @brief get SSL extra data * * @param ssl - SSL point * @param idx - data index @@ -1354,8 +1355,8 @@ char *SSL_get_cipher_version(const SSL *ssl); */ char *SSL_get_ex_data(const SSL *ssl, int idx); -/* - * SSL_get_ex_data_X509_STORE_CTX_idx - get index of the SSL extra data X509 storage context +/** + * @brief get index of the SSL extra data X509 storage context * * @param none * @@ -1363,8 +1364,8 @@ char *SSL_get_ex_data(const SSL *ssl, int idx); */ int SSL_get_ex_data_X509_STORE_CTX_idx(void); -/* - * SSL_get_peer_cert_chain - get peer certification chain +/** + * @brief get peer certification chain * * @param ssl - SSL point * @@ -1372,8 +1373,8 @@ int SSL_get_ex_data_X509_STORE_CTX_idx(void); */ STACK *SSL_get_peer_cert_chain(const SSL *ssl); -/* - * SSL_get_peer_certificate - get peer certification +/** + * @brief get peer certification * * @param ssl - SSL point * @@ -1381,8 +1382,8 @@ STACK *SSL_get_peer_cert_chain(const SSL *ssl); */ X509 *SSL_get_peer_certificate(const SSL *ssl); -/* - * SSL_get_quiet_shutdown - get SSL quiet shutdown mode +/** + * @brief get SSL quiet shutdown mode * * @param ssl - SSL point * @@ -1390,8 +1391,8 @@ X509 *SSL_get_peer_certificate(const SSL *ssl); */ int SSL_get_quiet_shutdown(const SSL *ssl); -/* - * SSL_get_rbio - get SSL read only IO handle +/** + * @brief get SSL read only IO handle * * @param ssl - SSL point * @@ -1399,19 +1400,19 @@ int SSL_get_quiet_shutdown(const SSL *ssl); */ BIO *SSL_get_rbio(const SSL *ssl); -/* - * SSL_get_shared_ciphers - get SSL shared ciphers +/** + * @brief get SSL shared ciphers * * @param ssl - SSL point * @param buf - buffer to store the ciphers * @param len - buffer len * - * @return shared ciphers or NULL if failed + * @return shared ciphers */ char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len); -/* - * SSL_get_shutdown - get SSL shutdown mode +/** + * @brief get SSL shutdown mode * * @param ssl - SSL point * @@ -1419,8 +1420,8 @@ char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len); */ int SSL_get_shutdown(const SSL *ssl); -/* - * SSL_get_time - get SSL session time +/** + * @brief get SSL session time * * @param ssl - SSL point * @@ -1428,8 +1429,8 @@ int SSL_get_shutdown(const SSL *ssl); */ long SSL_get_time(const SSL *ssl); -/* - * SSL_get_timeout - get SSL session timeout time +/** + * @brief get SSL session timeout time * * @param ssl - SSL point * @@ -1437,8 +1438,8 @@ long SSL_get_time(const SSL *ssl); */ long SSL_get_timeout(const SSL *ssl); -/* - * SSL_get_verify_mode - get SSL verifying mode +/** + * @brief get SSL verifying mode * * @param ssl - SSL point * @@ -1446,8 +1447,8 @@ long SSL_get_timeout(const SSL *ssl); */ int SSL_get_verify_mode(const SSL *ssl); -/* - * SSL_get_wbio - get SSL write only IO handle +/** + * @brief get SSL write only IO handle * * @param ssl - SSL point * @@ -1455,8 +1456,8 @@ int SSL_get_verify_mode(const SSL *ssl); */ BIO *SSL_get_wbio(const SSL *ssl); -/* - * SSL_load_client_CA_file - load SSL client CA certification file +/** + * @brief load SSL client CA certification file * * @param file - file name * @@ -1464,44 +1465,44 @@ BIO *SSL_get_wbio(const SSL *ssl); */ STACK *SSL_load_client_CA_file(const char *file); -/* - * SSL_up_ref - add SSL reference by '1' +/** + * @brief add SSL reference by '1' * * @param ssl - SSL point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_up_ref(SSL *ssl); -/* - * SSL_peek - read and put data into buf, but not clear the SSL low-level storage +/** + * @brief read and put data into buf, but not clear the SSL low-level storage * * @param ssl - SSL point * @param buf - storage buffer point * @param num - data bytes * - * @return - * > 0 : OK, and return read bytes - * = 0 : connect is closed - * < 0 : a error catch + * @return result + * > 0 : OK, and return read bytes + * = 0 : connect is closed + * < 0 : a error catch */ int SSL_peek(SSL *ssl, void *buf, int num); -/* - * SSL_renegotiate - make SSL renegotiate +/** + * @brief make SSL renegotiate * * @param ssl - SSL point * - * @return - * 1 : OK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_renegotiate(SSL *ssl); -/* - * SSL_rstate_string - get the state string where SSL is reading +/** + * @brief get the state string where SSL is reading * * @param ssl - SSL point * @@ -1509,8 +1510,8 @@ int SSL_renegotiate(SSL *ssl); */ const char *SSL_rstate_string(SSL *ssl); -/* - * SSL_rstate_string_long - get the statement long string where SSL is reading +/** + * @brief get the statement long string where SSL is reading * * @param ssl - SSL point * @@ -1518,8 +1519,8 @@ const char *SSL_rstate_string(SSL *ssl); */ const char *SSL_rstate_string_long(SSL *ssl); -/* - * SSL_set_accept_state - set SSL accept statement +/** + * @brief set SSL accept statement * * @param ssl - SSL point * @@ -1527,8 +1528,8 @@ const char *SSL_rstate_string_long(SSL *ssl); */ void SSL_set_accept_state(SSL *ssl); -/* - * SSL_set_app_data - set SSL application data +/** + * @brief set SSL application data * * @param ssl - SSL point * @param arg - SSL application data point @@ -1537,8 +1538,8 @@ void SSL_set_accept_state(SSL *ssl); */ void SSL_set_app_data(SSL *ssl, char *arg); -/* - * SSL_set_bio - set SSL BIO +/** + * @brief set SSL BIO * * @param ssl - SSL point * @param rbio - read only IO @@ -1548,8 +1549,8 @@ void SSL_set_app_data(SSL *ssl, char *arg); */ void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); -/* - * SSL_clear_options - clear SSL option +/** + * @brief clear SSL option * * @param ssl - SSL point * @param op - clear option @@ -1558,8 +1559,8 @@ void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); */ unsigned long SSL_clear_options(SSL *ssl, unsigned long op); -/* - * SSL_clear_options - get SSL option +/** + * @brief get SSL option * * @param ssl - SSL point * @@ -1567,8 +1568,8 @@ unsigned long SSL_clear_options(SSL *ssl, unsigned long op); */ unsigned long SSL_get_options(SSL *ssl); -/* - * SSL_clear_options - clear SSL option +/** + * @brief clear SSL option * * @param ssl - SSL point * @param op - setting option @@ -1577,8 +1578,8 @@ unsigned long SSL_get_options(SSL *ssl); */ unsigned long SSL_set_options(SSL *ssl, unsigned long op); -/* - * SSL_set_quiet_shutdown - set SSL quiet shutdown mode +/** + * @brief set SSL quiet shutdown mode * * @param ssl - SSL point * @param mode - quiet shutdown mode @@ -1587,8 +1588,8 @@ unsigned long SSL_set_options(SSL *ssl, unsigned long op); */ void SSL_set_quiet_shutdown(SSL *ssl, int mode); -/* - * SSL_set_quiet_shutdown - set SSL shutdown mode +/** + * @brief set SSL shutdown mode * * @param ssl - SSL point * @param mode - shutdown mode @@ -1597,8 +1598,8 @@ void SSL_set_quiet_shutdown(SSL *ssl, int mode); */ void SSL_set_shutdown(SSL *ssl, int mode); -/* - * SSL_set_time - set SSL session time +/** + * @brief set SSL session time * * @param ssl - SSL point * @param t - session time @@ -1607,8 +1608,8 @@ void SSL_set_shutdown(SSL *ssl, int mode); */ void SSL_set_time(SSL *ssl, long t); -/* - * SSL_set_time - set SSL session timeout time +/** + * @brief set SSL session timeout time * * @param ssl - SSL point * @param t - session timeout time @@ -1617,8 +1618,8 @@ void SSL_set_time(SSL *ssl, long t); */ void SSL_set_timeout(SSL *ssl, long t); -/* - * SSL_state_string - get SSL statement string +/** + * @brief get SSL statement string * * @param ssl - SSL point * @@ -1626,8 +1627,8 @@ void SSL_set_timeout(SSL *ssl, long t); */ char *SSL_state_string(const SSL *ssl); -/* - * SSL_state_string_long - get SSL statement long string +/** + * @brief get SSL statement long string * * @param ssl - SSL point * @@ -1635,8 +1636,8 @@ char *SSL_state_string(const SSL *ssl); */ char *SSL_state_string_long(const SSL *ssl); -/* - * SSL_total_renegotiations - get SSL renegotiation count +/** + * @brief get SSL renegotiation count * * @param ssl - SSL point * @@ -1644,8 +1645,8 @@ char *SSL_state_string_long(const SSL *ssl); */ long SSL_total_renegotiations(SSL *ssl); -/* - * SSL_version - get SSL version +/** + * @brief get SSL version * * @param ssl - SSL point * @@ -1653,20 +1654,20 @@ long SSL_total_renegotiations(SSL *ssl); */ int SSL_version(const SSL *ssl); -/* - * SSL_use_psk_identity_hint - set SSL PSK identity hint +/** + * @brief set SSL PSK identity hint * * @param ssl - SSL point * @param hint - identity hint * - * @return - * 1 : oK - * 0 : failed + * @return result + * 1 : OK + * 0 : failed */ int SSL_use_psk_identity_hint(SSL *ssl, const char *hint); -/* - * SSL_get_psk_identity_hint - get SSL PSK identity hint +/** + * @brief get SSL PSK identity hint * * @param ssl - SSL point * @@ -1674,8 +1675,8 @@ int SSL_use_psk_identity_hint(SSL *ssl, const char *hint); */ const char *SSL_get_psk_identity_hint(SSL *ssl); -/* - * SSL_get_psk_identity - get SSL PSK identity +/** + * @brief get SSL PSK identity * * @param ssl - SSL point * diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c index 2d82e62aaa..fd05bc8315 100644 --- a/components/openssl/library/ssl_cert.c +++ b/components/openssl/library/ssl_cert.c @@ -18,12 +18,8 @@ #include "ssl_dbg.h" #include "ssl_port.h" -/* - * ssl_cert_new - create a certification object include private key object - * - * @param none - * - * @return certification object point or NULL if failed +/** + * @brief create a certification object include private key object */ CERT *ssl_cert_new(void) { @@ -51,18 +47,14 @@ failed1: return NULL; } -/* - * ssl_cert_free - free a certification object - * - * @param c - certification object point - * - * @return none +/** + * @brief free a certification object */ -void ssl_cert_free(CERT *c) +void ssl_cert_free(CERT *cert) { - X509_free(c->x509); + X509_free(cert->x509); - EVP_PKEY_free(c->pkey); + EVP_PKEY_free(cert->pkey); - ssl_free(c); + ssl_free(cert); } diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index ae517b0a40..a84b89e06c 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -21,14 +21,8 @@ #define SSL_SEND_DATA_MAX_LENGTH 1460 -/* - * ossl_statem_in_error - Discover whether the current connection is in the error state - * - * @param ssl - SSL point - * - * @return - * 1 : Yes - * 0 : no +/** + * @brief Discover whether the current connection is in the error state */ int ossl_statem_in_error(const SSL *ssl) { @@ -38,81 +32,48 @@ int ossl_statem_in_error(const SSL *ssl) return 0; } -/* - * SSL_want - get the SSL specifical statement - * - * @param ssl - SSL point - * - * @return specifical statement +/** + * @brief get the SSL specifical statement */ int SSL_want(const SSL *ssl) { return ssl->rwstate; } -/* - * SSL_want_nothing - check if SSL want nothing - * - * @param ssl - SSL point - * - * @return - * 1 : yes - * 0 : no +/** + * @brief check if SSL want nothing */ int SSL_want_nothing(const SSL *ssl) { return (SSL_want(ssl) == SSL_NOTHING); } -/* - * SSL_want_read - check if SSL want to read - * - * @param ssl - SSL point - * - * @return - * 1 : yes - * 0 : no +/** + * @brief check if SSL want to read */ int SSL_want_read(const SSL *ssl) { return (SSL_want(ssl) == SSL_READING); } -/* - * SSL_want_read - check if SSL want to write - * - * @param ssl - SSL point - * - * @return - * 1 : yes - * 0 : no +/** + * @brief check if SSL want to write */ int SSL_want_write(const SSL *ssl) { return (SSL_want(ssl) == SSL_WRITING); } -/* - * SSL_want_read - check if SSL want to lookup X509 certification - * - * @param ssl - SSL point - * - * @return - * 1 : yes - * 0 : no +/** + * @brief check if SSL want to lookup X509 certification */ int SSL_want_x509_lookup(const SSL *ssl) { return (SSL_want(ssl) == SSL_WRITING); } -/* - * SSL_get_error - get SSL error code - * - * @param ssl - SSL point - * @param ret_code - SSL return code - * - * @return SSL error number +/** + * @brief get SSL error code */ int SSL_get_error(const SSL *ssl, int ret_code) { @@ -142,12 +103,8 @@ int SSL_get_error(const SSL *ssl, int ret_code) return ret; } -/* - * SSL_get_state - get the SSL state - * - * @param ssl - SSL point - * - * @return SSL state +/** + * @brief get the SSL state */ OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) { @@ -160,12 +117,8 @@ OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) return state; } -/* - * SSL_CTX_new - create a SSL context - * - * @param method - the SSL context configuration file - * - * @return the context point, if create failed return NULL +/** + * @brief create a SSL context */ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) { @@ -203,12 +156,8 @@ go_failed1: return NULL; } -/* - * SSL_CTX_free - free a SSL context - * - * @param method - the SSL context point - * - * @return none +/** + * @brief free a SSL context */ void SSL_CTX_free(SSL_CTX* ctx) { @@ -221,15 +170,8 @@ void SSL_CTX_free(SSL_CTX* ctx) ssl_free(ctx); } -/* - * SSL_CTX_set_ssl_version - set the SSL context version - * - * @param ctx - SSL context point - * @param meth - SSL method point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief set the SSL context version */ int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth) { @@ -243,12 +185,8 @@ int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth) return 1; } -/* - * SSL_CTX_get_ssl_method - get the SSL context current method - * - * @param ctx - SSL context point - * - * @return the SSL context current method +/** + * @brief get the SSL context current method */ const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) { @@ -257,12 +195,8 @@ const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx) return ctx->method; } -/* - * SSL_new - create a SSL - * - * @param ctx - the SSL context point - * - * @return the SSL point or NULL if failed +/** + * @brief create a SSL */ SSL *SSL_new(SSL_CTX *ctx) { @@ -300,12 +234,8 @@ failed1: return NULL; } -/* - * SSL_free - free the SSL - * - * @param ssl - the SSL point - * - * @return none +/** + * @brief free the SSL */ void SSL_free(SSL *ssl) { @@ -322,15 +252,8 @@ void SSL_free(SSL *ssl) ssl_free(ssl); } -/* - * SSL_do_handshake - perform the SSL handshake - * - * @param ssl - SSL point - * - * @return - * 1 : OK - * 0 : failed - * -1 : a error catch +/** + * @brief perform the SSL handshake */ int SSL_do_handshake(SSL *ssl) { @@ -343,14 +266,8 @@ int SSL_do_handshake(SSL *ssl) return ret; } -/* - * SSL_connect - connect to the remote SSL server - * - * @param ssl - the SSL point - * - * @return - * 1 : OK - * -1 : failed +/** + * @brief connect to the remote SSL server */ int SSL_connect(SSL *ssl) { @@ -359,14 +276,8 @@ int SSL_connect(SSL *ssl) return SSL_do_handshake(ssl); } -/* - * SSL_accept - accept the remote connection - * - * @param ssl - the SSL point - * - * @return - * 1 : OK - * -1 : failed +/** + * @brief accept the remote connection */ int SSL_accept(SSL *ssl) { @@ -375,15 +286,8 @@ int SSL_accept(SSL *ssl) return SSL_do_handshake(ssl); } -/* - * SSL_shutdown - shutdown the connection - * - * @param ssl - the SSL point - * - * @return - * 1 : OK - * 0 : shutdown is not finished - * -1 : an error catch +/** + * @brief shutdown the connection */ int SSL_shutdown(SSL *ssl) { @@ -398,14 +302,8 @@ int SSL_shutdown(SSL *ssl) return ret; } -/* - * SSL_clear - reset the SSL - * - * @param ssl - SSL point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief reset the SSL */ int SSL_clear(SSL *ssl) { @@ -429,17 +327,8 @@ go_failed1: return ret; } -/* - * SSL_read - read data from to remote - * - * @param ssl - the SSL point which has been connected - * @param buffer - the received data buffer point - * @param len - the received data length - * - * @return - * > 0 : OK, and return received data bytes - * = 0 : connection is closed - * < 0 : an error catch +/** + * @brief read data from to remote */ int SSL_read(SSL *ssl, void *buffer, int len) { @@ -458,17 +347,8 @@ int SSL_read(SSL *ssl, void *buffer, int len) return ret; } -/* - * SSL_write - send the data to remote - * - * @param ssl - the SSL point which has been connected - * @param buffer - the send data buffer point - * @param len - the send data length - * - * @return - * > 0 : OK, and return sent data bytes - * = 0 : connection is closed - * < 0 : an error catch +/** + * @brief send the data to remote */ int SSL_write(SSL *ssl, const void *buffer, int len) { @@ -511,12 +391,8 @@ int SSL_write(SSL *ssl, const void *buffer, int len) return ret; } -/* - * SSL_get_SSL_CTX - get SSL context of the SSL - * - * @param ssl - SSL point - * - * @return SSL context +/** + * @brief get SSL context of the SSL */ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { @@ -525,12 +401,8 @@ SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) return ssl->ctx; } -/* - * SSL_CTX_get_ssl_method - get the SSL current method - * - * @param ssl - SSL point - * - * @return the SSL current method +/** + * @brief get the SSL current method */ const SSL_METHOD *SSL_get_ssl_method(SSL *ssl) { @@ -539,15 +411,8 @@ const SSL_METHOD *SSL_get_ssl_method(SSL *ssl) return ssl->method; } -/* - * SSL_set_ssl_method - set the SSL method - * - * @param ssl - SSL point - * @param meth - SSL method point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief set the SSL method */ int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method) { @@ -580,12 +445,8 @@ go_failed1: return ret; } -/* - * SSL_get_shutdown - get SSL shutdown mode - * - * @param ssl - SSL point - * - * @return shutdown mode +/** + * @brief get SSL shutdown mode */ int SSL_get_shutdown(const SSL *ssl) { @@ -594,13 +455,8 @@ int SSL_get_shutdown(const SSL *ssl) return ssl->shutdown; } -/* - * SSL_set_quiet_shutdown - set SSL shutdown mode - * - * @param ssl - SSL point - * @param mode - shutdown mode - * - * @return none +/** + * @brief set SSL shutdown mode */ void SSL_set_shutdown(SSL *ssl, int mode) { @@ -610,12 +466,8 @@ void SSL_set_shutdown(SSL *ssl, int mode) } -/* - * SSL_pending - get the number of the bytes to be read - * - * @param ssl - SSL point - * - * @return number of the bytes +/** + * @brief get the number of the bytes to be read */ int SSL_pending(const SSL *ssl) { @@ -628,14 +480,8 @@ int SSL_pending(const SSL *ssl) return ret; } -/* - * SSL_has_pending - check if some data can be read - * - * @param ssl - SSL point - * - * @return - * 1 : there are bytes to be read - * 0 : no data +/** + * @brief check if some data can be read */ int SSL_has_pending(const SSL *ssl) { @@ -651,52 +497,32 @@ int SSL_has_pending(const SSL *ssl) return ret; } -/* - * SSL_CTX_clear_options - clear the SSL context option bit of "op" - * - * @param ctx - SSL context point - * @param op - option - * - * @return SSL context option +/** + * @brief clear the SSL context option bit of "op" */ unsigned long SSL_CTX_clear_options(SSL_CTX *ctx, unsigned long op) { return ctx->options &= ~op; } -/* - * SSL_CTX_clear_options - get the SSL context option - * - * @param ctx - SSL context point - * @param op - option - * - * @return SSL context option +/** + * @brief get the SSL context option */ unsigned long SSL_CTX_get_options(SSL_CTX *ctx) { return ctx->options; } -/* - * SSL_CTX_set_option - set the option of the SSL context - * - * @param ctx - the SSL context - * - * @return the SSL context option - * +/** + * @brief set the option of the SSL context */ unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long opt) { return ctx->options |= opt; } -/* - * SSL_clear_options - clear SSL option - * - * @param ssl - SSL point - * @param op - clear option - * - * @return SSL option +/** + * @brief clear SSL option */ unsigned long SSL_clear_options(SSL *ssl, unsigned long op) { @@ -705,12 +531,8 @@ unsigned long SSL_clear_options(SSL *ssl, unsigned long op) return ssl->options & ~op; } -/* - * SSL_clear_options - get SSL option - * - * @param ssl - SSL point - * - * @return SSL option +/** + * @brief get SSL option */ unsigned long SSL_get_options(SSL *ssl) { @@ -719,13 +541,8 @@ unsigned long SSL_get_options(SSL *ssl) return ssl->options; } -/* - * SSL_clear_options - clear SSL option - * - * @param ssl - SSL point - * @param op - setting option - * - * @return SSL option +/** + * @brief clear SSL option */ unsigned long SSL_set_options(SSL *ssl, unsigned long op) { @@ -734,14 +551,8 @@ unsigned long SSL_set_options(SSL *ssl, unsigned long op) return ssl->options |= op; } -/* - * SSL_get_fd - get the socket handle of the SSL - * - * @param ssl - SSL point - * - * @return - * >= 0 : yes, and return socket handle - * < 0 : a error catch +/** + * @brief get the socket handle of the SSL */ int SSL_get_fd(const SSL *ssl) { @@ -754,14 +565,8 @@ int SSL_get_fd(const SSL *ssl) return ret; } -/* - * SSL_get_rfd - get the read only socket handle of the SSL - * - * @param ssl - SSL point - * - * @return - * >= 0 : yes, and return socket handle - * < 0 : a error catch +/** + * @brief get the read only socket handle of the SSL */ int SSL_get_rfd(const SSL *ssl) { @@ -774,14 +579,8 @@ int SSL_get_rfd(const SSL *ssl) return ret; } -/* - * SSL_get_wfd - get the write only socket handle of the SSL - * - * @param ssl - SSL point - * - * @return - * >= 0 : yes, and return socket handle - * < 0 : a error catch +/** + * @brief get the write only socket handle of the SSL */ int SSL_get_wfd(const SSL *ssl) { @@ -794,15 +593,8 @@ int SSL_get_wfd(const SSL *ssl) return ret; } -/* - * SSL_set_fd - bind the socket file description into the SSL - * - * @param ssl - the SSL point - * @param fd - socket handle - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief bind the socket file description into the SSL */ int SSL_set_fd(SSL *ssl, int fd) { @@ -814,15 +606,8 @@ int SSL_set_fd(SSL *ssl, int fd) return 1; } -/* - * SSL_set_fd - bind the read only socket file description into the SSL - * - * @param ssl - the SSL point - * @param fd - socket handle - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief bind the read only socket file description into the SSL */ int SSL_set_rfd(SSL *ssl, int fd) { @@ -834,15 +619,8 @@ int SSL_set_rfd(SSL *ssl, int fd) return 1; } -/* - * SSL_set_fd - bind the write only socket file description into the SSL - * - * @param ssl - the SSL point - * @param fd - socket handle - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief bind the write only socket file description into the SSL */ int SSL_set_wfd(SSL *ssl, int fd) { @@ -854,12 +632,8 @@ int SSL_set_wfd(SSL *ssl, int fd) return 1; } -/* - * SSL_version - get SSL version - * - * @param ssl - SSL point - * - * @return SSL version +/** + * @brief get SSL version */ int SSL_version(const SSL *ssl) { @@ -868,12 +642,8 @@ int SSL_version(const SSL *ssl) return ssl->version; } -/* - * ssl_protocol_to_string - get the SSL version string - * - * @param version - the SSL version - * - * @return the SSL version string +/** + * @brief get the SSL version string */ static const char* ssl_protocol_to_string(int version) { @@ -893,12 +663,8 @@ static const char* ssl_protocol_to_string(int version) return str; } -/* - * SSL_get_version - get the SSL current version - * - * @param ssl - SSL point - * - * @return the version string +/** + * @brief get the SSL current version */ const char *SSL_get_version(const SSL *ssl) { @@ -907,12 +673,8 @@ const char *SSL_get_version(const SSL *ssl) return ssl_protocol_to_string(SSL_version(ssl)); } -/* - * SSL_alert_desc_string - get alert description string - * - * @param value - alert value - * - * @return alert description string +/** + * @brief get alert description string */ const char* SSL_alert_desc_string(int value) { @@ -1018,12 +780,8 @@ const char* SSL_alert_desc_string(int value) return str; } -/* - * SSL_alert_desc_string - get alert description long string - * - * @param value - alert value - * - * @return alert description long string +/** + * @brief get alert description long string */ const char* SSL_alert_desc_string_long(int value) { @@ -1129,12 +887,8 @@ const char* SSL_alert_desc_string_long(int value) return str; } -/* - * SSL_alert_type_string - get alert type string - * - * @param value - alert value - * - * @return alert type string +/** + * @brief get alert type string */ const char *SSL_alert_type_string(int value) { @@ -1156,12 +910,8 @@ const char *SSL_alert_type_string(int value) return str; } -/* - * SSL_alert_type_string_long - get alert type long string - * - * @param value - alert value - * - * @return alert type long string +/** + * @brief get alert type long string */ const char *SSL_alert_type_string_long(int value) { @@ -1183,12 +933,8 @@ const char *SSL_alert_type_string_long(int value) return str; } -/* - * SSL_rstate_string - get the state string where SSL is reading - * - * @param ssl - SSL point - * - * @return state string +/** + * @brief get the state string where SSL is reading */ const char *SSL_rstate_string(SSL *ssl) { @@ -1215,12 +961,8 @@ const char *SSL_rstate_string(SSL *ssl) return str; } -/* - * SSL_rstate_string_long - get the statement long string where SSL is reading - * - * @param ssl - SSL point - * - * @return statement long string +/** + * @brief get the statement long string where SSL is reading */ const char *SSL_rstate_string_long(SSL *ssl) { @@ -1246,12 +988,8 @@ const char *SSL_rstate_string_long(SSL *ssl) return str; } -/* - * SSL_state_string - get SSL statement string - * - * @param ssl - SSL point - * - * @return SSL statement string +/** + * @brief get SSL statement string */ char *SSL_state_string(const SSL *ssl) { @@ -1358,12 +1096,8 @@ char *SSL_state_string(const SSL *ssl) return str; } -/* - * SSL_state_string_long - get SSL statement long string - * - * @param ssl - SSL point - * - * @return SSL statement long string +/** + * @brief get SSL statement long string */ char *SSL_state_string_long(const SSL *ssl) { @@ -1476,13 +1210,8 @@ char *SSL_state_string_long(const SSL *ssl) return str; } -/* - * SSL_CTX_set_default_read_buffer_len - set the SSL context read buffer length - * - * @param ctx - SSL context point - * @param len - read buffer length - * - * @return none +/** + * @brief set the SSL context read buffer length */ void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len) { @@ -1492,13 +1221,8 @@ void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len) ctx->read_buffer_len = len; } -/* - * SSL_set_default_read_buffer_len - set the SSL read buffer length - * - * @param ssl - SSL point - * @param len - read buffer length - * - * @return none +/** + * @brief set the SSL read buffer length */ void SSL_set_default_read_buffer_len(SSL *ssl, size_t len) { @@ -1508,13 +1232,8 @@ void SSL_set_default_read_buffer_len(SSL *ssl, size_t len) SSL_METHOD_CALL(set_bufflen, ssl, len); } -/* - * SSL_set_info_callback - set the SSL information callback function - * - * @param ssl - SSL point - * @param cb - information callback function - * - * @return none +/** + * @brief set the SSL information callback function */ void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val)) { @@ -1523,32 +1242,23 @@ void SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int v ssl->info_callback = cb; } -/* - * SSL_CTX_up_ref - add SSL context reference count by '1' - * - * @param ctx - SSL context point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief add SSL context reference count by '1' */ int SSL_CTX_up_ref(SSL_CTX *ctx) { SSL_ASSERT(ctx); - /* no support multi-thread SSL here */ + /** + * no support multi-thread SSL here + */ ctx->references++; return 1; } -/* - * SSL_set_security_level - set the SSL security level - * - * @param ssl - SSL point - * @param level - security level - * - * @return none +/** + * @brief set the SSL security level */ void SSL_set_security_level(SSL *ssl, int level) { @@ -1557,12 +1267,8 @@ void SSL_set_security_level(SSL *ssl, int level) ssl->cert->sec_level = level; } -/* - * SSL_get_security_level - get the SSL security level - * - * @param ssl - SSL point - * - * @return security level +/** + * @brief get the SSL security level */ int SSL_get_security_level(const SSL *ssl) { @@ -1571,12 +1277,8 @@ int SSL_get_security_level(const SSL *ssl) return ssl->cert->sec_level; } -/* - * SSL_CTX_get_verify_mode - get the SSL verifying mode of the SSL context - * - * @param ctx - SSL context point - * - * @return verifying mode +/** + * @brief get the SSL verifying mode of the SSL context */ int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) { @@ -1585,13 +1287,8 @@ int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) return ctx->verify_mode; } -/* - * SSL_CTX_set_timeout - set the session timeout time - * - * @param ctx - SSL context point - * @param t - new session timeout time - * - * @return old session timeout time +/** + * @brief set the session timeout time */ long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) { @@ -1605,12 +1302,8 @@ long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) return l; } -/* - * SSL_CTX_get_timeout - get the session timeout time - * - * @param ctx - SSL context point - * - * @return current session timeout time +/** + * @brief get the session timeout time */ long SSL_CTX_get_timeout(const SSL_CTX *ctx) { @@ -1619,13 +1312,8 @@ long SSL_CTX_get_timeout(const SSL_CTX *ctx) return ctx->session_timeout; } -/* - * SSL_set_read_ahead - set the SSL if we can read as many as data - * - * @param ssl - SSL point - * @param yes - enable the function - * - * @return none +/** + * @brief set the SSL if we can read as many as data */ void SSL_set_read_ahead(SSL *ssl, int yes) { @@ -1634,13 +1322,8 @@ void SSL_set_read_ahead(SSL *ssl, int yes) ssl->rlayer.read_ahead = yes; } -/* - * SSL_set_read_ahead - set the SSL context if we can read as many as data - * - * @param ctx - SSL context point - * @param yes - enable the function - * - * @return none +/** + * @brief set the SSL context if we can read as many as data */ void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { @@ -1649,12 +1332,8 @@ void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) ctx->read_ahead = yes; } -/* - * SSL_set_read_ahead - get the SSL ahead signal if we can read as many as data - * - * @param ssl - SSL point - * - * @return SSL context ahead signal +/** + * @brief get the SSL ahead signal if we can read as many as data */ int SSL_get_read_ahead(const SSL *ssl) { @@ -1663,12 +1342,8 @@ int SSL_get_read_ahead(const SSL *ssl) return ssl->rlayer.read_ahead; } -/* - * SSL_set_read_ahead - get the SSL context ahead signal if we can read as many as data - * - * @param ctx - SSL context point - * - * @return SSL context ahead signal +/** + * @brief get the SSL context ahead signal if we can read as many as data */ long SSL_CTX_get_read_ahead(SSL_CTX *ctx) { @@ -1677,14 +1352,8 @@ long SSL_CTX_get_read_ahead(SSL_CTX *ctx) return ctx->read_ahead; } -/* - * SSL_CTX_get_ciphers - check if the SSL context can read as many as data - * - * @param ctx - SSL context point - * - * @return - * 1 : Yes - * 0 : No +/** + * @brief check if the SSL context can read as many as data */ long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx) { @@ -1693,13 +1362,8 @@ long SSL_CTX_get_default_read_ahead(SSL_CTX *ctx) return ctx->read_ahead; } -/* - * SSL_set_time - set SSL session time - * - * @param ssl - SSL point - * @param t - session time - * - * @return session time +/** + * @brief set SSL session time */ long SSL_set_time(SSL *ssl, long t) { @@ -1710,13 +1374,8 @@ long SSL_set_time(SSL *ssl, long t) return t; } -/* - * SSL_set_time - set SSL session timeout time - * - * @param ssl - SSL point - * @param t - session timeout time - * - * @return session timeout time +/** + * @brief set SSL session timeout time */ long SSL_set_timeout(SSL *ssl, long t) { @@ -1727,12 +1386,8 @@ long SSL_set_timeout(SSL *ssl, long t) return t; } -/* - * SSL_get_verify_result - get the verifying result of the SSL certification - * - * @param ssl - the SSL point - * - * @return the result of verifying +/** + * @brief get the verifying result of the SSL certification */ long SSL_get_verify_result(const SSL *ssl) { @@ -1741,12 +1396,8 @@ long SSL_get_verify_result(const SSL *ssl) return SSL_METHOD_CALL(get_verify_result, ssl); } -/* - * SSL_CTX_get_verify_depth - get the SSL verifying depth of the SSL context - * - * @param ctx - SSL context point - * - * @return verifying depth +/** + * @brief get the SSL verifying depth of the SSL context */ int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) { @@ -1755,13 +1406,8 @@ int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) return ctx->param.depth; } -/* - * SSL_CTX_set_verify_depth - set the SSL verify depth of the SSL context - * - * @param ctx - SSL context point - * @param depth - verifying depth - * - * @return one +/** + * @brief set the SSL verify depth of the SSL context */ void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) { @@ -1770,12 +1416,8 @@ void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) ctx->param.depth = depth; } -/* - * SSL_get_verify_depth - get the SSL verifying depth of the SSL - * - * @param ctx - SSL point - * - * @return verifying depth +/** + * @brief get the SSL verifying depth of the SSL */ int SSL_get_verify_depth(const SSL *ssl) { @@ -1784,13 +1426,8 @@ int SSL_get_verify_depth(const SSL *ssl) return ssl->param.depth; } -/* - * SSL_set_verify_depth - set the SSL verify depth of the SSL - * - * @param ctx - SSL point - * @param depth - verifying depth - * - * @return one +/** + * @brief set the SSL verify depth of the SSL */ void SSL_set_verify_depth(SSL *ssl, int depth) { @@ -1799,14 +1436,8 @@ void SSL_set_verify_depth(SSL *ssl, int depth) ssl->param.depth = depth; } -/* - * SSL_CTX_set_verify - set the SSL context verifying of the SSL context - * - * @param ctx - SSL context point - * @param mode - verifying mode - * @param verify_callback - verifying callback function - * - * @return none +/** + * @brief set the SSL context verifying of the SSL context */ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) { @@ -1816,14 +1447,8 @@ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509 ctx->default_verify_callback = verify_callback; } -/* - * SSL_set_verify - set the SSL verifying of the SSL context - * - * @param ctx - SSL point - * @param mode - verifying mode - * @param verify_callback - verifying callback function - * - * @return none +/** + * @brief set the SSL verifying of the SSL context */ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) { diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index c6fb40e59c..042d670ab9 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -16,7 +16,7 @@ #include "ssl_methods.h" #include "ssl_pm.h" -/* +/** * TLS method function collection */ IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, @@ -28,7 +28,7 @@ IMPLEMENT_TLS_METHOD_FUNC(TLS_method_func, ssl_pm_get_verify_result, ssl_pm_get_state); -/* +/** * TLS or SSL client method collection */ IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 0, TLS_method_func, TLS_client_method); @@ -41,7 +41,7 @@ IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_client_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, 0, TLS_method_func, SSLv3_client_method); -/* +/** * TLS or SSL server method collection */ IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, 1, TLS_method_func, TLS_server_method); @@ -54,7 +54,7 @@ IMPLEMENT_TLS_METHOD(TLS1_VERSION, 0, TLS_method_func, TLSv1_server_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, 1, TLS_method_func, SSLv3_server_method); -/* +/** * TLS or SSL method collection */ IMPLEMENT_TLS_METHOD(TLS_ANY_VERSION, -1, TLS_method_func, TLS_method); @@ -67,15 +67,15 @@ IMPLEMENT_SSL_METHOD(TLS1_VERSION, -1, TLS_method_func, TLSv1_method); IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); -/* - * X509 certification method collection +/** + * @brief get X509 object method */ IMPLEMENT_X509_METHOD(X509_method, x509_pm_new, x509_pm_free, x509_pm_load, x509_pm_unload); -/* - * private key method collection +/** + * @brief get private key object method */ IMPLEMENT_PKEY_METHOD(EVP_PKEY_method, pkey_pm_new, pkey_pm_free, diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index e13870344f..6f51963eb0 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -19,12 +19,8 @@ #include "ssl_dbg.h" #include "ssl_port.h" -/* - * EVP_PKEY_new - create a private key object - * - * @param none - * - * @return private key object point or NULL if failed +/** + * @brief create a private key object */ EVP_PKEY* EVP_PKEY_new(void) { @@ -49,12 +45,8 @@ failed1: return NULL; } -/* - * EVP_PKEY_free - free a private key object - * - * @param pkey - private key object point - * - * @return none +/** + * @brief free a private key object */ void EVP_PKEY_free(EVP_PKEY *pkey) { @@ -63,16 +55,9 @@ void EVP_PKEY_free(EVP_PKEY *pkey) ssl_free(pkey); } -/* - * d2i_PrivateKey - load a character key context into system context. If '*a' is pointed to the - * private key, then load key into it. Or create a new private key object - * - * @param type - private key type - * @param a - a point pointed to a private key point - * @param pp - a point pointed to the key context memory point - * @param length - key bytes - * - * @return private key object point or NULL if failed +/** + * @brief load a character key context into system context. If '*a' is pointed to the + * private key, then load key into it. Or create a new private key object */ EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **a, @@ -112,15 +97,8 @@ failed1: return NULL; } -/* - * SSL_CTX_use_certificate - set the SSL context private key - * - * @param ctx - SSL context point - * @param x - private key point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief set the SSL context private key */ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) { @@ -135,15 +113,8 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) return 1; } -/* - * SSL_CTX_use_certificate - set the SSL private key - * - * @param ctx - SSL point - * @param x - private key point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief set the SSL private key */ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { @@ -163,17 +134,8 @@ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) return 1; } -/* - * SSL_CTX_use_PrivateKey_ASN1 - load private key into the SSL context - * - * @param type - private key type - * @param ctx - SSL context point - * @param d - private key context point - * @param len - private key context bytes - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load private key into the SSL context */ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const unsigned char *d, long len) @@ -197,17 +159,8 @@ failed1: return 0; } -/* - * SSL_use_PrivateKey_ASN1 - load private key into the SSL - * - * @param type - private key type - * @param ctx - SSL context point - * @param d - private key context point - * @param len - private key context bytes - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load private key into the SSL */ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const unsigned char *d, long len) @@ -255,48 +208,24 @@ failed1: return 0; } -/* - * SSL_CTX_use_certificate_file - load the private key file into SSL context - * - * @param ctx - SSL context point - * @param file - private key file name - * @param type - private key encoding type - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load the private key file into SSL context */ int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { return 0; } -/* - * SSL_use_PrivateKey_file - load the private key file into SSL - * - * @param ctx - SSL point - * @param file - private key file name - * @param type - private key encoding type - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load the private key file into SSL */ int SSL_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { return 0; } -/* - * SSL_CTX_use_certificate_ASN1 - load the RSA ASN1 private key into SSL context - * - * @param ctx - SSL context point - * @param d - data point - * @param len - RSA private key length - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load the RSA ASN1 private key into SSL context */ int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len) { diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 6eb3c1d461..e96511dc4a 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -18,12 +18,8 @@ #include "ssl_dbg.h" #include "ssl_port.h" -/* - * sk_X509_NAME_new_null - create a X509 certification object - * - * @param none - * - * @return X509 certification object point or NULL if failed +/** + * @brief create a X509 certification object */ X509* X509_new(void) { @@ -48,12 +44,8 @@ failed1: return NULL; } -/* - * X509_free - free a X509 certification object - * - * @param x - X509 certification object point - * - * @return none +/** + * @brief free a X509 certification object */ void X509_free(X509 *x) { @@ -62,15 +54,9 @@ void X509_free(X509 *x) ssl_free(x); }; -/* - * d2i_X509 - load a character certification context into system context. If '*cert' is pointed to the - * certification, then load certification into it. Or create a new X509 certification object - * - * @param cert - a point pointed to X509 certification - * @param buffer - a point pointed to the certification context memory point - * @param length - certification bytes - * - * @return X509 certification object point or NULL if failed +/** + * @brief load a character certification context into system context. If '*cert' is pointed to the + * certification, then load certification into it. Or create a new X509 certification object */ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) { @@ -103,15 +89,8 @@ failed1: return NULL; } -/* - * SSL_CTX_add_client_CA - set SSL context client CA certification - * - * @param ctx - SSL context point - * @param x - client CA certification point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief set SSL context client CA certification */ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) { @@ -126,15 +105,8 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) return 1; } -/* - * SSL_add_client_CA - add CA client certification into the SSL - * - * @param ssl - SSL point - * @param x - CA certification point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief add CA client certification into the SSL */ int SSL_add_client_CA(SSL *ssl, X509 *x) { @@ -151,15 +123,8 @@ int SSL_add_client_CA(SSL *ssl, X509 *x) return 1; } -/* - * SSL_CTX_use_certificate - set the SSL context certification - * - * @param ctx - SSL context point - * @param x - X509 certification point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief set the SSL context certification */ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) { @@ -171,15 +136,8 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) return 1; } -/* - * SSL_CTX_use_certificate - set the SSL certification - * - * @param ctx - SSL point - * @param x - X509 certification point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief set the SSL certification */ int SSL_use_certificate(SSL *ssl, X509 *x) { @@ -191,12 +149,8 @@ int SSL_use_certificate(SSL *ssl, X509 *x) return 1; } -/* - * SSL_get_certificate - get the SSL certification point - * - * @param ssl - SSL point - * - * @return SSL certification point +/** + * @brief get the SSL certification point */ X509 *SSL_get_certificate(const SSL *ssl) { @@ -205,16 +159,8 @@ X509 *SSL_get_certificate(const SSL *ssl) return ssl->cert->x509; } -/* - * SSL_CTX_use_certificate_ASN1 - load certification into the SSL context - * - * @param ctx - SSL context point - * @param len - certification context bytes - * @param d - certification context point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load certification into the SSL context */ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d) @@ -238,16 +184,8 @@ failed1: return 0; } -/* - * SSL_use_certificate_ASN1 - load certification into the SSL - * - * @param ctx - SSL point - * @param len - certification context bytes - * @param d - certification context point - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load certification into the SSL */ int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d) @@ -295,44 +233,24 @@ failed1: return 0; } -/* - * SSL_CTX_use_certificate_file - load the certification file into SSL context - * - * @param ctx - SSL context point - * @param file - certification file name - * @param type - certification encoding type - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load the certification file into SSL context */ int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) { return 0; } -/* - * SSL_use_certificate_file - load the certification file into SSL - * - * @param ctx - SSL point - * @param file - certification file name - * @param type - certification encoding type - * - * @return - * 1 : OK - * 0 : failed +/** + * @brief load the certification file into SSL */ int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { return 0; } -/* - * SSL_get_peer_certificate - get peer certification - * - * @param ssl - SSL point - * - * @return certification +/** + * @brief get peer certification */ X509 *SSL_get_peer_certificate(const SSL *ssl) { diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index b03aee3e37..9abfc212ec 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -62,6 +62,9 @@ unsigned int max_content_len; /*********************************************************************************************/ /************************************ SSL arch interface *************************************/ +/** + * @brief create SSL low-level object + */ int ssl_pm_new(SSL *ssl) { struct ssl_pm *ssl_pm; @@ -140,6 +143,9 @@ failed1: return -1; } +/** + * @brief free SSL low-level object + */ void ssl_pm_free(SSL *ssl) { struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; @@ -156,6 +162,9 @@ void ssl_pm_free(SSL *ssl) ssl->ssl_pm = NULL; } +/** + * @brief reload SSL low-level certification object + */ static int ssl_pm_reload_crt(SSL *ssl) { int ret; From f9fd5b6c72b6a08579f17317ab997306393516b1 Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 14:52:33 +0800 Subject: [PATCH 023/285] components/openssl: add X509 verify result errno --- .../openssl/include/internal/x509_vfy.h | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 components/openssl/include/internal/x509_vfy.h diff --git a/components/openssl/include/internal/x509_vfy.h b/components/openssl/include/internal/x509_vfy.h new file mode 100644 index 0000000000..cab110e421 --- /dev/null +++ b/components/openssl/include/internal/x509_vfy.h @@ -0,0 +1,103 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _X509_VFY_H_ +#define _X509_VFY_H_ + +#define X509_V_OK 0 +#define X509_V_ERR_UNSPECIFIED 1 +#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT 2 +#define X509_V_ERR_UNABLE_TO_GET_CRL 3 +#define X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE 4 +#define X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE 5 +#define X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY 6 +#define X509_V_ERR_CERT_SIGNATURE_FAILURE 7 +#define X509_V_ERR_CRL_SIGNATURE_FAILURE 8 +#define X509_V_ERR_CERT_NOT_YET_VALID 9 +#define X509_V_ERR_CERT_HAS_EXPIRED 10 +#define X509_V_ERR_CRL_NOT_YET_VALID 11 +#define X509_V_ERR_CRL_HAS_EXPIRED 12 +#define X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD 13 +#define X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD 14 +#define X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD 15 +#define X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD 16 +#define X509_V_ERR_OUT_OF_MEM 17 +#define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18 +#define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19 +#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20 +#define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21 +#define X509_V_ERR_CERT_CHAIN_TOO_LONG 22 +#define X509_V_ERR_CERT_REVOKED 23 +#define X509_V_ERR_INVALID_CA 24 +#define X509_V_ERR_PATH_LENGTH_EXCEEDED 25 +#define X509_V_ERR_INVALID_PURPOSE 26 +#define X509_V_ERR_CERT_UNTRUSTED 27 +#define X509_V_ERR_CERT_REJECTED 28 +/* These are 'informational' when looking for issuer cert */ +#define X509_V_ERR_SUBJECT_ISSUER_MISMATCH 29 +#define X509_V_ERR_AKID_SKID_MISMATCH 30 +#define X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH 31 +#define X509_V_ERR_KEYUSAGE_NO_CERTSIGN 32 +#define X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER 33 +#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 +#define X509_V_ERR_KEYUSAGE_NO_CRL_SIGN 35 +#define X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION 36 +#define X509_V_ERR_INVALID_NON_CA 37 +#define X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED 38 +#define X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE 39 +#define X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED 40 +#define X509_V_ERR_INVALID_EXTENSION 41 +#define X509_V_ERR_INVALID_POLICY_EXTENSION 42 +#define X509_V_ERR_NO_EXPLICIT_POLICY 43 +#define X509_V_ERR_DIFFERENT_CRL_SCOPE 44 +#define X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE 45 +#define X509_V_ERR_UNNESTED_RESOURCE 46 +#define X509_V_ERR_PERMITTED_VIOLATION 47 +#define X509_V_ERR_EXCLUDED_VIOLATION 48 +#define X509_V_ERR_SUBTREE_MINMAX 49 +/* The application is not happy */ +#define X509_V_ERR_APPLICATION_VERIFICATION 50 +#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE 51 +#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX 52 +#define X509_V_ERR_UNSUPPORTED_NAME_SYNTAX 53 +#define X509_V_ERR_CRL_PATH_VALIDATION_ERROR 54 +/* Another issuer check debug option */ +#define X509_V_ERR_PATH_LOOP 55 +/* Suite B mode algorithm violation */ +#define X509_V_ERR_SUITE_B_INVALID_VERSION 56 +#define X509_V_ERR_SUITE_B_INVALID_ALGORITHM 57 +#define X509_V_ERR_SUITE_B_INVALID_CURVE 58 +#define X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM 59 +#define X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED 60 +#define X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 61 +/* Host, email and IP check errors */ +#define X509_V_ERR_HOSTNAME_MISMATCH 62 +#define X509_V_ERR_EMAIL_MISMATCH 63 +#define X509_V_ERR_IP_ADDRESS_MISMATCH 64 +/* DANE TLSA errors */ +#define X509_V_ERR_DANE_NO_MATCH 65 +/* security level errors */ +#define X509_V_ERR_EE_KEY_TOO_SMALL 66 +#define X509_V_ERR_CA_KEY_TOO_SMALL 67 +#define X509_V_ERR_CA_MD_TOO_WEAK 68 +/* Caller error */ +#define X509_V_ERR_INVALID_CALL 69 +/* Issuer lookup error */ +#define X509_V_ERR_STORE_LOOKUP 70 +/* Certificate transparency */ +#define X509_V_ERR_NO_VALID_SCTS 71 + +#define X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION 72 + +#endif From 5c5f7eb7feb16ba2632ad4701d7ef283a3371fec Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 14:53:19 +0800 Subject: [PATCH 024/285] components/openssl: add openssl stack object function --- .../openssl/include/internal/ssl_stack.h | 33 +++++++++ components/openssl/library/ssl_stack.c | 70 +++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 components/openssl/include/internal/ssl_stack.h create mode 100644 components/openssl/library/ssl_stack.c diff --git a/components/openssl/include/internal/ssl_stack.h b/components/openssl/include/internal/ssl_stack.h new file mode 100644 index 0000000000..b97015bd95 --- /dev/null +++ b/components/openssl/include/internal/ssl_stack.h @@ -0,0 +1,33 @@ +#ifndef _SSL_STACK_H_ +#define _SSL_STACK_H_ + +#include "ssl_types.h" + +/** + * @brief create a openssl stack object + * + * @param c - stack function + * + * @return openssl stack object point + */ +OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c); + +/** + * @brief create a NULL function openssl stack object + * + * @param none + * + * @return openssl stack object point + */ +OPENSSL_STACK *OPENSSL_sk_new_null(void); + +/** + * @brief free openssl stack object + * + * @param openssl stack object point + * + * @return none + */ +void OPENSSL_sk_free(OPENSSL_STACK *stack); + +#endif diff --git a/components/openssl/library/ssl_stack.c b/components/openssl/library/ssl_stack.c new file mode 100644 index 0000000000..46e6f7efd8 --- /dev/null +++ b/components/openssl/library/ssl_stack.c @@ -0,0 +1,70 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "ssl_stack.h" +#include "ssl_dbg.h" +#include "ssl_port.h" + +#ifndef CONFIG_MIN_NODES + #define MIN_NODES 4 +#else + #define MIN_NODES CONFIG_MIN_NODES +#endif + +/** + * @brief create a openssl stack object + */ +OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c) +{ + OPENSSL_STACK *stack; + char **data; + + stack = ssl_malloc(sizeof(OPENSSL_STACK)); + if (!stack) + SSL_RET(failed1); + + data = ssl_malloc(sizeof(*data) * MIN_NODES); + if (!data) + SSL_RET(failed2); + + stack->data = data; + stack->num_alloc = MIN_NODES; + stack->c = c; + + return stack; + +failed2: + ssl_free(stack); +failed1: + return NULL; +} + +/** + * @brief create a NULL function openssl stack object + */ +OPENSSL_STACK *OPENSSL_sk_new_null(void) +{ + return OPENSSL_sk_new((OPENSSL_sk_compfunc)NULL); +} + +/** + * @brief free openssl stack object + */ +void OPENSSL_sk_free(OPENSSL_STACK *stack) +{ + SSL_ASSERT(stack); + + ssl_free(stack->data); + ssl_free(stack); +} From 12b72e91afb384d975a5abfe0eeedcec560345b6 Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 14:58:14 +0800 Subject: [PATCH 025/285] components/openssl: remove unused variate --- components/openssl/library/ssl_pkey.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 6f51963eb0..6891b69eb3 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -118,9 +118,6 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) */ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { - int ret; - int ssl_ret; - SSL_ASSERT(ctx); SSL_ASSERT(pkey); From 83aea6c833c399757d3c740bcd23b0f6ba912859 Mon Sep 17 00:00:00 2001 From: dongheng Date: Fri, 23 Sep 2016 15:18:14 +0800 Subject: [PATCH 026/285] components/openssl: add extern C symbol --- components/openssl/include/internal/ssl3.h | 8 ++++++++ components/openssl/include/internal/ssl_cert.h | 8 ++++++++ components/openssl/include/internal/ssl_code.h | 8 ++++++++ components/openssl/include/internal/ssl_dbg.h | 8 ++++++++ components/openssl/include/internal/ssl_lib.h | 8 ++++++++ components/openssl/include/internal/ssl_methods.h | 8 ++++++++ components/openssl/include/internal/ssl_pkey.h | 8 ++++++++ components/openssl/include/internal/ssl_stack.h | 8 ++++++++ components/openssl/include/internal/ssl_types.h | 8 ++++++++ components/openssl/include/internal/ssl_x509.h | 8 ++++++++ components/openssl/include/internal/tls1.h | 8 ++++++++ components/openssl/include/internal/x509_vfy.h | 8 ++++++++ components/openssl/include/openssl/ssl.h | 7 +++++++ components/openssl/include/platform/ssl_pm.h | 4 ++++ components/openssl/include/platform/ssl_port.h | 4 ++++ 15 files changed, 111 insertions(+) diff --git a/components/openssl/include/internal/ssl3.h b/components/openssl/include/internal/ssl3.h index c90d546df0..007b392f3e 100644 --- a/components/openssl/include/internal/ssl3.h +++ b/components/openssl/include/internal/ssl3.h @@ -15,6 +15,10 @@ #ifndef _SSL3_H_ #define _SSL3_H_ +#ifdef __cplusplus + extern "C" { +#endif + # define SSL3_AD_CLOSE_NOTIFY 0 # define SSL3_AD_UNEXPECTED_MESSAGE 10/* fatal */ # define SSL3_AD_BAD_RECORD_MAC 20/* fatal */ @@ -33,4 +37,8 @@ #define SSL3_VERSION 0x0300 +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_cert.h b/components/openssl/include/internal/ssl_cert.h index b0bd09d480..6441aaf521 100644 --- a/components/openssl/include/internal/ssl_cert.h +++ b/components/openssl/include/internal/ssl_cert.h @@ -15,6 +15,10 @@ #ifndef _SSL_CERT_H_ #define _SSL_CERT_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl_types.h" /** @@ -35,4 +39,8 @@ CERT* ssl_cert_new(void); */ void ssl_cert_free(CERT *cert); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_code.h b/components/openssl/include/internal/ssl_code.h index 34107d432d..80fdbb20f3 100644 --- a/components/openssl/include/internal/ssl_code.h +++ b/components/openssl/include/internal/ssl_code.h @@ -15,6 +15,10 @@ #ifndef _SSL_CODE_H_ #define _SSL_CODE_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl3.h" #include "tls1.h" #include "x509_vfy.h" @@ -113,4 +117,8 @@ typedef enum { TLS_ST_SW_FINISHED } OSSL_HANDSHAKE_STATE; +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 745de536ff..27a4bc4db5 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -15,6 +15,10 @@ #ifndef _SSL_DEBUG_H_ #define _SSL_DEBUG_H_ +#ifdef __cplusplus + extern "C" { +#endif + #define SSL_DEBUG_ENBALE 0 #define SSL_DEBUG_LEVEL 0 #define SSL_ASSERT_ENABLE 0 @@ -46,4 +50,8 @@ #define SSL_DEBUG(level, ...) { if (level > SSL_DEBUG_LEVEL) {SSL_PRINT(__VA_ARGS__);} } +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_lib.h b/components/openssl/include/internal/ssl_lib.h index 6ea547a7c5..bf7de22fdf 100644 --- a/components/openssl/include/internal/ssl_lib.h +++ b/components/openssl/include/internal/ssl_lib.h @@ -15,6 +15,14 @@ #ifndef _SSL_LIB_H_ #define _SSL_LIB_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl_types.h" +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index 68737b4381..a20b7c768e 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -15,6 +15,10 @@ #ifndef _SSL_METHODS_H_ #define _SSL_METHODS_H_ +#ifdef __cplusplus + extern "C" { +#endif + /** * TLS method function implement */ @@ -110,4 +114,8 @@ const X509_METHOD* X509_method(void); */ const PKEY_METHOD* EVP_PKEY_method(void); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h index d9a22ee02c..5b7f341de9 100644 --- a/components/openssl/include/internal/ssl_pkey.h +++ b/components/openssl/include/internal/ssl_pkey.h @@ -15,6 +15,10 @@ #ifndef _SSL_PKEY_H_ #define _SSL_PKEY_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl_types.h" /** @@ -51,4 +55,8 @@ EVP_PKEY* d2i_PrivateKey(int type, */ void EVP_PKEY_free(EVP_PKEY *x); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_stack.h b/components/openssl/include/internal/ssl_stack.h index b97015bd95..b37c8dffa9 100644 --- a/components/openssl/include/internal/ssl_stack.h +++ b/components/openssl/include/internal/ssl_stack.h @@ -1,6 +1,10 @@ #ifndef _SSL_STACK_H_ #define _SSL_STACK_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl_types.h" /** @@ -30,4 +34,8 @@ OPENSSL_STACK *OPENSSL_sk_new_null(void); */ void OPENSSL_sk_free(OPENSSL_STACK *stack); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 6da6076148..1dc31f5a53 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -15,6 +15,10 @@ #ifndef _SSL_TYPES_H_ #define _SSL_TYPES_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl_code.h" typedef void SSL_CIPHER; @@ -294,4 +298,8 @@ typedef int (*next_proto_cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index 9359073b69..5dac46137b 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -15,6 +15,10 @@ #ifndef _SSL_X509_H_ #define _SSL_X509_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl_types.h" #include "ssl_stack.h" @@ -50,4 +54,8 @@ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); */ void X509_free(X509 *x); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/tls1.h b/components/openssl/include/internal/tls1.h index b2da639194..a9da53e063 100644 --- a/components/openssl/include/internal/tls1.h +++ b/components/openssl/include/internal/tls1.h @@ -15,6 +15,10 @@ #ifndef _TLS1_H_ #define _TLS1_H_ +#ifdef __cplusplus + extern "C" { +#endif + # define TLS1_AD_DECRYPTION_FAILED 21 # define TLS1_AD_RECORD_OVERFLOW 22 # define TLS1_AD_UNKNOWN_CA 48/* fatal */ @@ -44,4 +48,8 @@ #define TLS1_1_VERSION 0x0302 #define TLS1_2_VERSION 0x0303 +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/internal/x509_vfy.h b/components/openssl/include/internal/x509_vfy.h index cab110e421..d5b0d1a213 100644 --- a/components/openssl/include/internal/x509_vfy.h +++ b/components/openssl/include/internal/x509_vfy.h @@ -15,6 +15,10 @@ #ifndef _X509_VFY_H_ #define _X509_VFY_H_ +#ifdef __cplusplus + extern "C" { +#endif + #define X509_V_OK 0 #define X509_V_ERR_UNSPECIFIED 1 #define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT 2 @@ -100,4 +104,8 @@ #define X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION 72 +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 3f92a68d70..3e8e88e67c 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -15,6 +15,10 @@ #ifndef _SSL_H_ #define _SSL_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "platform/ssl_port.h" #include "internal/ssl_x509.h" @@ -1684,5 +1688,8 @@ const char *SSL_get_psk_identity_hint(SSL *ssl); */ const char *SSL_get_psk_identity(SSL *ssl); +#ifdef __cplusplus +} +#endif #endif diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index 3f64a4ae32..47a7331b7e 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -15,6 +15,10 @@ #ifndef _SSL_PM_H_ #define _SSL_PM_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "ssl_types.h" #include "ssl_port.h" diff --git a/components/openssl/include/platform/ssl_port.h b/components/openssl/include/platform/ssl_port.h index 23ef5a8757..995d33e0e5 100644 --- a/components/openssl/include/platform/ssl_port.h +++ b/components/openssl/include/platform/ssl_port.h @@ -15,6 +15,10 @@ #ifndef _SSL_PORT_H_ #define _SSL_PORT_H_ +#ifdef __cplusplus + extern "C" { +#endif + #include "esp_types.h" void* ssl_zalloc(size_t size); From d2bc170b869be45772c0889f0c2a8af751222510 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Fri, 23 Sep 2016 18:13:10 +0800 Subject: [PATCH 027/285] components/openssl: add SSL session function 1. add SSL session new and free function 2. add SSL session peer cert get and free operation 3. above all, change low-level cert object to be object point not object --- .../openssl/include/internal/ssl_types.h | 2 +- components/openssl/library/ssl_lib.c | 46 +++- components/openssl/library/ssl_x509.c | 4 +- components/openssl/platform/ssl_pm.c | 207 ++++++++++-------- 4 files changed, 161 insertions(+), 98 deletions(-) diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 1dc31f5a53..34249ea054 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -213,7 +213,7 @@ struct ssl_st /* where we are */ OSSL_STATEM statem; - SSL_SESSION session; + SSL_SESSION *session; int verify_mode; diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index a84b89e06c..ded30a33ac 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -117,6 +117,38 @@ OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl) return state; } +/** + * @brief create a new SSL session object + */ +SSL_SESSION* SSL_SESSION_new(void) +{ + SSL_SESSION *session; + + session = ssl_zalloc(sizeof(SSL_SESSION)); + if (!session) + SSL_RET(failed1); + + session->peer = X509_new(); + if (!session->peer) + SSL_RET(failed2); + + return session; + +failed2: + ssl_free(session); +failed1: + return NULL; +} + +/** + * @brief free a new SSL session object + */ +void SSL_SESSION_free(SSL_SESSION *session) +{ + X509_free(session->peer); + ssl_free(session); +} + /** * @brief create a SSL context */ @@ -210,6 +242,10 @@ SSL *SSL_new(SSL_CTX *ctx) if (!ssl) SSL_RET(failed1, "ssl_zalloc\n"); + ssl->session = SSL_SESSION_new(); + if (!ssl->session) + SSL_RET(failed2, "ssl_zalloc\n"); + ssl->ctx = ctx; ssl->method = ctx->method; @@ -222,12 +258,14 @@ SSL *SSL_new(SSL_CTX *ctx) ret = SSL_METHOD_CALL(new, ssl); if (ret) - SSL_RET(failed2, "ssl_new\n"); + SSL_RET(failed3, "ssl_new\n"); ssl->rwstate = SSL_NOTHING; return ssl; +failed3: + SSL_SESSION_free(ssl->session); failed2: ssl_free(ssl); failed1: @@ -243,6 +281,8 @@ void SSL_free(SSL *ssl) SSL_METHOD_CALL(free, ssl); + SSL_SESSION_free(ssl->session); + if (ssl->ca_reload) X509_free(ssl->client_CA); @@ -1369,7 +1409,7 @@ long SSL_set_time(SSL *ssl, long t) { SSL_ASSERT(ssl); - ssl->session.time = t; + ssl->session->time = t; return t; } @@ -1381,7 +1421,7 @@ long SSL_set_timeout(SSL *ssl, long t) { SSL_ASSERT(ssl); - ssl->session.timeout = t; + ssl->session->timeout = t; return t; } diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index e96511dc4a..c3fa0b307a 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -32,7 +32,7 @@ X509* X509_new(void) x->method = X509_method(); - ret = x->method->x509_new(x); + ret = X509_METHOD_CALL(new, x); if (ret) SSL_RET(failed2, "x509_new\n"); @@ -256,5 +256,5 @@ X509 *SSL_get_peer_certificate(const SSL *ssl) { SSL_ASSERT(ssl); - return ssl->session.peer; + return ssl->session->peer; } diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 9abfc212ec..0cf8f6c0a9 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -43,16 +43,16 @@ struct ssl_pm struct x509_pm { - mbedtls_x509_crt x509_crt; + mbedtls_x509_crt *x509_crt; - int load; + mbedtls_x509_crt *ex_crt; }; struct pkey_pm { - mbedtls_pk_context pkey; + mbedtls_pk_context *pkey; - int load; + mbedtls_pk_context *ex_pkey; }; @@ -78,13 +78,9 @@ int ssl_pm_new(SSL *ssl) const SSL_METHOD *method = ssl->method; - ssl->session.peer = ssl_zalloc(sizeof(X509)); - if (!ssl->session.peer) - SSL_ERR(ret, failed1, "ssl_zalloc\n"); - ssl_pm = ssl_zalloc(sizeof(struct ssl_pm)); if (!ssl_pm) - SSL_ERR(ret, failed2, "ssl_zalloc\n"); + SSL_ERR(ret, failed1, "ssl_zalloc\n"); mbedtls_net_init(&ssl_pm->fd); mbedtls_net_init(&ssl_pm->cl_fd); @@ -96,7 +92,7 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ctr_drbg_seed(&ssl_pm->ctr_drbg, mbedtls_entropy_func, &ssl_pm->entropy, pers, pers_len); if (ret) - SSL_ERR(ret, failed3, "mbedtls_ctr_drbg_seed:[-0x%x]\n", -ret); + SSL_ERR(ret, failed2, "mbedtls_ctr_drbg_seed:[-0x%x]\n", -ret); if (method->endpoint) { endpoint = MBEDTLS_SSL_IS_SERVER; @@ -105,7 +101,7 @@ int ssl_pm_new(SSL *ssl) } ret = mbedtls_ssl_config_defaults(&ssl_pm->conf, endpoint, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (ret) - SSL_ERR(ret, failed3, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); + SSL_ERR(ret, failed2, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); if (TLS1_2_VERSION == ssl->version) version = MBEDTLS_SSL_MINOR_VERSION_3; @@ -124,7 +120,7 @@ int ssl_pm_new(SSL *ssl) ret = mbedtls_ssl_setup(&ssl_pm->ssl, &ssl_pm->conf); if (ret) - SSL_ERR(ret, failed4, "mbedtls_ssl_setup:[-0x%x]\n", -ret); + SSL_ERR(ret, failed3, "mbedtls_ssl_setup:[-0x%x]\n", -ret); mbedtls_ssl_set_bio(&ssl_pm->ssl, &ssl_pm->fd, mbedtls_net_send, mbedtls_net_recv, NULL); @@ -132,13 +128,11 @@ int ssl_pm_new(SSL *ssl) return 0; -failed4: +failed3: mbedtls_ssl_config_free(&ssl_pm->conf); mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); -failed3: - mbedtls_entropy_free(&ssl_pm->entropy); failed2: - ssl_free(ssl->session.peer); + mbedtls_entropy_free(&ssl_pm->entropy); failed1: return -1; } @@ -155,9 +149,6 @@ void ssl_pm_free(SSL *ssl) mbedtls_ssl_config_free(&ssl_pm->conf); mbedtls_ssl_free(&ssl_pm->ssl); - ssl_free(ssl->session.peer); - ssl->session.peer = NULL; - ssl_free(ssl_pm); ssl->ssl_pm = NULL; } @@ -186,12 +177,12 @@ static int ssl_pm_reload_crt(SSL *ssl) mbedtls_ssl_conf_authmode(&ssl_pm->conf, mode); - if (ca_pm->load) { - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, &ca_pm->x509_crt, NULL); + if (ca_pm->x509_crt) { + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); } - if (pkey_pm->load) { - ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, &crt_pm->x509_crt, &pkey_pm->pkey); + if (crt_pm->x509_crt && pkey_pm->pkey) { + ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, crt_pm->x509_crt, pkey_pm->pkey); if (ret) return -1; } @@ -217,9 +208,11 @@ int ssl_pm_handshake(SSL *ssl) ssl_speed_up_exit(); if (!mbed_ret) { + struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; + ret = 1; - ssl->session.peer->x509_pm = (struct x509_pm *)mbedtls_ssl_get_peer_cert(&ssl_pm->ssl); + x509_pm->ex_crt = (mbedtls_x509_crt *)mbedtls_ssl_get_peer_cert(&ssl_pm->ssl); } else { ret = 0; SSL_DEBUG(1, "mbedtls_ssl_handshake [-0x%x]\n", -mbed_ret); @@ -234,8 +227,13 @@ int ssl_pm_shutdown(SSL *ssl) struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm; mbed_ret = mbedtls_ssl_close_notify(&ssl_pm->ssl); - if (!mbed_ret) + if (!mbed_ret) { + struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; + ret = 0; + + x509_pm->ex_crt = NULL; + } else ret = -1; @@ -365,51 +363,26 @@ int x509_pm_new(X509 *x) x509_pm = ssl_zalloc(sizeof(struct x509_pm)); if (!x509_pm) - return -1; + SSL_RET(failed1); x->x509_pm = x509_pm; return 0; + +failed1: + return -1; } void x509_pm_unload(X509 *x) { struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; - if (x509_pm->load) - mbedtls_x509_crt_free(&x509_pm->x509_crt); + if (x509_pm->x509_crt) { + mbedtls_x509_crt_free(x509_pm->x509_crt); - x509_pm->load = 0; -} - -int x509_pm_load(X509 *x, const unsigned char *buffer, int len) -{ - int ret; - unsigned char *load_buf; - struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; - - load_buf = ssl_malloc(len + 1); - if (!load_buf) - SSL_RET(failed1); - - ssl_memcpy(load_buf, buffer, len); - load_buf[len] = '\0'; - - x509_pm_unload(x); - - mbedtls_x509_crt_init(&x509_pm->x509_crt); - ret = mbedtls_x509_crt_parse(&x509_pm->x509_crt, load_buf, len); - ssl_free(load_buf); - - if (ret) - SSL_RET(failed1, ""); - - x509_pm->load = 1; - - return 0; - -failed1: - return -1; + ssl_free(x509_pm->x509_crt); + x509_pm->x509_crt = NULL; + } } void x509_pm_free(X509 *x) @@ -420,6 +393,44 @@ void x509_pm_free(X509 *x) x->x509_pm = NULL; } +int x509_pm_load(X509 *x, const unsigned char *buffer, int len) +{ + int ret; + unsigned char *load_buf; + struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; + + if (!x509_pm->x509_crt) { + x509_pm->x509_crt = ssl_malloc(sizeof(mbedtls_x509_crt)); + if (!x509_pm->x509_crt) + SSL_RET(failed1); + } + + load_buf = ssl_malloc(len + 1); + if (!load_buf) + SSL_RET(failed2); + + ssl_memcpy(load_buf, buffer, len); + load_buf[len] = '\0'; + + if (x509_pm->x509_crt) + mbedtls_x509_crt_free(x509_pm->x509_crt); + + mbedtls_x509_crt_init(x509_pm->x509_crt); + ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len); + ssl_free(load_buf); + + if (ret) + SSL_RET(failed2); + + return 0; + +failed2: + ssl_free(x509_pm->x509_crt); + x509_pm->x509_crt = NULL; +failed1: + return -1; +} + int pkey_pm_new(EVP_PKEY *pkey) { struct pkey_pm *pkey_pm; @@ -437,40 +448,12 @@ void pkey_pm_unload(EVP_PKEY *pkey) { struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; - if (pkey_pm->load) - mbedtls_pk_free(&pkey_pm->pkey); + if (pkey_pm->pkey) { + mbedtls_pk_free(pkey_pm->pkey); - pkey_pm->load = 0; -} - -int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len) -{ - int ret; - unsigned char *load_buf; - struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; - - load_buf = ssl_malloc(len + 1); - if (!load_buf) - SSL_RET(failed1); - - ssl_memcpy(load_buf, buffer, len); - load_buf[len] = '\0'; - - pkey_pm_unload(pkey); - - mbedtls_pk_init(&pkey_pm->pkey); - ret = mbedtls_pk_parse_key(&pkey_pm->pkey, load_buf, len, NULL, 0); - ssl_free(load_buf); - - if (ret) - SSL_RET(failed1, ""); - - pkey_pm->load = 1; - - return 0; - -failed1: - return -1; + ssl_free(pkey_pm->pkey); + pkey_pm->pkey = NULL; + } } void pkey_pm_free(EVP_PKEY *pkey) @@ -481,6 +464,46 @@ void pkey_pm_free(EVP_PKEY *pkey) pkey->pkey_pm = NULL; } +int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len) +{ + int ret; + unsigned char *load_buf; + struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; + + if (!pkey_pm->pkey) { + pkey_pm->pkey = ssl_malloc(sizeof(mbedtls_pk_context)); + if (!pkey_pm->pkey) + SSL_RET(failed1); + } + + load_buf = ssl_malloc(len + 1); + if (!load_buf) + SSL_RET(failed2); + + ssl_memcpy(load_buf, buffer, len); + load_buf[len] = '\0'; + + if (pkey_pm->pkey) + mbedtls_pk_free(pkey_pm->pkey); + + mbedtls_pk_init(pkey_pm->pkey); + ret = mbedtls_pk_parse_key(pkey_pm->pkey, load_buf, len, NULL, 0); + ssl_free(load_buf); + + if (ret) + SSL_RET(failed2); + + return 0; + +failed2: + ssl_free(pkey_pm->pkey); + pkey_pm->pkey = NULL; +failed1: + return -1; +} + + + void ssl_pm_set_bufflen(SSL *ssl, int len) { max_content_len = len; From e1c4a4bfa3e9a929c42e40238878c1bce83db76a Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Fri, 23 Sep 2016 18:47:09 +0800 Subject: [PATCH 028/285] components/openssl: add cert and pkey extra object point the point is pointed to its father's object and should not free just set NULL if not use --- components/openssl/library/ssl_lib.c | 26 +++++++++++++++++--------- components/openssl/platform/ssl_pm.c | 23 +++++++++++++++++++++-- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index ded30a33ac..06bbe270c5 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -246,24 +246,34 @@ SSL *SSL_new(SSL_CTX *ctx) if (!ssl->session) SSL_RET(failed2, "ssl_zalloc\n"); + ssl->cert = ssl_cert_new(); + if (!ssl->cert) + SSL_RET(failed3, "ssl_cert_new\n"); + + ssl->client_CA = X509_new(); + if (!ssl->client_CA) + SSL_RET(failed4, "ssl_cert_new\n"); + ssl->ctx = ctx; ssl->method = ctx->method; ssl->version = ctx->version; ssl->options = ctx->options; - ssl->cert = ctx->cert; - ssl->client_CA = ctx->client_CA; ssl->verify_mode = ctx->verify_mode; ret = SSL_METHOD_CALL(new, ssl); if (ret) - SSL_RET(failed3, "ssl_new\n"); + SSL_RET(failed5, "ssl_new\n"); ssl->rwstate = SSL_NOTHING; return ssl; +failed5: + X509_free(ssl->client_CA); +failed4: + ssl_cert_free(ssl->cert); failed3: SSL_SESSION_free(ssl->session); failed2: @@ -281,14 +291,12 @@ void SSL_free(SSL *ssl) SSL_METHOD_CALL(free, ssl); + X509_free(ssl->client_CA); + + ssl_cert_free(ssl->cert); + SSL_SESSION_free(ssl->session); - if (ssl->ca_reload) - X509_free(ssl->client_CA); - - if (ssl->crt_reload) - ssl_cert_free(ssl->cert); - ssl_free(ssl); } diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 0cf8f6c0a9..311c3a4b6f 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -78,6 +78,14 @@ int ssl_pm_new(SSL *ssl) const SSL_METHOD *method = ssl->method; + struct x509_pm *ctx_ca = (struct x509_pm *)ssl->ctx->client_CA->x509_pm; + struct x509_pm *ctx_crt = (struct x509_pm *)ssl->ctx->cert->x509->x509_pm; + struct pkey_pm *ctx_pkey = (struct pkey_pm *)ssl->ctx->cert->pkey->pkey_pm; + + struct x509_pm *ssl_ca = (struct x509_pm *)ssl->client_CA->x509_pm; + struct x509_pm *ssl_crt = (struct x509_pm *)ssl->cert->x509->x509_pm; + struct pkey_pm *ssl_pkey = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; + ssl_pm = ssl_zalloc(sizeof(struct ssl_pm)); if (!ssl_pm) SSL_ERR(ret, failed1, "ssl_zalloc\n"); @@ -126,6 +134,10 @@ int ssl_pm_new(SSL *ssl) ssl->ssl_pm = ssl_pm; + ssl_ca->ex_crt = ctx_ca->x509_crt; + ssl_crt->ex_crt = ctx_crt->x509_crt; + ssl_pkey->ex_pkey = ctx_pkey->pkey; + return 0; failed3: @@ -179,14 +191,21 @@ static int ssl_pm_reload_crt(SSL *ssl) if (ca_pm->x509_crt) { mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); + } else if (ca_pm->ex_crt) { + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); } if (crt_pm->x509_crt && pkey_pm->pkey) { ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, crt_pm->x509_crt, pkey_pm->pkey); - if (ret) - return -1; + } else if (crt_pm->ex_crt && pkey_pm->ex_pkey) { + ret = mbedtls_ssl_conf_own_cert(&ssl_pm->conf, crt_pm->ex_crt, pkey_pm->ex_pkey); + } else { + ret = 0; } + if (ret) + return -1; + return 0; } From cf4aaf639714f4d479dd38ea396ff0a66d5a5981 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 26 Sep 2016 11:14:19 +0800 Subject: [PATCH 029/285] components/openssl: optimize the SSL certification and private key function 1. add inheritance function 2. remove low-level platform unload cert & pkey function 3. optimize the cert load and free function --- .../openssl/include/internal/ssl_cert.h | 9 ++ .../openssl/include/internal/ssl_methods.h | 12 +-- .../openssl/include/internal/ssl_pkey.h | 9 ++ .../openssl/include/internal/ssl_types.h | 12 +-- .../openssl/include/internal/ssl_x509.h | 9 ++ components/openssl/include/platform/ssl_pm.h | 11 +-- components/openssl/library/ssl_cert.c | 33 +++++-- components/openssl/library/ssl_lib.c | 16 ++-- components/openssl/library/ssl_methods.c | 4 +- components/openssl/library/ssl_pkey.c | 82 ++++++++--------- components/openssl/library/ssl_x509.c | 89 ++++++++++--------- components/openssl/platform/ssl_pm.c | 56 +++++------- 12 files changed, 178 insertions(+), 164 deletions(-) diff --git a/components/openssl/include/internal/ssl_cert.h b/components/openssl/include/internal/ssl_cert.h index 6441aaf521..86cf31ad51 100644 --- a/components/openssl/include/internal/ssl_cert.h +++ b/components/openssl/include/internal/ssl_cert.h @@ -21,6 +21,15 @@ #include "ssl_types.h" +/** + * @brief create a certification object include private key object according to input certification + * + * @param ic - input certification point + * + * @return certification object point + */ +CERT *__ssl_cert_new(CERT *ic); + /** * @brief create a certification object include private key object * diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index a20b7c768e..9fd9ce9068 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -69,14 +69,12 @@ #define IMPLEMENT_X509_METHOD(func_name, \ new, \ free, \ - load, \ - unload) \ + load) \ const X509_METHOD* func_name(void) { \ static const X509_METHOD func_name##_data LOCAL_ATRR = { \ new, \ free, \ - load, \ - unload, \ + load \ }; \ return &func_name##_data; \ } @@ -84,14 +82,12 @@ #define IMPLEMENT_PKEY_METHOD(func_name, \ new, \ free, \ - load, \ - unload) \ + load) \ const PKEY_METHOD* func_name(void) { \ static const PKEY_METHOD func_name##_data LOCAL_ATRR = { \ new, \ free, \ - load, \ - unload, \ + load \ }; \ return &func_name##_data; \ } diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h index 5b7f341de9..f4da041681 100644 --- a/components/openssl/include/internal/ssl_pkey.h +++ b/components/openssl/include/internal/ssl_pkey.h @@ -21,6 +21,15 @@ #include "ssl_types.h" +/** + * @brief create a private key object according to input private key + * + * @param ipk - input private key point + * + * @return new private key object point + */ +EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk); + /** * @brief create a private key object * diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 34249ea054..c571865c1e 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -196,12 +196,8 @@ struct ssl_st /* shut things down(0x01 : sent, 0x02 : received) */ int shutdown; - int crt_reload; - CERT *cert; - int ca_reload; - X509 *client_CA; SSL_CTX *ctx; @@ -274,24 +270,20 @@ struct ssl_method_func_st { struct x509_method_st { - int (*x509_new)(X509 *x); + int (*x509_new)(X509 *x, X509 *m_x); void (*x509_free)(X509 *x); int (*x509_load)(X509 *x, const unsigned char *buf, int len); - - void (*x509_unload)(X509 *x); }; struct pkey_method_st { - int (*pkey_new)(EVP_PKEY *pkey); + int (*pkey_new)(EVP_PKEY *pkey, EVP_PKEY *m_pkey); void (*pkey_free)(EVP_PKEY *pkey); int (*pkey_load)(EVP_PKEY *pkey, const unsigned char *buf, int len); - - void (*pkey_unload)(EVP_PKEY *pkey); }; typedef int (*next_proto_cb)(SSL *ssl, unsigned char **out, diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index 5dac46137b..2c72980b07 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -24,6 +24,15 @@ DEFINE_STACK_OF(X509_NAME) +/** + * @brief create a X509 certification object according to input X509 certification + * + * @param ix - input X509 certification point + * + * @return new X509 certification object point + */ +X509* __X509_new(X509 *ix); + /** * @brief create a X509 certification object * diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index 47a7331b7e..cf1d213799 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -42,16 +42,13 @@ OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl); void ssl_pm_set_bufflen(SSL *ssl, int len); -int x509_pm_new(X509 *x); +int x509_pm_new(X509 *x, X509 *m_x); void x509_pm_free(X509 *x); int x509_pm_load(X509 *x, const unsigned char *buffer, int len); -void x509_pm_unload(X509 *x); -void x509_pm_start_ca(X509 *x); -int pkey_pm_new(EVP_PKEY *pkey); -void pkey_pm_free(EVP_PKEY *pkey); -int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len); -void pkey_pm_unload(EVP_PKEY *pkey); +int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pk); +void pkey_pm_free(EVP_PKEY *pk); +int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len); long ssl_pm_get_verify_result(const SSL *ssl); diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c index fd05bc8315..e4fd4d7785 100644 --- a/components/openssl/library/ssl_cert.c +++ b/components/openssl/library/ssl_cert.c @@ -19,23 +19,34 @@ #include "ssl_port.h" /** - * @brief create a certification object include private key object + * @brief create a certification object according to input certification */ -CERT *ssl_cert_new(void) +CERT *__ssl_cert_new(CERT *ic) { CERT *cert; + X509 *ix; + EVP_PKEY *ipk; + cert = ssl_zalloc(sizeof(CERT)); if (!cert) SSL_RET(failed1, "ssl_zalloc\n"); - cert->pkey = EVP_PKEY_new(); - if (!cert->pkey) - SSL_RET(failed2, "EVP_PKEY_new\n"); + if (ic) { + ipk = ic->pkey; + ix = ic->x509; + } else { + ipk = NULL; + ix = NULL; + } - cert->x509 = X509_new(); + cert->pkey = __EVP_PKEY_new(ipk); + if (!cert->pkey) + SSL_RET(failed2, "__EVP_PKEY_new\n"); + + cert->x509 = __X509_new(ix); if (!cert->x509) - SSL_RET(failed3, "X509_new\n"); + SSL_RET(failed3, "__X509_new\n"); return cert; @@ -47,6 +58,14 @@ failed1: return NULL; } +/** + * @brief create a certification object include private key object + */ +CERT *ssl_cert_new(void) +{ + return __ssl_cert_new(NULL); +} + /** * @brief free a certification object */ diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 06bbe270c5..b82d54cd26 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -158,11 +158,11 @@ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) CERT *cert; X509 *client_ca; - if (!method) SSL_RET(go_failed1, "method\n"); + if (!method) SSL_RET(go_failed1, "method:NULL\n"); client_ca = X509_new(); if (!client_ca) - SSL_RET(go_failed1, "sk_X509_NAME_new_null\n"); + SSL_RET(go_failed1, "X509_new\n"); cert = ssl_cert_new(); if (!cert) @@ -170,7 +170,7 @@ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) ctx = (SSL_CTX *)ssl_zalloc(sizeof(SSL_CTX)); if (!ctx) - SSL_RET(go_failed3, "ssl_ctx_new:ctx\n"); + SSL_RET(go_failed3, "ssl_zalloc:ctx\n"); ctx->method = method; ctx->client_CA = client_ca; @@ -244,15 +244,15 @@ SSL *SSL_new(SSL_CTX *ctx) ssl->session = SSL_SESSION_new(); if (!ssl->session) - SSL_RET(failed2, "ssl_zalloc\n"); + SSL_RET(failed2, "SSL_SESSION_new\n"); - ssl->cert = ssl_cert_new(); + ssl->cert = __ssl_cert_new(ctx->cert); if (!ssl->cert) - SSL_RET(failed3, "ssl_cert_new\n"); + SSL_RET(failed3, "__ssl_cert_new\n"); - ssl->client_CA = X509_new(); + ssl->client_CA = __X509_new(ctx->client_CA); if (!ssl->client_CA) - SSL_RET(failed4, "ssl_cert_new\n"); + SSL_RET(failed4, "__X509_new\n"); ssl->ctx = ctx; ssl->method = ctx->method; diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index 042d670ab9..e363b5e46d 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -72,11 +72,11 @@ IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); */ IMPLEMENT_X509_METHOD(X509_method, x509_pm_new, x509_pm_free, - x509_pm_load, x509_pm_unload); + x509_pm_load); /** * @brief get private key object method */ IMPLEMENT_PKEY_METHOD(EVP_PKEY_method, pkey_pm_new, pkey_pm_free, - pkey_pm_load, pkey_pm_unload); + pkey_pm_load); diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 6891b69eb3..573b1f2e8f 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -20,20 +20,24 @@ #include "ssl_port.h" /** - * @brief create a private key object + * @brief create a private key object according to input private key */ -EVP_PKEY* EVP_PKEY_new(void) +EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk) { int ret; EVP_PKEY *pkey; pkey = ssl_zalloc(sizeof(EVP_PKEY)); if (!pkey) - SSL_RET(failed1, "ssl_malloc\n"); + SSL_RET(failed1, "ssl_zalloc\n"); - pkey->method = EVP_PKEY_method(); + if (ipk) { + pkey->method = ipk->method; + } else { + pkey->method = EVP_PKEY_method(); + } - ret = EVP_PKEY_METHOD_CALL(new, pkey); + ret = EVP_PKEY_METHOD_CALL(new, pkey, ipk); if (ret) SSL_RET(failed2, "EVP_PKEY_METHOD_CALL\n"); @@ -45,6 +49,14 @@ failed1: return NULL; } +/** + * @brief create a private key object + */ +EVP_PKEY* EVP_PKEY_new(void) +{ + return __EVP_PKEY_new(NULL); +} + /** * @brief free a private key object */ @@ -105,6 +117,9 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) SSL_ASSERT(ctx); SSL_ASSERT(pkey); + if (ctx->cert->pkey == pkey) + return 1; + if (ctx->cert->pkey) EVP_PKEY_free(ctx->cert->pkey); @@ -118,12 +133,13 @@ int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) */ int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { - SSL_ASSERT(ctx); + SSL_ASSERT(ssl); SSL_ASSERT(pkey); - if (!ssl->ca_reload) - ssl->ca_reload = 1; - else + if (ssl->cert->pkey == pkey) + return 1; + + if (ssl->cert->pkey) EVP_PKEY_free(ssl->cert->pkey); ssl->cert->pkey = pkey; @@ -138,20 +154,20 @@ int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const unsigned char *d, long len) { int ret; - EVP_PKEY *pkey; + EVP_PKEY *pk; - pkey = d2i_PrivateKey(0, &ctx->cert->pkey, &d, len); - if (!pkey) + pk = d2i_PrivateKey(0, NULL, &d, len); + if (!pk) SSL_RET(failed1, "d2i_PrivateKey\n"); - ret = SSL_CTX_use_PrivateKey(ctx, pkey); + ret = SSL_CTX_use_PrivateKey(ctx, pk); if (!ret) SSL_RET(failed2, "SSL_CTX_use_PrivateKey\n"); return 1; failed2: - EVP_PKEY_free(pkey); + EVP_PKEY_free(pk); failed1: return 0; } @@ -163,44 +179,20 @@ int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const unsigned char *d, long len) { int ret; - int reload; - EVP_PKEY *pkey; - CERT *cert; - CERT *old_cert; + EVP_PKEY *pk; - if (!ssl->crt_reload) { - cert = ssl_cert_new(); - if (!cert) - SSL_RET(failed1, "ssl_cert_new\n"); + pk = d2i_PrivateKey(0, NULL, &d, len); + if (!pk) + SSL_RET(failed1, "d2i_PrivateKey\n"); - old_cert = ssl->cert ; - ssl->cert = cert; - - ssl->crt_reload = 1; - - reload = 1; - } else { - reload = 0; - } - - pkey = d2i_PrivateKey(0, &ssl->cert->pkey, &d, len); - if (!pkey) - SSL_RET(failed2, "d2i_PrivateKey\n"); - - ret = SSL_use_PrivateKey(ssl, pkey); + ret = SSL_use_PrivateKey(ssl, pk); if (!ret) - SSL_RET(failed3, "SSL_use_PrivateKey\n"); + SSL_RET(failed2, "SSL_use_PrivateKey\n"); return 1; -failed3: - EVP_PKEY_free(pkey); failed2: - if (reload) { - ssl->cert = old_cert; - ssl_cert_free(cert); - ssl->crt_reload = 0; - } + EVP_PKEY_free(pk); failed1: return 0; } diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index c3fa0b307a..b57cc0dfb9 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -19,9 +19,9 @@ #include "ssl_port.h" /** - * @brief create a X509 certification object + * @brief create a X509 certification object according to input X509 certification */ -X509* X509_new(void) +X509* __X509_new(X509 *ix) { int ret; X509 *x; @@ -30,9 +30,12 @@ X509* X509_new(void) if (!x) SSL_RET(failed1, "ssl_malloc\n"); - x->method = X509_method(); + if (ix) + x->method = ix->method; + else + x->method = X509_method(); - ret = X509_METHOD_CALL(new, x); + ret = X509_METHOD_CALL(new, x, ix); if (ret) SSL_RET(failed2, "x509_new\n"); @@ -44,6 +47,14 @@ failed1: return NULL; } +/** + * @brief create a X509 certification object + */ +X509* X509_new(void) +{ + return __X509_new(NULL); +} + /** * @brief free a X509 certification object */ @@ -78,7 +89,7 @@ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len) ret = X509_METHOD_CALL(load, x, buffer, len); if (ret) - SSL_RET(failed2, "X509_METHOD_CALL\n"); + SSL_RET(failed2, "x509_load\n"); return x; @@ -97,8 +108,10 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) SSL_ASSERT(ctx); SSL_ASSERT(x); - if (ctx->client_CA) - X509_free(ctx->client_CA); + if (ctx->client_CA == x) + return 1; + + X509_free(ctx->client_CA); ctx->client_CA = x; @@ -113,10 +126,10 @@ int SSL_add_client_CA(SSL *ssl, X509 *x) SSL_ASSERT(ssl); SSL_ASSERT(x); - if (!ssl->ca_reload) - ssl->ca_reload = 1; - else - X509_free(ssl->client_CA); + if (ssl->client_CA == x) + return 1; + + X509_free(ssl->client_CA); ssl->client_CA = x; @@ -131,6 +144,11 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) SSL_ASSERT(ctx); SSL_ASSERT(x); + if (ctx->cert->x509 == x) + return 1; + + X509_free(ctx->cert->x509); + ctx->cert->x509 = x; return 1; @@ -141,9 +159,14 @@ int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) */ int SSL_use_certificate(SSL *ssl, X509 *x) { - SSL_ASSERT(ctx); + SSL_ASSERT(ssl); SSL_ASSERT(x); + if (ssl->cert->x509 == x) + return 1; + + X509_free(ssl->cert->x509); + ssl->cert->x509 = x; return 1; @@ -166,20 +189,20 @@ int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d) { int ret; - X509 *cert; + X509 *x; - cert = d2i_X509(&ctx->cert->x509, d, len); - if (!cert) + x = d2i_X509(NULL, d, len); + if (!x) SSL_RET(failed1, "d2i_X509\n"); - ret = SSL_CTX_use_certificate(ctx, cert); + ret = SSL_CTX_use_certificate(ctx, x); if (!ret) SSL_RET(failed2, "SSL_CTX_use_certificate\n"); return 1; failed2: - X509_free(cert); + X509_free(x); failed1: return 0; } @@ -193,42 +216,20 @@ int SSL_use_certificate_ASN1(SSL *ssl, int len, int ret; int reload; X509 *x; - CERT *cert; - CERT *old_cert; + int m = 0; - if (!ssl->crt_reload) { - cert = ssl_cert_new(); - if (!cert) - SSL_RET(failed1, "ssl_cert_new\n"); - - old_cert = ssl->cert ; - ssl->cert = cert; - - ssl->crt_reload = 1; - - reload = 1; - } else { - reload = 0; - } - - x = d2i_X509(&ssl->cert->x509, d, len); + x = d2i_X509(NULL, d, len); if (!x) - SSL_RET(failed2, "d2i_X509\n"); + SSL_RET(failed1, "d2i_X509\n"); ret = SSL_use_certificate(ssl, x); if (!ret) - SSL_RET(failed3, "SSL_use_certificate\n"); + SSL_RET(failed2, "SSL_use_certificate\n"); return 1; -failed3: - X509_free(x); failed2: - if (reload) { - ssl->cert = old_cert; - ssl_cert_free(cert); - ssl->crt_reload = 0; - } + X509_free(x); failed1: return 0; } diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 311c3a4b6f..9f5290cc52 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -78,14 +78,6 @@ int ssl_pm_new(SSL *ssl) const SSL_METHOD *method = ssl->method; - struct x509_pm *ctx_ca = (struct x509_pm *)ssl->ctx->client_CA->x509_pm; - struct x509_pm *ctx_crt = (struct x509_pm *)ssl->ctx->cert->x509->x509_pm; - struct pkey_pm *ctx_pkey = (struct pkey_pm *)ssl->ctx->cert->pkey->pkey_pm; - - struct x509_pm *ssl_ca = (struct x509_pm *)ssl->client_CA->x509_pm; - struct x509_pm *ssl_crt = (struct x509_pm *)ssl->cert->x509->x509_pm; - struct pkey_pm *ssl_pkey = (struct pkey_pm *)ssl->cert->pkey->pkey_pm; - ssl_pm = ssl_zalloc(sizeof(struct ssl_pm)); if (!ssl_pm) SSL_ERR(ret, failed1, "ssl_zalloc\n"); @@ -134,10 +126,6 @@ int ssl_pm_new(SSL *ssl) ssl->ssl_pm = ssl_pm; - ssl_ca->ex_crt = ctx_ca->x509_crt; - ssl_crt->ex_crt = ctx_crt->x509_crt; - ssl_pkey->ex_pkey = ctx_pkey->pkey; - return 0; failed3: @@ -376,7 +364,7 @@ OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl) return state; } -int x509_pm_new(X509 *x) +int x509_pm_new(X509 *x, X509 *m_x) { struct x509_pm *x509_pm; @@ -386,13 +374,19 @@ int x509_pm_new(X509 *x) x->x509_pm = x509_pm; + if (m_x) { + struct x509_pm *m_x509_pm = (struct x509_pm *)m_x->x509_pm; + + x509_pm->ex_crt = m_x509_pm->x509_crt; + } + return 0; failed1: return -1; } -void x509_pm_unload(X509 *x) +void x509_pm_free(X509 *x) { struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; @@ -402,11 +396,6 @@ void x509_pm_unload(X509 *x) ssl_free(x509_pm->x509_crt); x509_pm->x509_crt = NULL; } -} - -void x509_pm_free(X509 *x) -{ - x509_pm_unload(x); ssl_free(x->x509_pm); x->x509_pm = NULL; @@ -450,7 +439,7 @@ failed1: return -1; } -int pkey_pm_new(EVP_PKEY *pkey) +int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pkey) { struct pkey_pm *pkey_pm; @@ -458,14 +447,20 @@ int pkey_pm_new(EVP_PKEY *pkey) if (!pkey_pm) return -1; - pkey->pkey_pm = pkey_pm; + pk->pkey_pm = pkey_pm; + + if (m_pkey) { + struct pkey_pm *m_pkey_pm = (struct pkey_pm *)m_pkey->pkey_pm; + + pkey_pm->ex_pkey = m_pkey_pm->pkey; + } return 0; } -void pkey_pm_unload(EVP_PKEY *pkey) +void pkey_pm_free(EVP_PKEY *pk) { - struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; + struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; if (pkey_pm->pkey) { mbedtls_pk_free(pkey_pm->pkey); @@ -473,21 +468,16 @@ void pkey_pm_unload(EVP_PKEY *pkey) ssl_free(pkey_pm->pkey); pkey_pm->pkey = NULL; } + + ssl_free(pk->pkey_pm); + pk->pkey_pm = NULL; } -void pkey_pm_free(EVP_PKEY *pkey) -{ - pkey_pm_unload(pkey); - - ssl_free(pkey->pkey_pm); - pkey->pkey_pm = NULL; -} - -int pkey_pm_load(EVP_PKEY *pkey, const unsigned char *buffer, int len) +int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) { int ret; unsigned char *load_buf; - struct pkey_pm *pkey_pm = (struct pkey_pm *)pkey->pkey_pm; + struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; if (!pkey_pm->pkey) { pkey_pm->pkey = ssl_malloc(sizeof(mbedtls_pk_context)); From 3882937427e7cd3fe60ee78ff7b5d583cfb84fec Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 27 Sep 2016 10:06:24 +0800 Subject: [PATCH 030/285] components/openssl: add debug message and change verifying mode --- components/openssl/include/internal/ssl_dbg.h | 6 ++-- .../openssl/include/internal/ssl_methods.h | 2 ++ .../openssl/include/internal/ssl_x509.h | 24 ++++++++++++++++ components/openssl/library/ssl_lib.c | 6 ++-- components/openssl/library/ssl_methods.c | 1 - components/openssl/library/ssl_pkey.c | 2 -- components/openssl/library/ssl_stack.c | 8 +++--- components/openssl/library/ssl_x509.c | 3 -- components/openssl/platform/ssl_pm.c | 28 ++++++++++--------- 9 files changed, 51 insertions(+), 29 deletions(-) diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 27a4bc4db5..d6ae47499e 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -19,10 +19,10 @@ extern "C" { #endif -#define SSL_DEBUG_ENBALE 0 +#define SSL_DEBUG_ENBALE 1 #define SSL_DEBUG_LEVEL 0 -#define SSL_ASSERT_ENABLE 0 -#define SSL_DEBUG_LOCATION_ENABLE 0 +#define SSL_ASSERT_ENABLE 1 +#define SSL_DEBUG_LOCATION_ENABLE 1 #if SSL_DEBUG_ENBALE extern int ets_printf(const char *fmt, ...); diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index 9fd9ce9068..7a63b9e949 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -15,6 +15,8 @@ #ifndef _SSL_METHODS_H_ #define _SSL_METHODS_H_ +#include "ssl_types.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index 2c72980b07..b5fea34f1a 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -63,6 +63,30 @@ X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); */ void X509_free(X509 *x); +/** + * @brief set SSL context client CA certification + * + * @param ctx - SSL context point + * @param x - X509 certification point + * + * @return result + * 0 : failed + * 1 : OK + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + +/** + * @brief add CA client certification into the SSL + * + * @param ssl - SSL point + * @param x - X509 certification point + * + * @return result + * 0 : failed + * 1 : OK + */ +int SSL_add_client_CA(SSL *ssl, X509 *x); + #ifdef __cplusplus } #endif diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index b82d54cd26..267d23f25f 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -126,11 +126,11 @@ SSL_SESSION* SSL_SESSION_new(void) session = ssl_zalloc(sizeof(SSL_SESSION)); if (!session) - SSL_RET(failed1); + SSL_RET(failed1, "ssl_zalloc\n"); session->peer = X509_new(); if (!session->peer) - SSL_RET(failed2); + SSL_RET(failed2, "X509_new\n"); return session; @@ -1500,7 +1500,7 @@ void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509 */ void SSL_set_verify(SSL *ssl, int mode, int (*verify_callback)(int, X509_STORE_CTX *)) { - SSL_ASSERT(ctx); + SSL_ASSERT(ssl); ssl->verify_mode = mode; ssl->verify_callback = verify_callback; diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index e363b5e46d..8159511c49 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "ssl_lib.h" #include "ssl_methods.h" #include "ssl_pm.h" diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 573b1f2e8f..20debfbcfc 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -12,9 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "ssl_lib.h" #include "ssl_pkey.h" -#include "ssl_cert.h" #include "ssl_methods.h" #include "ssl_dbg.h" #include "ssl_port.h" diff --git a/components/openssl/library/ssl_stack.c b/components/openssl/library/ssl_stack.c index 46e6f7efd8..4ea40e7259 100644 --- a/components/openssl/library/ssl_stack.c +++ b/components/openssl/library/ssl_stack.c @@ -30,13 +30,13 @@ OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c) OPENSSL_STACK *stack; char **data; - stack = ssl_malloc(sizeof(OPENSSL_STACK)); + stack = ssl_zalloc(sizeof(OPENSSL_STACK)); if (!stack) - SSL_RET(failed1); + SSL_RET(failed1, "ssl_zalloc\n"); - data = ssl_malloc(sizeof(*data) * MIN_NODES); + data = ssl_zalloc(sizeof(*data) * MIN_NODES); if (!data) - SSL_RET(failed2); + SSL_RET(failed2, "ssl_zalloc\n"); stack->data = data; stack->num_alloc = MIN_NODES; diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index b57cc0dfb9..d060419e6a 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -13,7 +13,6 @@ // limitations under the License. #include "ssl_x509.h" -#include "ssl_cert.h" #include "ssl_methods.h" #include "ssl_dbg.h" #include "ssl_port.h" @@ -214,9 +213,7 @@ int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d) { int ret; - int reload; X509 *x; - int m = 0; x = d2i_X509(NULL, d, len); if (!x) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 9f5290cc52..151adbaf81 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -112,7 +112,7 @@ int ssl_pm_new(SSL *ssl) else version = MBEDTLS_SSL_MINOR_VERSION_0; - mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + //mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); @@ -169,7 +169,7 @@ static int ssl_pm_reload_crt(SSL *ssl) if (ssl->verify_mode == SSL_VERIFY_PEER) mode = MBEDTLS_SSL_VERIFY_REQUIRED; else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT) - mode = MBEDTLS_SSL_VERIFY_NONE; + mode = MBEDTLS_SSL_VERIFY_OPTIONAL; else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE) mode = MBEDTLS_SSL_VERIFY_UNSET; else @@ -370,7 +370,7 @@ int x509_pm_new(X509 *x, X509 *m_x) x509_pm = ssl_zalloc(sizeof(struct x509_pm)); if (!x509_pm) - SSL_RET(failed1); + SSL_RET(failed1, "ssl_zalloc\n"); x->x509_pm = x509_pm; @@ -408,27 +408,28 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; if (!x509_pm->x509_crt) { - x509_pm->x509_crt = ssl_malloc(sizeof(mbedtls_x509_crt)); + x509_pm->x509_crt = ssl_zalloc(sizeof(mbedtls_x509_crt)); if (!x509_pm->x509_crt) - SSL_RET(failed1); + SSL_RET(failed1, "ssl_zalloc\n"); } load_buf = ssl_malloc(len + 1); if (!load_buf) - SSL_RET(failed2); + SSL_RET(failed2, "ssl_malloc\n"); ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; + mbedtls_x509_crt_init(x509_pm->x509_crt); + if (x509_pm->x509_crt) mbedtls_x509_crt_free(x509_pm->x509_crt); - mbedtls_x509_crt_init(x509_pm->x509_crt); ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len); ssl_free(load_buf); if (ret) - SSL_RET(failed2); + SSL_RET(failed2, "mbedtls_x509_crt_parse, return [-0x%x]\n", -ret); return 0; @@ -480,27 +481,28 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; if (!pkey_pm->pkey) { - pkey_pm->pkey = ssl_malloc(sizeof(mbedtls_pk_context)); + pkey_pm->pkey = ssl_zalloc(sizeof(mbedtls_pk_context)); if (!pkey_pm->pkey) - SSL_RET(failed1); + SSL_RET(failed1, "ssl_zalloc\n"); } load_buf = ssl_malloc(len + 1); if (!load_buf) - SSL_RET(failed2); + SSL_RET(failed2, "ssl_malloc\n"); ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; + mbedtls_pk_init(pkey_pm->pkey); + if (pkey_pm->pkey) mbedtls_pk_free(pkey_pm->pkey); - mbedtls_pk_init(pkey_pm->pkey); ret = mbedtls_pk_parse_key(pkey_pm->pkey, load_buf, len, NULL, 0); ssl_free(load_buf); if (ret) - SSL_RET(failed2); + SSL_RET(failed2, "mbedtls_pk_parse_key, return [-0x%x]\n", -ret); return 0; From 652ddae44f7fe78ab45f7efc524204f2e6ad5ee8 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 27 Sep 2016 14:28:39 +0800 Subject: [PATCH 031/285] components/openssl: change low-level certification loading sequence --- components/openssl/platform/ssl_pm.c | 34 ++++++++++++++++++---------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 151adbaf81..9df8b6481e 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -25,6 +25,12 @@ #include "mbedtls/error.h" #include "mbedtls/certs.h" +#if 0 + #define DEBUG_LOAD_BUF_STRING(str) SSL_DEBUG(1, "%s\n", str) +#else + #define DEBUG_LOAD_BUF_STRING(str) +#endif + struct ssl_pm { /* local socket file description */ @@ -407,10 +413,13 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) unsigned char *load_buf; struct x509_pm *x509_pm = (struct x509_pm *)x->x509_pm; + if (x509_pm->x509_crt) + mbedtls_x509_crt_free(x509_pm->x509_crt); + if (!x509_pm->x509_crt) { - x509_pm->x509_crt = ssl_zalloc(sizeof(mbedtls_x509_crt)); + x509_pm->x509_crt = ssl_malloc(sizeof(mbedtls_x509_crt)); if (!x509_pm->x509_crt) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_malloc\n"); } load_buf = ssl_malloc(len + 1); @@ -420,12 +429,11 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; - mbedtls_x509_crt_init(x509_pm->x509_crt); + DEBUG_LOAD_BUF_STRING(load_buf); - if (x509_pm->x509_crt) - mbedtls_x509_crt_free(x509_pm->x509_crt); + mbedtls_x509_crt_init(x509_pm->x509_crt); - ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len); + ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1); ssl_free(load_buf); if (ret) @@ -480,10 +488,13 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) unsigned char *load_buf; struct pkey_pm *pkey_pm = (struct pkey_pm *)pk->pkey_pm; + if (pkey_pm->pkey) + mbedtls_pk_free(pkey_pm->pkey); + if (!pkey_pm->pkey) { - pkey_pm->pkey = ssl_zalloc(sizeof(mbedtls_pk_context)); + pkey_pm->pkey = ssl_malloc(sizeof(mbedtls_pk_context)); if (!pkey_pm->pkey) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_malloc\n"); } load_buf = ssl_malloc(len + 1); @@ -493,12 +504,11 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; + DEBUG_LOAD_BUF_STRING(load_buf); + mbedtls_pk_init(pkey_pm->pkey); - if (pkey_pm->pkey) - mbedtls_pk_free(pkey_pm->pkey); - - ret = mbedtls_pk_parse_key(pkey_pm->pkey, load_buf, len, NULL, 0); + ret = mbedtls_pk_parse_key(pkey_pm->pkey, load_buf, len + 1, NULL, 0); ssl_free(load_buf); if (ret) From 877adaab7a0365ab2a85f9df8073332a38fd9232 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 27 Sep 2016 18:50:57 +0800 Subject: [PATCH 032/285] components/openssl: add some function description --- .../openssl/include/internal/ssl_pkey.h | 15 +++++++++++++ .../openssl/include/internal/ssl_x509.h | 14 +++++++++++++ components/openssl/include/openssl/ssl.h | 21 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/components/openssl/include/internal/ssl_pkey.h b/components/openssl/include/internal/ssl_pkey.h index f4da041681..e790fcc995 100644 --- a/components/openssl/include/internal/ssl_pkey.h +++ b/components/openssl/include/internal/ssl_pkey.h @@ -64,6 +64,21 @@ EVP_PKEY* d2i_PrivateKey(int type, */ void EVP_PKEY_free(EVP_PKEY *x); +/** + * @brief load private key into the SSL + * + * @param type - private key type + * @param ssl - SSL point + * @param len - data bytes + * @param d - data point + * + * @return result + * 0 : failed + * 1 : OK + */ + int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const unsigned char *d, long len); + + #ifdef __cplusplus } #endif diff --git a/components/openssl/include/internal/ssl_x509.h b/components/openssl/include/internal/ssl_x509.h index b5fea34f1a..840fbf1ec1 100644 --- a/components/openssl/include/internal/ssl_x509.h +++ b/components/openssl/include/internal/ssl_x509.h @@ -87,6 +87,20 @@ int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); */ int SSL_add_client_CA(SSL *ssl, X509 *x); +/** + * @brief load certification into the SSL + * + * @param ssl - SSL point + * @param len - data bytes + * @param d - data point + * + * @return result + * 0 : failed + * 1 : OK + * + */ +int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); + #ifdef __cplusplus } #endif diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 3e8e88e67c..1d115214fd 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -21,6 +21,7 @@ #include "platform/ssl_port.h" #include "internal/ssl_x509.h" +#include "internal/ssl_pkey.h" /* { @@ -426,6 +427,26 @@ const char *SSL_get_version(const SSL *ssl); */ int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); +/** + * @brief get the bytes numbers which are to be read + * + * @param ssl - SSL point + * + * @return bytes number + */ +int SSL_pending(const SSL *ssl); + +/** + * @brief check if SSL want nothing + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_nothing(const SSL *ssl); + /** * @brief get the SSL context current method * From 6941b5871a73086786191a9d73a21d8c64dbec82 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 27 Sep 2016 18:52:31 +0800 Subject: [PATCH 033/285] components/openssl: add OpenSSL APIs description --- components/openssl/OpenSSL_APIs.rst | 688 ++++++++++++++++++++++++++++ 1 file changed, 688 insertions(+) create mode 100644 components/openssl/OpenSSL_APIs.rst diff --git a/components/openssl/OpenSSL_APIs.rst b/components/openssl/OpenSSL_APIs.rst new file mode 100644 index 0000000000..e130d8398b --- /dev/null +++ b/components/openssl/OpenSSL_APIs.rst @@ -0,0 +1,688 @@ +OpenSSL APIs +====================== + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the SSLV2.3 version SSL context client method + */ +const SSL_METHOD* SSLv23_client_method(void); + + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the TLSV1.0 version SSL context client method + */ +const SSL_METHOD* TLSv1_client_method(void); + + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the SSLV1.0 version SSL context client method + */ +const SSL_METHOD* SSLv3_client_method(void); + + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the TLSV1.1 version SSL context client method + */ +const SSL_METHOD* TLSv1_1_client_method(void); + + +/** + * @brief create the target SSL context client method + * + * @param none + * + * @return the TLSV1.2 version SSL context client method + */ +const SSL_METHOD* TLSv1_2_client_method(void); + + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the SSLV2.3 version SSL context server method + */ +const SSL_METHOD* SSLv23_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLSV1.1 version SSL context server method + */ +const SSL_METHOD* TLSv1_1_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLSV1.2 version SSL context server method + */ +const SSL_METHOD* TLSv1_2_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the TLSV1.0 version SSL context server method + */ +const SSL_METHOD* TLSv1_server_method(void); + +/** + * @brief create the target SSL context server method + * + * @param none + * + * @return the SSLV3.0 version SSL context server method + */ +const SSL_METHOD* SSLv3_server_method(void); + +/** + * @brief create a SSL context + * + * @param method - the SSL context method point + * + * @return the context point + */ +SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); + +/** + * @brief free a SSL context + * + * @param method - the SSL context point + * + * @return none + */ +void SSL_CTX_free(SSL_CTX *ctx); + +/** + * @brief set the SSL context version + * + * @param ctx - SSL context point + * @param meth - SSL method point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); + +/** + * @brief get the SSL context current method + * + * @param ctx - SSL context point + * + * @return the SSL context current method + */ +const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); + +/** + * @brief create a SSL + * + * @param ctx - the SSL context point + * + * @return the SSL point + */ +SSL* SSL_new(SSL_CTX *ctx); + +/** + * @brief free the SSL + * + * @param ssl - the SSL point + * + * @return none + */ +void SSL_free(SSL *ssl); + +/** + * @brief perform the SSL handshake + * + * @param ssl - SSL point + * + * @return result + * 1 : OK + * 0 : failed + * -1 : a error catch + */ +int SSL_do_handshake(SSL *ssl); + +/** + * @brief connect to the remote SSL server + * + * @param ssl - the SSL point + * + * @return result + * 1 : OK + * -1 : failed + */ +int SSL_connect(SSL *ssl); + +/** + * @brief accept the remote connection + * + * @param ssl - the SSL point + * + * @return result + * 1 : OK + * -1 : failed + */ +int SSL_accept(SSL *ssl); + +/** + * @brief shutdown the connection + * + * @param ssl - the SSL point + * + * @return result + * 1 : OK + * 0 : shutdown is not finished + * -1 : an error catch + */ +int SSL_shutdown(SSL *ssl); + +/** + * @brief reset the SSL + * + * @param ssl - SSL point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_clear(SSL *ssl); + +/** + * @brief read data from to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the received data buffer point + * @param len - the received data length + * + * @return result + * > 0 : OK, and return received data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_read(SSL *ssl, void *buffer, int len); + +/** + * @brief send the data to remote + * + * @param ssl - the SSL point which has been connected + * @param buffer - the send data buffer point + * @param len - the send data length + * + * @return result + * > 0 : OK, and return sent data bytes + * = 0 : connection is closed + * < 0 : an error catch + */ +int SSL_write(SSL *ssl, const void *buffer, int len); + +/** + * @brief get SSL context of the SSL + * + * @param ssl - SSL point + * + * @return SSL context + */ +SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); + +/** + * @brief get SSL shutdown mode + * + * @param ssl - SSL point + * + * @return shutdown mode + */ +int SSL_get_shutdown(const SSL *ssl); + +/** + * @brief set SSL shutdown mode + * + * @param ssl - SSL point + * @param mode - shutdown mode + * + * @return none + */ +void SSL_set_shutdown(SSL *ssl, int mode); + +/** + * @brief get the SSL current method + * + * @param ssl - SSL point + * + * @return the SSL current method + */ +const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); + +/** + * @brief set the SSL method + * + * @param ssl - SSL point + * @param meth - SSL method point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); + +/** + * @brief get the bytes numbers which are to be read + * + * @param ssl - SSL point + * + * @return bytes number + */ +int SSL_pending(const SSL *ssl); + +/** + * @brief check if some data can be read + * + * @param ssl - SSL point + * + * @return + * 1 : there are bytes to be read + * 0 : no data + */ +int SSL_has_pending(const SSL *ssl); + +/** + * @brief get the socket handle of the SSL + * + * @param ssl - SSL point + * + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_fd(const SSL *ssl); + +/** + * @brief get the read only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_rfd(const SSL *ssl); + +/** + * @brief get the write only socket handle of the SSL + * + * @param ssl - SSL point + * + * @return result + * >= 0 : yes, and return socket handle + * < 0 : a error catch + */ +int SSL_get_wfd(const SSL *ssl); + +/** + * @brief bind the socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_set_fd(SSL *ssl, int fd); + +/** + * @brief bind the read only socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_set_rfd(SSL *ssl, int fd); + +/** + * @brief bind the write only socket file description into the SSL + * + * @param ssl - the SSL point + * @param fd - socket handle + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_set_wfd(SSL *ssl, int fd); + +/** + * @brief get SSL version + * + * @param ssl - SSL point + * + * @return SSL version + */ +int SSL_version(const SSL *ssl); + +/** + * @brief get the SSL current version + * + * @param ssl - SSL point + * + * @return the version string + */ +const char *SSL_get_version(const SSL *ssl); + +/** + * @brief get the SSL state + * + * @param ssl - SSL point + * + * @return SSL state + */ +OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); + +/** + * @brief get alert description string + * + * @param value - alert value + * + * @return alert description string + */ +const char *SSL_alert_desc_string(int value); + +/** + * @brief get alert description long string + * + * @param value - alert value + * + * @return alert description long string + */ +const char *SSL_alert_desc_string_long(int value); + +/** + * @brief get alert type string + * + * @param value - alert value + * + * @return alert type string + */ +const char *SSL_alert_type_string(int value); + +/** + * @brief get alert type long string + * + * @param value - alert value + * + * @return alert type long string + */ +const char *SSL_alert_type_string_long(int value); + +/** + * @brief get the state string where SSL is reading + * + * @param ssl - SSL point + * + * @return state string + */ +const char *SSL_rstate_string(SSL *ssl); + +/** + * @brief get the statement long string where SSL is reading + * + * @param ssl - SSL point + * + * @return statement long string + */ +const char *SSL_rstate_string_long(SSL *ssl); + +/** + * @brief get SSL statement string + * + * @param ssl - SSL point + * + * @return SSL statement string + */ +char *SSL_state_string(const SSL *ssl); + +/** + * @brief get SSL statement long string + * + * @param ssl - SSL point + * + * @return SSL statement long string + */ +char *SSL_state_string_long(const SSL *ssl); + +/** + * @brief get SSL error code + * + * @param ssl - SSL point + * @param ret_code - SSL return code + * + * @return SSL error number + */ +int SSL_get_error(const SSL *ssl, int ret_code); + +/** + * @brief load a character certification context into system context. If '*cert' is pointed to the + * certification, then load certification into it. Or create a new X509 certification object + * + * @param cert - a point pointed to X509 certification + * @param buffer - a point pointed to the certification context memory point + * @param length - certification bytes + * + * @return X509 certification object point + */ +X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); + +/** + * @brief add CA client certification into the SSL + * + * @param ssl - SSL point + * @param x - CA certification point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_add_client_CA(SSL *ssl, X509 *x); + +/** + * @brief add CA client certification into the SSL context + * + * @param ctx - SSL context point + * @param x - CA certification point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + +/** + * @brief get the SSL certification point + * + * @param ssl - SSL point + * + * @return SSL certification point + */ +X509 *SSL_get_certificate(const SSL *ssl); + +/** + * @brief get the verifying result of the SSL certification + * + * @param ssl - the SSL point + * + * @return the result of verifying + */ +long SSL_get_verify_result(const SSL *ssl); + +/** + * @brief These functions load the certification into the SSL_CTX or SSL object + * + * @param ctx - the SSL context point + * @param pkey - certification object point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); + +/** + * @brief load the ASN1 certification into SSL context + * + * @param ctx - SSL context point + * @param len - certification length + * @param d - data point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); + +/** + * @brief These functions load the private key into the SSL_CTX or SSL object + * + * @param ctx - the SSL context point + * @param pkey - private key object point + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); + +/** + * @brief load the ASN1 private key into SSL context + * + * @param ctx - SSL context point + * @param d - data point + * @param len - private key length + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len); + +/** + * @brief load the RSA ASN1 private key into SSL context + * + * @param ctx - SSL context point + * @param d - data point + * @param len - RSA private key length + * + * @return result + * 1 : OK + * 0 : failed + */ +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); + +/** + * @brief load certification into the SSL + * + * @param ssl - SSL point + * @param len - data bytes + * @param d - data point + * + * @return result + * 0 : failed + * 1 : OK + * + */ +int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); + +/** + * @brief get peer certification + * + * @param ssl - SSL point + * + * @return certification + */ +X509 *SSL_get_peer_certificate(const SSL *ssl); + +/** + * @brief set the SSL context read buffer length + * + * @param ctx - SSL context point + * @param len - read buffer length + * + * @return none + */ +void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); + +/** + * @brief set the SSL read buffer length + * + * @param ssl - SSL point + * @param len - read buffer length + * + * @return none + */ +void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); + +/** + * @brief get the SSL specifical statement + * + * @param ssl - SSL point + * + * @return specifical statement + */ +int SSL_want(const SSL *ssl); + +/** + * @brief check if SSL want nothing + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_nothing(const SSL *ssl); + +/** + * @brief check if SSL want to read + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_read(const SSL *ssl); + +/** + * @brief check if SSL want to write + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_write(const SSL *ssl); From 9e20d31f898ef561c908d3db14887a8e68dd9bb4 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 27 Sep 2016 19:06:07 +0800 Subject: [PATCH 034/285] components/openssl: fix extra certification loading --- components/openssl/platform/ssl_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 9df8b6481e..4bc631382f 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -186,7 +186,7 @@ static int ssl_pm_reload_crt(SSL *ssl) if (ca_pm->x509_crt) { mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); } else if (ca_pm->ex_crt) { - mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->x509_crt, NULL); + mbedtls_ssl_conf_ca_chain(&ssl_pm->conf, ca_pm->ex_crt, NULL); } if (crt_pm->x509_crt && pkey_pm->pkey) { From 9c4e43a3a5becc441642edbbbf06b90a06bfa5eb Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 28 Sep 2016 16:46:27 +0800 Subject: [PATCH 035/285] components/openssl: optimize the OpenSSL APIs brief document 1. change document name 2. change function introduction template --- components/openssl/OpenSSL-APIs.rst | 1478 +++++++++++++++++++++++++++ components/openssl/OpenSSL_APIs.rst | 688 ------------- 2 files changed, 1478 insertions(+), 688 deletions(-) create mode 100644 components/openssl/OpenSSL-APIs.rst delete mode 100644 components/openssl/OpenSSL_APIs.rst diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst new file mode 100644 index 0000000000..ff91d2ebb7 --- /dev/null +++ b/components/openssl/OpenSSL-APIs.rst @@ -0,0 +1,1478 @@ +OpenSSL-APIs +====================== + +Chapter 1. SSL Context Method Create +Chapter 2. SSL Context Fucntion +Chapter 3. SSL Fucntion +Chapter 4. SSL X509 Certification and Private Key Function + +====================== +Chapter 1. SSL Context Method Create + +1.1 const SSL_METHOD* SSLv23_client_method(void); + + Arguments : none + + Return : SSLV2 and 3 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv23_client_method(); + + ... + } + + +1.2 const SSL_METHOD* TLSv1_client_method(void); + + Arguments : none + + Return : TLSV1.0 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_client_method(); + + ... + } + + +1.3 const SSL_METHOD* SSLv3_client_method(void); + + Arguments : none + + Return : SSLV3.0 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv3_client_method(); + + ... + } + + +1.4 const SSL_METHOD* TLSv1_1_client_method(void); + + Arguments : none + + Return : TLSV1.1 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_1_client_method(); + + ... + } + + +1.5 const SSL_METHOD* TLSv1_2_client_method(void); + + Arguments : none + + Return : TLSV1.2 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_2_client_method(); + + ... + } + + +1.6 const SSL_METHOD* SSLv23_server_method(void); + + Arguments : none + + Return : SSLV2 and 3 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv23_server_method(); + + ... + } + + +1.7 const SSL_METHOD* TLSv1_1_server_method(void); + + Arguments : none + + Return : TLSV1.1 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_1_server_method(); + + ... + } + + +1.8 const SSL_METHOD* TLSv1_2_server_method(void); + + Arguments : none + + Return : TLSV1.2 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_2_server_method(); + + ... + } + + +1.9 const SSL_METHOD* TLSv1_server_method(void); + + Arguments : none + + Return : TLSV1.0 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_server_method(); + + ... + } + + +1.10 const SSL_METHOD* SSLv3_server_method(void); + + Arguments : none + + Return : SSLV3.0 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv3_server_method(); + + ... + } + + +====================== +Chapter 2. SSL Context Fucntion + +2.1 SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); + + Arguments : method - the SSL context method point + + Return : context point + + Description : create a SSL context + + Example : + + void example(void) + { + SSL_CTX *ctx = SSL_CTX_new(SSLv3_server_method()); + + ... + } + + +2.2 void SSL_CTX_free(SSL_CTX *ctx); + + Arguments : ctx - the SSL context point + + Return : none + + Description : free a SSL context + + Example : + + void example(void) + { + SSL_CTX *ctx; + + ... ... + + SSL_CTX_free(ctx); + } + + +2.3 int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); + + Arguments : ctx - SSL context point + meth - SSL method point + + Return : result + 1 : OK + 0 : failed + + Description : set the SSL context version + + Example : + + void example(void) + { + SSL_CTX *ctx; + const SSL_METHOD *meth; + + ... ... + + SSL_CTX_set_ssl_version(ctx, meth); + } + + +2.4 const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); + + Arguments : ctx - SSL context point + + Return : SSL context method + + Description : get the SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method; + SSL_CTX *ctx; + + ... ... + + method = SSL_CTX_get_ssl_method(ctx); + } + + +====================== +Chapter 3. SSL Fucntion + +3.1 SSL* SSL_new(SSL_CTX *ctx); + + Arguments : ctx - SSL context point + + Return : SSL method + + Description : create a SSL + + Example : + + void example(void) + { + SSL *ssl; + SSL_CTX *ctx; + + ... ... + + ssl = SSL_new(ctx); + } + + +3.2 void SSL_free(SSL *ssl); + + Arguments : ssl - SSL point + + Return : none + + Description : free SSL + + Example : + + void example(void) + { + SSL *ssl; + + ... ... + + SSL_free(ssl); + } + + +3.3 int SSL_do_handshake(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : perform the SSL handshake + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_do_handshake(ssl); + } + + +3.4 int SSL_connect(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : connect to the remote SSL server + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_connect(ssl); + } + + +3.5 int SSL_accept(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : accept the remote connection + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_accept(ssl); + } + + +3.6 int SSL_shutdown(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : shutdown the connection + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_shutdown(ssl); + } + + +3.7 int SSL_clear(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed + + Description : shutdown the connection + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_clear(ssl); + } + + +3.8 int SSL_read(SSL *ssl, void *buffer, int len); + + Arguments : ssl - point + buffer - data buffer point + len - data length + + Return : result + > 0 : OK, and return received data bytes + = 0 : no data received or connection is closed + < 0 : an error catch + + Description : read data from remote + + Example : + + void example(void) + { + SSL *ssl; + char *buf; + int len; + int ret; + + ... ... + + ret = SSL_read(ssl, buf, len); + } + +3.9 int SSL_write(SSL *ssl, const void *buffer, int len); + + Arguments : ssl - SSL point + buffer - data buffer point + len - data length + + Return : result + > 0 : OK, and return received data bytes + = 0 : no data sent or connection is closed + < 0 : an error catch + + Description : send the data to remote + + Example : + + void example(void) + { + SSL *ssl; + char *buf; + int len; + int ret; + + ... ... + + ret = SSL_write(ssl, buf, len); + } + + +3.10 SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL context + + Description : get SSL context of the SSL + + Example : + + void example(void) + { + SSL *ssl; + SSL_CTX *ctx; + + ... ... + + ctx = SSL_get_SSL_CTX(ssl); + } + + +3.11 int SSL_get_shutdown(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : shutdown mode + + Description : get SSL shutdown mode + + Example : + + void example(void) + { + SSL *ssl; + int mode; + + ... ... + + mode = SSL_get_SSL_CTX(ssl); + } + + +3.12 void SSL_set_shutdown(SSL *ssl, int mode); + + Arguments : ssl - SSL point + + Return : shutdown mode + + Description : set SSL shutdown mode + + Example : + + void example(void) + { + SSL *ssl; + int mode = 0; + + ... ... + + SSL_set_shutdown(ssl, mode); + } + + +3.13 const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL method + + Description : set SSL shutdown mode + + Example : + + void example(void) + { + SSL *ssl; + const SSL_METHOD *method; + + ... ... + + method = SSL_get_ssl_method(ssl); + } + + +3.14 int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); + + Arguments : ssl - SSL point + meth - SSL method point + + Return : result + 1 : OK + 0 : failed + + Description : set the SSL method + + Example : + + void example(void) + { + int ret; + SSL *ssl; + const SSL_METHOD *method; + + ... ... + + ret = SSL_set_ssl_method(ssl, method); + } + + +3.15 int SSL_pending(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : data bytes + + Description : get received data bytes + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_pending(ssl); + } + + +3.16 int SSL_has_pending(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : Yes + 0 : No + + Description : check if data is received + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_has_pending(ssl); + } + + +3.17 int SSL_get_fd(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + >= 0 : socket id + < 0 : a error catch + + Description : get the socket of the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_get_fd(ssl); + } + + +3.18 int SSL_get_rfd(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + >= 0 : socket id + < 0 : a error catch + + Description : get the read only socket of the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_get_rfd(ssl); + } + + +3.19 int SSL_get_wfd(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + >= 0 : socket id + < 0 : a error catch + + Description : get the write only socket of the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_get_wfd(ssl); + } + + +3.20 int SSL_set_fd(SSL *ssl, int fd); + + Arguments : ssl - SSL point + fd - socket id + + Return : result + 1 : OK + 0 : failed + + Description : set socket to SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + int socket; + + ... ... + + ret = SSL_set_fd(ssl, socket); + } + + +3.21 int SSL_set_rfd(SSL *ssl, int fd); + + Arguments : ssl - SSL point + fd - socket id + + Return : result + 1 : OK + 0 : failed + + Description : set read only socket to SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + int socket; + + ... ... + + ret = SSL_set_rfd(ssl, socket); + } + + +3.22 int SSL_set_wfd(SSL *ssl, int fd); + + Arguments : ssl - SSL point + fd - socket id + + Return : result + 1 : OK + 0 : failed + + Description : set write only socket to SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + int socket; + + ... ... + + ret = SSL_set_wfd(ssl, socket); + } + + +3.23 int SSL_version(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL version + + Description : get SSL version + + Example : + + void example(void) + { + int version; + SSL *ssl; + + ... ... + + version = SSL_version(ssl); + } + + +3.24 const char *SSL_get_version(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL version string + + Description : get the SSL current version string + + Example : + + void example(void) + { + char *version; + SSL *ssl; + + ... ... + + version = SSL_get_version(ssl); + } + + +3.25 OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL state + + Description : get the SSL state + + Example : + + void example(void) + { + OSSL_HANDSHAKE_STATE state; + SSL *ssl; + + ... ... + + state = SSL_get_state(ssl); + } + + +3.26 const char *SSL_alert_desc_string(int value); + + Arguments : value - SSL description + + Return : alert value string + + Description : get alert description string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_desc_string(val); + } + + +3.27 const char *SSL_alert_desc_string_long(int value); + + Arguments : value - SSL description + + Return : alert value long string + + Description : get alert description long string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_desc_string_long(val); + } + + +3.28 const char *SSL_alert_type_string(int value); + + Arguments : value - SSL type description + + Return : alert type string + + Description : get alert type string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_type_string(val); + } + + +3.29 const char *SSL_alert_type_string_long(int value); + + Arguments : value - SSL type description + + Return : alert type long string + + Description : get alert type long string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_type_string_long(val); + } + +3.30 const char *SSL_rstate_string(SSL *ssl); + + Arguments : ssl - SSL point + + Return : state string + + Description : get the state string where SSL is reading + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_rstate_string(ssl); + } + + +3.31 const char *SSL_rstate_string_long(SSL *ssl); + + Arguments : ssl - SSL point + + Return : state long string + + Description : get the state long string where SSL is reading + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_rstate_string_long(ssl); + } + + +3.32 char *SSL_state_string(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : state string + + Description : get the state string + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_state_string(ssl); + } + + +3.33 char *SSL_state_string_long(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : state long string + + Description : get the state long string + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_state_string(ssl); + } + + +3.34 int SSL_get_error(const SSL *ssl, int ret_code); + + Arguments : ssl - SSL point + ret_code - SSL return code + + Return : SSL error number + + Description : get SSL error code + + Example : + + void example(void) + { + SSL *ssl; + int ret; + int err; + + ... ... + + err = SSL_get_error(ssl, ret); + } + +3.35 void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); + + Arguments : ctx - SSL context point + len - read buffer length + + Return : none + + Description : set the SSL context read buffer length + + Example : + + void example(void) + { + SSL_CTX *ctx; + size_t len; + + ... ... + + SSL_CTX_set_default_read_buffer_len(ctx, len); + } + + +3.36 void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); + + Arguments : ssl - SSL point + len - read buffer length + + Return : none + + Description : set the SSL read buffer length + + Example : + + void example(void) + { + SSL *ssl; + size_t len; + + ... ... + + SSL_set_default_read_buffer_len(ctx, len); + } + + +3.37 int SSL_want(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : specifical statement + + Description : get the SSL specifical statement + + Example : + + void example(void) + { + SSL *ssl; + int state; + + ... ... + + state = SSL_want(ssl); + } + + +3.38 int SSL_want_nothing(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 0 : false + 1 : true + + Description : check if SSL want nothing + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_want(ssl); + } + + +3.39 int SSL_want_read(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 0 : false + 1 : true + + Description : check if SSL want to read + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_want_read(ssl); + } + + +3.40 int SSL_want_write(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 0 : false + 1 : true + + Description : check if SSL want to write + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_want_write(ssl); + } + +====================== +Chapter 4. SSL X509 Certification and Private Key Function + +4.1 X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); + + Arguments : cert - a point pointed to X509 certification + buffer - a point pointed to the certification context memory point + length - certification bytes + + Return : X509 certification object point + + Description : load a character certification context into system context. If '*cert' is pointed to the + certification, then load certification into it. Or create a new X509 certification object + + Example : + + void example(void) + { + X509 *new; + X509 *cert; + unsigned char *buffer; + long len; + ... ... + + new = d2i_X509(&cert, buffer, len); + } + + +4.2 int SSL_add_client_CA(SSL *ssl, X509 *x); + + Arguments : ssl - SSL point + x - CA certification point + + Return : result + 1 : OK + 0 : failed + + Description : add CA client certification into the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + X509 *new; + + ... ... + + ret = SSL_add_client_CA(ssl, new); + } + + +4.3 int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + + Arguments : ctx - SSL context point + x - CA certification point + + Return : result + 1 : OK + 0 : failed + + Description : add CA client certification into the SSL context + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + X509 *new; + + ... ... + + ret = SSL_add_clSSL_CTX_add_client_CAient_CA(ctx, new); + } + + +4.4 X509 *SSL_get_certificate(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL certification point + + Description : get the SSL certification point + + Example : + + void example(void) + { + SSL *ssl; + X509 *cert; + + ... ... + + cert = SSL_get_certificate(ssl); + } + + +4.5 long SSL_get_verify_result(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : the result of verifying + + Description : get the verifying result of the SSL certification + + Example : + + void example(void) + { + SSL *ssl; + long ret; + + ... ... + + ret = SSL_get_verify_result(ssl); + } + + +4.6 int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); + + Arguments : ctx - the SSL context point + pkey - certification object point + + Return : result + 1 : OK + 0 : failed + + Description : load the certification into the SSL_CTX or SSL object + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx + X509 *new; + + ... ... + + ret = SSL_CTX_use_certificate(ctx, new); + } + + +4.7 int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); + + Arguments : ctx - SSL context point + len - certification length + d - data point + + Return : result + 1 : OK + 0 : failed + + Description : load the ASN1 certification into SSL context + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + const unsigned char *buf; + int len; + + ... ... + + ret = SSL_CTX_use_certificate_ASN1(ctx, len, buf); + } + + +4.8 int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); + + Arguments : ctx - SSL context point + pkey - private key object point + + Return : result + 1 : OK + 0 : failed + + Description : load the private key into the context object + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + EVP_PKEY *pkey; + + ... ... + + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + } + + +4.9 int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len); + + Arguments : ctx - SSL context point + d - data point + len - private key length + + Return : result + 1 : OK + 0 : failed + + Description : load the ASN1 private key into SSL context + + Example : + + void example(void) + { + int ret; + int pk; + SSL_CTX *ctx; + const unsigned char *buf; + long len; + + ... ... + + ret = SSL_CTX_use_PrivateKey_ASN1(pk, ctx, buf, len); + } + + +4.10 int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); + + Arguments : ctx - SSL context point + d - data point + len - private key length + + Return : result + 1 : OK + 0 : failed + + Description : load the RSA ASN1 private key into SSL context + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + const unsigned char *buf; + long len; + + ... ... + + ret = SSL_CTX_use_RSAPrivateKey_ASN1(ctx, buf, len); + } + + +4.11 int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); + + Arguments : ssl - SSL point + len - data bytes + d - data point + + Return : result + 1 : OK + 0 : failed + + Description : load certification into the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + const unsigned char *buf; + long len; + + ... ... + + ret = SSL_use_certificate_ASN1(ssl, len, buf); + } + + +4.12 X509 *SSL_get_peer_certificate(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : peer certification + + Description : get peer certification + + Example : + + void example(void) + { + SSL *ssl; + X509 *peer; + + ... ... + + peer = SSL_get_peer_certificate(ssl); + } + +====================== +END \ No newline at end of file diff --git a/components/openssl/OpenSSL_APIs.rst b/components/openssl/OpenSSL_APIs.rst deleted file mode 100644 index e130d8398b..0000000000 --- a/components/openssl/OpenSSL_APIs.rst +++ /dev/null @@ -1,688 +0,0 @@ -OpenSSL APIs -====================== - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the SSLV2.3 version SSL context client method - */ -const SSL_METHOD* SSLv23_client_method(void); - - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the TLSV1.0 version SSL context client method - */ -const SSL_METHOD* TLSv1_client_method(void); - - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the SSLV1.0 version SSL context client method - */ -const SSL_METHOD* SSLv3_client_method(void); - - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the TLSV1.1 version SSL context client method - */ -const SSL_METHOD* TLSv1_1_client_method(void); - - -/** - * @brief create the target SSL context client method - * - * @param none - * - * @return the TLSV1.2 version SSL context client method - */ -const SSL_METHOD* TLSv1_2_client_method(void); - - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the SSLV2.3 version SSL context server method - */ -const SSL_METHOD* SSLv23_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLSV1.1 version SSL context server method - */ -const SSL_METHOD* TLSv1_1_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLSV1.2 version SSL context server method - */ -const SSL_METHOD* TLSv1_2_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the TLSV1.0 version SSL context server method - */ -const SSL_METHOD* TLSv1_server_method(void); - -/** - * @brief create the target SSL context server method - * - * @param none - * - * @return the SSLV3.0 version SSL context server method - */ -const SSL_METHOD* SSLv3_server_method(void); - -/** - * @brief create a SSL context - * - * @param method - the SSL context method point - * - * @return the context point - */ -SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); - -/** - * @brief free a SSL context - * - * @param method - the SSL context point - * - * @return none - */ -void SSL_CTX_free(SSL_CTX *ctx); - -/** - * @brief set the SSL context version - * - * @param ctx - SSL context point - * @param meth - SSL method point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); - -/** - * @brief get the SSL context current method - * - * @param ctx - SSL context point - * - * @return the SSL context current method - */ -const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); - -/** - * @brief create a SSL - * - * @param ctx - the SSL context point - * - * @return the SSL point - */ -SSL* SSL_new(SSL_CTX *ctx); - -/** - * @brief free the SSL - * - * @param ssl - the SSL point - * - * @return none - */ -void SSL_free(SSL *ssl); - -/** - * @brief perform the SSL handshake - * - * @param ssl - SSL point - * - * @return result - * 1 : OK - * 0 : failed - * -1 : a error catch - */ -int SSL_do_handshake(SSL *ssl); - -/** - * @brief connect to the remote SSL server - * - * @param ssl - the SSL point - * - * @return result - * 1 : OK - * -1 : failed - */ -int SSL_connect(SSL *ssl); - -/** - * @brief accept the remote connection - * - * @param ssl - the SSL point - * - * @return result - * 1 : OK - * -1 : failed - */ -int SSL_accept(SSL *ssl); - -/** - * @brief shutdown the connection - * - * @param ssl - the SSL point - * - * @return result - * 1 : OK - * 0 : shutdown is not finished - * -1 : an error catch - */ -int SSL_shutdown(SSL *ssl); - -/** - * @brief reset the SSL - * - * @param ssl - SSL point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_clear(SSL *ssl); - -/** - * @brief read data from to remote - * - * @param ssl - the SSL point which has been connected - * @param buffer - the received data buffer point - * @param len - the received data length - * - * @return result - * > 0 : OK, and return received data bytes - * = 0 : connection is closed - * < 0 : an error catch - */ -int SSL_read(SSL *ssl, void *buffer, int len); - -/** - * @brief send the data to remote - * - * @param ssl - the SSL point which has been connected - * @param buffer - the send data buffer point - * @param len - the send data length - * - * @return result - * > 0 : OK, and return sent data bytes - * = 0 : connection is closed - * < 0 : an error catch - */ -int SSL_write(SSL *ssl, const void *buffer, int len); - -/** - * @brief get SSL context of the SSL - * - * @param ssl - SSL point - * - * @return SSL context - */ -SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); - -/** - * @brief get SSL shutdown mode - * - * @param ssl - SSL point - * - * @return shutdown mode - */ -int SSL_get_shutdown(const SSL *ssl); - -/** - * @brief set SSL shutdown mode - * - * @param ssl - SSL point - * @param mode - shutdown mode - * - * @return none - */ -void SSL_set_shutdown(SSL *ssl, int mode); - -/** - * @brief get the SSL current method - * - * @param ssl - SSL point - * - * @return the SSL current method - */ -const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); - -/** - * @brief set the SSL method - * - * @param ssl - SSL point - * @param meth - SSL method point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); - -/** - * @brief get the bytes numbers which are to be read - * - * @param ssl - SSL point - * - * @return bytes number - */ -int SSL_pending(const SSL *ssl); - -/** - * @brief check if some data can be read - * - * @param ssl - SSL point - * - * @return - * 1 : there are bytes to be read - * 0 : no data - */ -int SSL_has_pending(const SSL *ssl); - -/** - * @brief get the socket handle of the SSL - * - * @param ssl - SSL point - * - * @return result - * >= 0 : yes, and return socket handle - * < 0 : a error catch - */ -int SSL_get_fd(const SSL *ssl); - -/** - * @brief get the read only socket handle of the SSL - * - * @param ssl - SSL point - * - * @return result - * >= 0 : yes, and return socket handle - * < 0 : a error catch - */ -int SSL_get_rfd(const SSL *ssl); - -/** - * @brief get the write only socket handle of the SSL - * - * @param ssl - SSL point - * - * @return result - * >= 0 : yes, and return socket handle - * < 0 : a error catch - */ -int SSL_get_wfd(const SSL *ssl); - -/** - * @brief bind the socket file description into the SSL - * - * @param ssl - the SSL point - * @param fd - socket handle - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_set_fd(SSL *ssl, int fd); - -/** - * @brief bind the read only socket file description into the SSL - * - * @param ssl - the SSL point - * @param fd - socket handle - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_set_rfd(SSL *ssl, int fd); - -/** - * @brief bind the write only socket file description into the SSL - * - * @param ssl - the SSL point - * @param fd - socket handle - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_set_wfd(SSL *ssl, int fd); - -/** - * @brief get SSL version - * - * @param ssl - SSL point - * - * @return SSL version - */ -int SSL_version(const SSL *ssl); - -/** - * @brief get the SSL current version - * - * @param ssl - SSL point - * - * @return the version string - */ -const char *SSL_get_version(const SSL *ssl); - -/** - * @brief get the SSL state - * - * @param ssl - SSL point - * - * @return SSL state - */ -OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); - -/** - * @brief get alert description string - * - * @param value - alert value - * - * @return alert description string - */ -const char *SSL_alert_desc_string(int value); - -/** - * @brief get alert description long string - * - * @param value - alert value - * - * @return alert description long string - */ -const char *SSL_alert_desc_string_long(int value); - -/** - * @brief get alert type string - * - * @param value - alert value - * - * @return alert type string - */ -const char *SSL_alert_type_string(int value); - -/** - * @brief get alert type long string - * - * @param value - alert value - * - * @return alert type long string - */ -const char *SSL_alert_type_string_long(int value); - -/** - * @brief get the state string where SSL is reading - * - * @param ssl - SSL point - * - * @return state string - */ -const char *SSL_rstate_string(SSL *ssl); - -/** - * @brief get the statement long string where SSL is reading - * - * @param ssl - SSL point - * - * @return statement long string - */ -const char *SSL_rstate_string_long(SSL *ssl); - -/** - * @brief get SSL statement string - * - * @param ssl - SSL point - * - * @return SSL statement string - */ -char *SSL_state_string(const SSL *ssl); - -/** - * @brief get SSL statement long string - * - * @param ssl - SSL point - * - * @return SSL statement long string - */ -char *SSL_state_string_long(const SSL *ssl); - -/** - * @brief get SSL error code - * - * @param ssl - SSL point - * @param ret_code - SSL return code - * - * @return SSL error number - */ -int SSL_get_error(const SSL *ssl, int ret_code); - -/** - * @brief load a character certification context into system context. If '*cert' is pointed to the - * certification, then load certification into it. Or create a new X509 certification object - * - * @param cert - a point pointed to X509 certification - * @param buffer - a point pointed to the certification context memory point - * @param length - certification bytes - * - * @return X509 certification object point - */ -X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); - -/** - * @brief add CA client certification into the SSL - * - * @param ssl - SSL point - * @param x - CA certification point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_add_client_CA(SSL *ssl, X509 *x); - -/** - * @brief add CA client certification into the SSL context - * - * @param ctx - SSL context point - * @param x - CA certification point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); - -/** - * @brief get the SSL certification point - * - * @param ssl - SSL point - * - * @return SSL certification point - */ -X509 *SSL_get_certificate(const SSL *ssl); - -/** - * @brief get the verifying result of the SSL certification - * - * @param ssl - the SSL point - * - * @return the result of verifying - */ -long SSL_get_verify_result(const SSL *ssl); - -/** - * @brief These functions load the certification into the SSL_CTX or SSL object - * - * @param ctx - the SSL context point - * @param pkey - certification object point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); - -/** - * @brief load the ASN1 certification into SSL context - * - * @param ctx - SSL context point - * @param len - certification length - * @param d - data point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); - -/** - * @brief These functions load the private key into the SSL_CTX or SSL object - * - * @param ctx - the SSL context point - * @param pkey - private key object point - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); - -/** - * @brief load the ASN1 private key into SSL context - * - * @param ctx - SSL context point - * @param d - data point - * @param len - private key length - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len); - -/** - * @brief load the RSA ASN1 private key into SSL context - * - * @param ctx - SSL context point - * @param d - data point - * @param len - RSA private key length - * - * @return result - * 1 : OK - * 0 : failed - */ -int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); - -/** - * @brief load certification into the SSL - * - * @param ssl - SSL point - * @param len - data bytes - * @param d - data point - * - * @return result - * 0 : failed - * 1 : OK - * - */ -int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); - -/** - * @brief get peer certification - * - * @param ssl - SSL point - * - * @return certification - */ -X509 *SSL_get_peer_certificate(const SSL *ssl); - -/** - * @brief set the SSL context read buffer length - * - * @param ctx - SSL context point - * @param len - read buffer length - * - * @return none - */ -void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); - -/** - * @brief set the SSL read buffer length - * - * @param ssl - SSL point - * @param len - read buffer length - * - * @return none - */ -void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); - -/** - * @brief get the SSL specifical statement - * - * @param ssl - SSL point - * - * @return specifical statement - */ -int SSL_want(const SSL *ssl); - -/** - * @brief check if SSL want nothing - * - * @param ssl - SSL point - * - * @return result - * 0 : false - * 1 : true - */ -int SSL_want_nothing(const SSL *ssl); - -/** - * @brief check if SSL want to read - * - * @param ssl - SSL point - * - * @return result - * 0 : false - * 1 : true - */ -int SSL_want_read(const SSL *ssl); - -/** - * @brief check if SSL want to write - * - * @param ssl - SSL point - * - * @return result - * 0 : false - * 1 : true - */ -int SSL_want_write(const SSL *ssl); From 1bcc5438ec51e3755b4fe98e18891e96335e1a57 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 28 Sep 2016 19:40:05 +0800 Subject: [PATCH 036/285] components/openssl: fix .rst file encoding type --- components/openssl/OpenSSL-APIs.rst | 2965 ++++++++++++++------------- 1 file changed, 1487 insertions(+), 1478 deletions(-) diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index ff91d2ebb7..5a44794cd2 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -1,1478 +1,1487 @@ -OpenSSL-APIs -====================== - -Chapter 1. SSL Context Method Create -Chapter 2. SSL Context Fucntion -Chapter 3. SSL Fucntion -Chapter 4. SSL X509 Certification and Private Key Function - -====================== -Chapter 1. SSL Context Method Create - -1.1 const SSL_METHOD* SSLv23_client_method(void); - - Arguments : none - - Return : SSLV2 and 3 version SSL context client method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = SSLv23_client_method(); - - ... - } - - -1.2 const SSL_METHOD* TLSv1_client_method(void); - - Arguments : none - - Return : TLSV1.0 version SSL context client method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = TLSv1_client_method(); - - ... - } - - -1.3 const SSL_METHOD* SSLv3_client_method(void); - - Arguments : none - - Return : SSLV3.0 version SSL context client method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = SSLv3_client_method(); - - ... - } - - -1.4 const SSL_METHOD* TLSv1_1_client_method(void); - - Arguments : none - - Return : TLSV1.1 version SSL context client method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = TLSv1_1_client_method(); - - ... - } - - -1.5 const SSL_METHOD* TLSv1_2_client_method(void); - - Arguments : none - - Return : TLSV1.2 version SSL context client method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = TLSv1_2_client_method(); - - ... - } - - -1.6 const SSL_METHOD* SSLv23_server_method(void); - - Arguments : none - - Return : SSLV2 and 3 version SSL context server method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = SSLv23_server_method(); - - ... - } - - -1.7 const SSL_METHOD* TLSv1_1_server_method(void); - - Arguments : none - - Return : TLSV1.1 version SSL context server method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = TLSv1_1_server_method(); - - ... - } - - -1.8 const SSL_METHOD* TLSv1_2_server_method(void); - - Arguments : none - - Return : TLSV1.2 version SSL context server method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = TLSv1_2_server_method(); - - ... - } - - -1.9 const SSL_METHOD* TLSv1_server_method(void); - - Arguments : none - - Return : TLSV1.0 version SSL context server method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = TLSv1_server_method(); - - ... - } - - -1.10 const SSL_METHOD* SSLv3_server_method(void); - - Arguments : none - - Return : SSLV3.0 version SSL context server method point - - Description : create the target SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method = SSLv3_server_method(); - - ... - } - - -====================== -Chapter 2. SSL Context Fucntion - -2.1 SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); - - Arguments : method - the SSL context method point - - Return : context point - - Description : create a SSL context - - Example : - - void example(void) - { - SSL_CTX *ctx = SSL_CTX_new(SSLv3_server_method()); - - ... - } - - -2.2 void SSL_CTX_free(SSL_CTX *ctx); - - Arguments : ctx - the SSL context point - - Return : none - - Description : free a SSL context - - Example : - - void example(void) - { - SSL_CTX *ctx; - - ... ... - - SSL_CTX_free(ctx); - } - - -2.3 int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); - - Arguments : ctx - SSL context point - meth - SSL method point - - Return : result - 1 : OK - 0 : failed - - Description : set the SSL context version - - Example : - - void example(void) - { - SSL_CTX *ctx; - const SSL_METHOD *meth; - - ... ... - - SSL_CTX_set_ssl_version(ctx, meth); - } - - -2.4 const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); - - Arguments : ctx - SSL context point - - Return : SSL context method - - Description : get the SSL context method - - Example : - - void example(void) - { - const SSL_METHOD *method; - SSL_CTX *ctx; - - ... ... - - method = SSL_CTX_get_ssl_method(ctx); - } - - -====================== -Chapter 3. SSL Fucntion - -3.1 SSL* SSL_new(SSL_CTX *ctx); - - Arguments : ctx - SSL context point - - Return : SSL method - - Description : create a SSL - - Example : - - void example(void) - { - SSL *ssl; - SSL_CTX *ctx; - - ... ... - - ssl = SSL_new(ctx); - } - - -3.2 void SSL_free(SSL *ssl); - - Arguments : ssl - SSL point - - Return : none - - Description : free SSL - - Example : - - void example(void) - { - SSL *ssl; - - ... ... - - SSL_free(ssl); - } - - -3.3 int SSL_do_handshake(SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 1 : OK - 0 : failed, connect is close by remote - -1 : a error catch - - Description : perform the SSL handshake - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_do_handshake(ssl); - } - - -3.4 int SSL_connect(SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 1 : OK - 0 : failed, connect is close by remote - -1 : a error catch - - Description : connect to the remote SSL server - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_connect(ssl); - } - - -3.5 int SSL_accept(SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 1 : OK - 0 : failed, connect is close by remote - -1 : a error catch - - Description : accept the remote connection - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_accept(ssl); - } - - -3.6 int SSL_shutdown(SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 1 : OK - 0 : failed, connect is close by remote - -1 : a error catch - - Description : shutdown the connection - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_shutdown(ssl); - } - - -3.7 int SSL_clear(SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 1 : OK - 0 : failed - - Description : shutdown the connection - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_clear(ssl); - } - - -3.8 int SSL_read(SSL *ssl, void *buffer, int len); - - Arguments : ssl - point - buffer - data buffer point - len - data length - - Return : result - > 0 : OK, and return received data bytes - = 0 : no data received or connection is closed - < 0 : an error catch - - Description : read data from remote - - Example : - - void example(void) - { - SSL *ssl; - char *buf; - int len; - int ret; - - ... ... - - ret = SSL_read(ssl, buf, len); - } - -3.9 int SSL_write(SSL *ssl, const void *buffer, int len); - - Arguments : ssl - SSL point - buffer - data buffer point - len - data length - - Return : result - > 0 : OK, and return received data bytes - = 0 : no data sent or connection is closed - < 0 : an error catch - - Description : send the data to remote - - Example : - - void example(void) - { - SSL *ssl; - char *buf; - int len; - int ret; - - ... ... - - ret = SSL_write(ssl, buf, len); - } - - -3.10 SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : SSL context - - Description : get SSL context of the SSL - - Example : - - void example(void) - { - SSL *ssl; - SSL_CTX *ctx; - - ... ... - - ctx = SSL_get_SSL_CTX(ssl); - } - - -3.11 int SSL_get_shutdown(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : shutdown mode - - Description : get SSL shutdown mode - - Example : - - void example(void) - { - SSL *ssl; - int mode; - - ... ... - - mode = SSL_get_SSL_CTX(ssl); - } - - -3.12 void SSL_set_shutdown(SSL *ssl, int mode); - - Arguments : ssl - SSL point - - Return : shutdown mode - - Description : set SSL shutdown mode - - Example : - - void example(void) - { - SSL *ssl; - int mode = 0; - - ... ... - - SSL_set_shutdown(ssl, mode); - } - - -3.13 const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); - - Arguments : ssl - SSL point - - Return : SSL method - - Description : set SSL shutdown mode - - Example : - - void example(void) - { - SSL *ssl; - const SSL_METHOD *method; - - ... ... - - method = SSL_get_ssl_method(ssl); - } - - -3.14 int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); - - Arguments : ssl - SSL point - meth - SSL method point - - Return : result - 1 : OK - 0 : failed - - Description : set the SSL method - - Example : - - void example(void) - { - int ret; - SSL *ssl; - const SSL_METHOD *method; - - ... ... - - ret = SSL_set_ssl_method(ssl, method); - } - - -3.15 int SSL_pending(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : data bytes - - Description : get received data bytes - - Example : - - void example(void) - { - int ret; - SSL *ssl; - - ... ... - - ret = SSL_pending(ssl); - } - - -3.16 int SSL_has_pending(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 1 : Yes - 0 : No - - Description : check if data is received - - Example : - - void example(void) - { - int ret; - SSL *ssl; - - ... ... - - ret = SSL_has_pending(ssl); - } - - -3.17 int SSL_get_fd(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - >= 0 : socket id - < 0 : a error catch - - Description : get the socket of the SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - - ... ... - - ret = SSL_get_fd(ssl); - } - - -3.18 int SSL_get_rfd(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - >= 0 : socket id - < 0 : a error catch - - Description : get the read only socket of the SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - - ... ... - - ret = SSL_get_rfd(ssl); - } - - -3.19 int SSL_get_wfd(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - >= 0 : socket id - < 0 : a error catch - - Description : get the write only socket of the SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - - ... ... - - ret = SSL_get_wfd(ssl); - } - - -3.20 int SSL_set_fd(SSL *ssl, int fd); - - Arguments : ssl - SSL point - fd - socket id - - Return : result - 1 : OK - 0 : failed - - Description : set socket to SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - int socket; - - ... ... - - ret = SSL_set_fd(ssl, socket); - } - - -3.21 int SSL_set_rfd(SSL *ssl, int fd); - - Arguments : ssl - SSL point - fd - socket id - - Return : result - 1 : OK - 0 : failed - - Description : set read only socket to SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - int socket; - - ... ... - - ret = SSL_set_rfd(ssl, socket); - } - - -3.22 int SSL_set_wfd(SSL *ssl, int fd); - - Arguments : ssl - SSL point - fd - socket id - - Return : result - 1 : OK - 0 : failed - - Description : set write only socket to SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - int socket; - - ... ... - - ret = SSL_set_wfd(ssl, socket); - } - - -3.23 int SSL_version(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : SSL version - - Description : get SSL version - - Example : - - void example(void) - { - int version; - SSL *ssl; - - ... ... - - version = SSL_version(ssl); - } - - -3.24 const char *SSL_get_version(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : SSL version string - - Description : get the SSL current version string - - Example : - - void example(void) - { - char *version; - SSL *ssl; - - ... ... - - version = SSL_get_version(ssl); - } - - -3.25 OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : SSL state - - Description : get the SSL state - - Example : - - void example(void) - { - OSSL_HANDSHAKE_STATE state; - SSL *ssl; - - ... ... - - state = SSL_get_state(ssl); - } - - -3.26 const char *SSL_alert_desc_string(int value); - - Arguments : value - SSL description - - Return : alert value string - - Description : get alert description string - - Example : - - void example(void) - { - int val; - char *str; - - ... ... - - str = SSL_alert_desc_string(val); - } - - -3.27 const char *SSL_alert_desc_string_long(int value); - - Arguments : value - SSL description - - Return : alert value long string - - Description : get alert description long string - - Example : - - void example(void) - { - int val; - char *str; - - ... ... - - str = SSL_alert_desc_string_long(val); - } - - -3.28 const char *SSL_alert_type_string(int value); - - Arguments : value - SSL type description - - Return : alert type string - - Description : get alert type string - - Example : - - void example(void) - { - int val; - char *str; - - ... ... - - str = SSL_alert_type_string(val); - } - - -3.29 const char *SSL_alert_type_string_long(int value); - - Arguments : value - SSL type description - - Return : alert type long string - - Description : get alert type long string - - Example : - - void example(void) - { - int val; - char *str; - - ... ... - - str = SSL_alert_type_string_long(val); - } - -3.30 const char *SSL_rstate_string(SSL *ssl); - - Arguments : ssl - SSL point - - Return : state string - - Description : get the state string where SSL is reading - - Example : - - void example(void) - { - SSL *ssl; - char *str; - - ... ... - - str = SSL_rstate_string(ssl); - } - - -3.31 const char *SSL_rstate_string_long(SSL *ssl); - - Arguments : ssl - SSL point - - Return : state long string - - Description : get the state long string where SSL is reading - - Example : - - void example(void) - { - SSL *ssl; - char *str; - - ... ... - - str = SSL_rstate_string_long(ssl); - } - - -3.32 char *SSL_state_string(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : state string - - Description : get the state string - - Example : - - void example(void) - { - SSL *ssl; - char *str; - - ... ... - - str = SSL_state_string(ssl); - } - - -3.33 char *SSL_state_string_long(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : state long string - - Description : get the state long string - - Example : - - void example(void) - { - SSL *ssl; - char *str; - - ... ... - - str = SSL_state_string(ssl); - } - - -3.34 int SSL_get_error(const SSL *ssl, int ret_code); - - Arguments : ssl - SSL point - ret_code - SSL return code - - Return : SSL error number - - Description : get SSL error code - - Example : - - void example(void) - { - SSL *ssl; - int ret; - int err; - - ... ... - - err = SSL_get_error(ssl, ret); - } - -3.35 void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); - - Arguments : ctx - SSL context point - len - read buffer length - - Return : none - - Description : set the SSL context read buffer length - - Example : - - void example(void) - { - SSL_CTX *ctx; - size_t len; - - ... ... - - SSL_CTX_set_default_read_buffer_len(ctx, len); - } - - -3.36 void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); - - Arguments : ssl - SSL point - len - read buffer length - - Return : none - - Description : set the SSL read buffer length - - Example : - - void example(void) - { - SSL *ssl; - size_t len; - - ... ... - - SSL_set_default_read_buffer_len(ctx, len); - } - - -3.37 int SSL_want(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : specifical statement - - Description : get the SSL specifical statement - - Example : - - void example(void) - { - SSL *ssl; - int state; - - ... ... - - state = SSL_want(ssl); - } - - -3.38 int SSL_want_nothing(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 0 : false - 1 : true - - Description : check if SSL want nothing - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_want(ssl); - } - - -3.39 int SSL_want_read(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 0 : false - 1 : true - - Description : check if SSL want to read - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_want_read(ssl); - } - - -3.40 int SSL_want_write(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : result - 0 : false - 1 : true - - Description : check if SSL want to write - - Example : - - void example(void) - { - SSL *ssl; - int ret; - - ... ... - - ret = SSL_want_write(ssl); - } - -====================== -Chapter 4. SSL X509 Certification and Private Key Function - -4.1 X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); - - Arguments : cert - a point pointed to X509 certification - buffer - a point pointed to the certification context memory point - length - certification bytes - - Return : X509 certification object point - - Description : load a character certification context into system context. If '*cert' is pointed to the - certification, then load certification into it. Or create a new X509 certification object - - Example : - - void example(void) - { - X509 *new; - X509 *cert; - unsigned char *buffer; - long len; - ... ... - - new = d2i_X509(&cert, buffer, len); - } - - -4.2 int SSL_add_client_CA(SSL *ssl, X509 *x); - - Arguments : ssl - SSL point - x - CA certification point - - Return : result - 1 : OK - 0 : failed - - Description : add CA client certification into the SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - X509 *new; - - ... ... - - ret = SSL_add_client_CA(ssl, new); - } - - -4.3 int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); - - Arguments : ctx - SSL context point - x - CA certification point - - Return : result - 1 : OK - 0 : failed - - Description : add CA client certification into the SSL context - - Example : - - void example(void) - { - int ret; - SSL_CTX *ctx; - X509 *new; - - ... ... - - ret = SSL_add_clSSL_CTX_add_client_CAient_CA(ctx, new); - } - - -4.4 X509 *SSL_get_certificate(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : SSL certification point - - Description : get the SSL certification point - - Example : - - void example(void) - { - SSL *ssl; - X509 *cert; - - ... ... - - cert = SSL_get_certificate(ssl); - } - - -4.5 long SSL_get_verify_result(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : the result of verifying - - Description : get the verifying result of the SSL certification - - Example : - - void example(void) - { - SSL *ssl; - long ret; - - ... ... - - ret = SSL_get_verify_result(ssl); - } - - -4.6 int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); - - Arguments : ctx - the SSL context point - pkey - certification object point - - Return : result - 1 : OK - 0 : failed - - Description : load the certification into the SSL_CTX or SSL object - - Example : - - void example(void) - { - int ret; - SSL_CTX *ctx - X509 *new; - - ... ... - - ret = SSL_CTX_use_certificate(ctx, new); - } - - -4.7 int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); - - Arguments : ctx - SSL context point - len - certification length - d - data point - - Return : result - 1 : OK - 0 : failed - - Description : load the ASN1 certification into SSL context - - Example : - - void example(void) - { - int ret; - SSL_CTX *ctx; - const unsigned char *buf; - int len; - - ... ... - - ret = SSL_CTX_use_certificate_ASN1(ctx, len, buf); - } - - -4.8 int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); - - Arguments : ctx - SSL context point - pkey - private key object point - - Return : result - 1 : OK - 0 : failed - - Description : load the private key into the context object - - Example : - - void example(void) - { - int ret; - SSL_CTX *ctx; - EVP_PKEY *pkey; - - ... ... - - ret = SSL_CTX_use_PrivateKey(ctx, pkey); - } - - -4.9 int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len); - - Arguments : ctx - SSL context point - d - data point - len - private key length - - Return : result - 1 : OK - 0 : failed - - Description : load the ASN1 private key into SSL context - - Example : - - void example(void) - { - int ret; - int pk; - SSL_CTX *ctx; - const unsigned char *buf; - long len; - - ... ... - - ret = SSL_CTX_use_PrivateKey_ASN1(pk, ctx, buf, len); - } - - -4.10 int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); - - Arguments : ctx - SSL context point - d - data point - len - private key length - - Return : result - 1 : OK - 0 : failed - - Description : load the RSA ASN1 private key into SSL context - - Example : - - void example(void) - { - int ret; - SSL_CTX *ctx; - const unsigned char *buf; - long len; - - ... ... - - ret = SSL_CTX_use_RSAPrivateKey_ASN1(ctx, buf, len); - } - - -4.11 int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); - - Arguments : ssl - SSL point - len - data bytes - d - data point - - Return : result - 1 : OK - 0 : failed - - Description : load certification into the SSL - - Example : - - void example(void) - { - int ret; - SSL *ssl; - const unsigned char *buf; - long len; - - ... ... - - ret = SSL_use_certificate_ASN1(ssl, len, buf); - } - - -4.12 X509 *SSL_get_peer_certificate(const SSL *ssl); - - Arguments : ssl - SSL point - - Return : peer certification - - Description : get peer certification - - Example : - - void example(void) - { - SSL *ssl; - X509 *peer; - - ... ... - - peer = SSL_get_peer_certificate(ssl); - } - -====================== -END \ No newline at end of file +OpenSSL-APIs +============ + +All original source code in this repository is Copyright (C) 2015-2016 +Espressif Systems. This source code is licensed under the Apache +License 2.0 as described in the file LICENSE. + +Chapter Introduction +==================== + +Chapter 1. SSL Context Method Create +Chapter 2. SSL Context Fucntion +Chapter 3. SSL Fucntion +Chapter 4. SSL X509 Certification and Private Key Function + + +Chapter 1. SSL Context Method Create +==================================== + +1.1 const SSL_METHOD* SSLv23_client_method(void); + + Arguments : none + + Return : SSLV2 and 3 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv23_client_method(); + + ... + } + + +1.2 const SSL_METHOD* TLSv1_client_method(void); + + Arguments : none + + Return : TLSV1.0 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_client_method(); + + ... + } + + +1.3 const SSL_METHOD* SSLv3_client_method(void); + + Arguments : none + + Return : SSLV3.0 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv3_client_method(); + + ... + } + + +1.4 const SSL_METHOD* TLSv1_1_client_method(void); + + Arguments : none + + Return : TLSV1.1 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_1_client_method(); + + ... + } + + +1.5 const SSL_METHOD* TLSv1_2_client_method(void); + + Arguments : none + + Return : TLSV1.2 version SSL context client method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_2_client_method(); + + ... + } + + +1.6 const SSL_METHOD* SSLv23_server_method(void); + + Arguments : none + + Return : SSLV2 and 3 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv23_server_method(); + + ... + } + + +1.7 const SSL_METHOD* TLSv1_1_server_method(void); + + Arguments : none + + Return : TLSV1.1 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_1_server_method(); + + ... + } + + +1.8 const SSL_METHOD* TLSv1_2_server_method(void); + + Arguments : none + + Return : TLSV1.2 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_2_server_method(); + + ... + } + + +1.9 const SSL_METHOD* TLSv1_server_method(void); + + Arguments : none + + Return : TLSV1.0 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = TLSv1_server_method(); + + ... + } + + +1.10 const SSL_METHOD* SSLv3_server_method(void); + + Arguments : none + + Return : SSLV3.0 version SSL context server method point + + Description : create the target SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method = SSLv3_server_method(); + + ... + } + + + +Chapter 2. SSL Context Fucntion +=============================== + +2.1 SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); + + Arguments : method - the SSL context method point + + Return : context point + + Description : create a SSL context + + Example : + + void example(void) + { + SSL_CTX *ctx = SSL_CTX_new(SSLv3_server_method()); + + ... + } + + +2.2 void SSL_CTX_free(SSL_CTX *ctx); + + Arguments : ctx - the SSL context point + + Return : none + + Description : free a SSL context + + Example : + + void example(void) + { + SSL_CTX *ctx; + + ... ... + + SSL_CTX_free(ctx); + } + + +2.3 int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); + + Arguments : ctx - SSL context point + meth - SSL method point + + Return : result + 1 : OK + 0 : failed + + Description : set the SSL context version + + Example : + + void example(void) + { + SSL_CTX *ctx; + const SSL_METHOD *meth; + + ... ... + + SSL_CTX_set_ssl_version(ctx, meth); + } + + +2.4 const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); + + Arguments : ctx - SSL context point + + Return : SSL context method + + Description : get the SSL context method + + Example : + + void example(void) + { + const SSL_METHOD *method; + SSL_CTX *ctx; + + ... ... + + method = SSL_CTX_get_ssl_method(ctx); + } + + + +Chapter 3. SSL Fucntion +======================= + +3.1 SSL* SSL_new(SSL_CTX *ctx); + + Arguments : ctx - SSL context point + + Return : SSL method + + Description : create a SSL + + Example : + + void example(void) + { + SSL *ssl; + SSL_CTX *ctx; + + ... ... + + ssl = SSL_new(ctx); + } + + +3.2 void SSL_free(SSL *ssl); + + Arguments : ssl - SSL point + + Return : none + + Description : free SSL + + Example : + + void example(void) + { + SSL *ssl; + + ... ... + + SSL_free(ssl); + } + + +3.3 int SSL_do_handshake(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : perform the SSL handshake + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_do_handshake(ssl); + } + + +3.4 int SSL_connect(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : connect to the remote SSL server + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_connect(ssl); + } + + +3.5 int SSL_accept(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : accept the remote connection + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_accept(ssl); + } + + +3.6 int SSL_shutdown(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed, connect is close by remote + -1 : a error catch + + Description : shutdown the connection + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_shutdown(ssl); + } + + +3.7 int SSL_clear(SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : OK + 0 : failed + + Description : shutdown the connection + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_clear(ssl); + } + + +3.8 int SSL_read(SSL *ssl, void *buffer, int len); + + Arguments : ssl - point + buffer - data buffer point + len - data length + + Return : result + > 0 : OK, and return received data bytes + = 0 : no data received or connection is closed + < 0 : an error catch + + Description : read data from remote + + Example : + + void example(void) + { + SSL *ssl; + char *buf; + int len; + int ret; + + ... ... + + ret = SSL_read(ssl, buf, len); + } + +3.9 int SSL_write(SSL *ssl, const void *buffer, int len); + + Arguments : ssl - SSL point + buffer - data buffer point + len - data length + + Return : result + > 0 : OK, and return received data bytes + = 0 : no data sent or connection is closed + < 0 : an error catch + + Description : send the data to remote + + Example : + + void example(void) + { + SSL *ssl; + char *buf; + int len; + int ret; + + ... ... + + ret = SSL_write(ssl, buf, len); + } + + +3.10 SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL context + + Description : get SSL context of the SSL + + Example : + + void example(void) + { + SSL *ssl; + SSL_CTX *ctx; + + ... ... + + ctx = SSL_get_SSL_CTX(ssl); + } + + +3.11 int SSL_get_shutdown(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : shutdown mode + + Description : get SSL shutdown mode + + Example : + + void example(void) + { + SSL *ssl; + int mode; + + ... ... + + mode = SSL_get_SSL_CTX(ssl); + } + + +3.12 void SSL_set_shutdown(SSL *ssl, int mode); + + Arguments : ssl - SSL point + + Return : shutdown mode + + Description : set SSL shutdown mode + + Example : + + void example(void) + { + SSL *ssl; + int mode = 0; + + ... ... + + SSL_set_shutdown(ssl, mode); + } + + +3.13 const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL method + + Description : set SSL shutdown mode + + Example : + + void example(void) + { + SSL *ssl; + const SSL_METHOD *method; + + ... ... + + method = SSL_get_ssl_method(ssl); + } + + +3.14 int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); + + Arguments : ssl - SSL point + meth - SSL method point + + Return : result + 1 : OK + 0 : failed + + Description : set the SSL method + + Example : + + void example(void) + { + int ret; + SSL *ssl; + const SSL_METHOD *method; + + ... ... + + ret = SSL_set_ssl_method(ssl, method); + } + + +3.15 int SSL_pending(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : data bytes + + Description : get received data bytes + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_pending(ssl); + } + + +3.16 int SSL_has_pending(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 1 : Yes + 0 : No + + Description : check if data is received + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_has_pending(ssl); + } + + +3.17 int SSL_get_fd(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + >= 0 : socket id + < 0 : a error catch + + Description : get the socket of the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_get_fd(ssl); + } + + +3.18 int SSL_get_rfd(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + >= 0 : socket id + < 0 : a error catch + + Description : get the read only socket of the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_get_rfd(ssl); + } + + +3.19 int SSL_get_wfd(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + >= 0 : socket id + < 0 : a error catch + + Description : get the write only socket of the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + + ... ... + + ret = SSL_get_wfd(ssl); + } + + +3.20 int SSL_set_fd(SSL *ssl, int fd); + + Arguments : ssl - SSL point + fd - socket id + + Return : result + 1 : OK + 0 : failed + + Description : set socket to SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + int socket; + + ... ... + + ret = SSL_set_fd(ssl, socket); + } + + +3.21 int SSL_set_rfd(SSL *ssl, int fd); + + Arguments : ssl - SSL point + fd - socket id + + Return : result + 1 : OK + 0 : failed + + Description : set read only socket to SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + int socket; + + ... ... + + ret = SSL_set_rfd(ssl, socket); + } + + +3.22 int SSL_set_wfd(SSL *ssl, int fd); + + Arguments : ssl - SSL point + fd - socket id + + Return : result + 1 : OK + 0 : failed + + Description : set write only socket to SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + int socket; + + ... ... + + ret = SSL_set_wfd(ssl, socket); + } + + +3.23 int SSL_version(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL version + + Description : get SSL version + + Example : + + void example(void) + { + int version; + SSL *ssl; + + ... ... + + version = SSL_version(ssl); + } + + +3.24 const char *SSL_get_version(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL version string + + Description : get the SSL current version string + + Example : + + void example(void) + { + char *version; + SSL *ssl; + + ... ... + + version = SSL_get_version(ssl); + } + + +3.25 OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL state + + Description : get the SSL state + + Example : + + void example(void) + { + OSSL_HANDSHAKE_STATE state; + SSL *ssl; + + ... ... + + state = SSL_get_state(ssl); + } + + +3.26 const char *SSL_alert_desc_string(int value); + + Arguments : value - SSL description + + Return : alert value string + + Description : get alert description string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_desc_string(val); + } + + +3.27 const char *SSL_alert_desc_string_long(int value); + + Arguments : value - SSL description + + Return : alert value long string + + Description : get alert description long string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_desc_string_long(val); + } + + +3.28 const char *SSL_alert_type_string(int value); + + Arguments : value - SSL type description + + Return : alert type string + + Description : get alert type string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_type_string(val); + } + + +3.29 const char *SSL_alert_type_string_long(int value); + + Arguments : value - SSL type description + + Return : alert type long string + + Description : get alert type long string + + Example : + + void example(void) + { + int val; + char *str; + + ... ... + + str = SSL_alert_type_string_long(val); + } + +3.30 const char *SSL_rstate_string(SSL *ssl); + + Arguments : ssl - SSL point + + Return : state string + + Description : get the state string where SSL is reading + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_rstate_string(ssl); + } + + +3.31 const char *SSL_rstate_string_long(SSL *ssl); + + Arguments : ssl - SSL point + + Return : state long string + + Description : get the state long string where SSL is reading + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_rstate_string_long(ssl); + } + + +3.32 char *SSL_state_string(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : state string + + Description : get the state string + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_state_string(ssl); + } + + +3.33 char *SSL_state_string_long(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : state long string + + Description : get the state long string + + Example : + + void example(void) + { + SSL *ssl; + char *str; + + ... ... + + str = SSL_state_string(ssl); + } + + +3.34 int SSL_get_error(const SSL *ssl, int ret_code); + + Arguments : ssl - SSL point + ret_code - SSL return code + + Return : SSL error number + + Description : get SSL error code + + Example : + + void example(void) + { + SSL *ssl; + int ret; + int err; + + ... ... + + err = SSL_get_error(ssl, ret); + } + +3.35 void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); + + Arguments : ctx - SSL context point + len - read buffer length + + Return : none + + Description : set the SSL context read buffer length + + Example : + + void example(void) + { + SSL_CTX *ctx; + size_t len; + + ... ... + + SSL_CTX_set_default_read_buffer_len(ctx, len); + } + + +3.36 void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); + + Arguments : ssl - SSL point + len - read buffer length + + Return : none + + Description : set the SSL read buffer length + + Example : + + void example(void) + { + SSL *ssl; + size_t len; + + ... ... + + SSL_set_default_read_buffer_len(ctx, len); + } + + +3.37 int SSL_want(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : specifical statement + + Description : get the SSL specifical statement + + Example : + + void example(void) + { + SSL *ssl; + int state; + + ... ... + + state = SSL_want(ssl); + } + + +3.38 int SSL_want_nothing(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 0 : false + 1 : true + + Description : check if SSL want nothing + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_want(ssl); + } + + +3.39 int SSL_want_read(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 0 : false + 1 : true + + Description : check if SSL want to read + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_want_read(ssl); + } + + +3.40 int SSL_want_write(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : result + 0 : false + 1 : true + + Description : check if SSL want to write + + Example : + + void example(void) + { + SSL *ssl; + int ret; + + ... ... + + ret = SSL_want_write(ssl); + } + + +Chapter 4. SSL X509 Certification and Private Key Function +========================================================== + +4.1 X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); + + Arguments : cert - a point pointed to X509 certification + buffer - a point pointed to the certification context memory point + length - certification bytes + + Return : X509 certification object point + + Description : load a character certification context into system context. If '*cert' is pointed to the + certification, then load certification into it. Or create a new X509 certification object + + Example : + + void example(void) + { + X509 *new; + X509 *cert; + unsigned char *buffer; + long len; + ... ... + + new = d2i_X509(&cert, buffer, len); + } + + +4.2 int SSL_add_client_CA(SSL *ssl, X509 *x); + + Arguments : ssl - SSL point + x - CA certification point + + Return : result + 1 : OK + 0 : failed + + Description : add CA client certification into the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + X509 *new; + + ... ... + + ret = SSL_add_client_CA(ssl, new); + } + + +4.3 int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); + + Arguments : ctx - SSL context point + x - CA certification point + + Return : result + 1 : OK + 0 : failed + + Description : add CA client certification into the SSL context + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + X509 *new; + + ... ... + + ret = SSL_add_clSSL_CTX_add_client_CAient_CA(ctx, new); + } + + +4.4 X509 *SSL_get_certificate(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : SSL certification point + + Description : get the SSL certification point + + Example : + + void example(void) + { + SSL *ssl; + X509 *cert; + + ... ... + + cert = SSL_get_certificate(ssl); + } + + +4.5 long SSL_get_verify_result(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : the result of verifying + + Description : get the verifying result of the SSL certification + + Example : + + void example(void) + { + SSL *ssl; + long ret; + + ... ... + + ret = SSL_get_verify_result(ssl); + } + + +4.6 int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); + + Arguments : ctx - the SSL context point + pkey - certification object point + + Return : result + 1 : OK + 0 : failed + + Description : load the certification into the SSL_CTX or SSL object + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx + X509 *new; + + ... ... + + ret = SSL_CTX_use_certificate(ctx, new); + } + + +4.7 int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); + + Arguments : ctx - SSL context point + len - certification length + d - data point + + Return : result + 1 : OK + 0 : failed + + Description : load the ASN1 certification into SSL context + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + const unsigned char *buf; + int len; + + ... ... + + ret = SSL_CTX_use_certificate_ASN1(ctx, len, buf); + } + + +4.8 int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); + + Arguments : ctx - SSL context point + pkey - private key object point + + Return : result + 1 : OK + 0 : failed + + Description : load the private key into the context object + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + EVP_PKEY *pkey; + + ... ... + + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + } + + +4.9 int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len); + + Arguments : ctx - SSL context point + d - data point + len - private key length + + Return : result + 1 : OK + 0 : failed + + Description : load the ASN1 private key into SSL context + + Example : + + void example(void) + { + int ret; + int pk; + SSL_CTX *ctx; + const unsigned char *buf; + long len; + + ... ... + + ret = SSL_CTX_use_PrivateKey_ASN1(pk, ctx, buf, len); + } + + +4.10 int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); + + Arguments : ctx - SSL context point + d - data point + len - private key length + + Return : result + 1 : OK + 0 : failed + + Description : load the RSA ASN1 private key into SSL context + + Example : + + void example(void) + { + int ret; + SSL_CTX *ctx; + const unsigned char *buf; + long len; + + ... ... + + ret = SSL_CTX_use_RSAPrivateKey_ASN1(ctx, buf, len); + } + + +4.11 int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); + + Arguments : ssl - SSL point + len - data bytes + d - data point + + Return : result + 1 : OK + 0 : failed + + Description : load certification into the SSL + + Example : + + void example(void) + { + int ret; + SSL *ssl; + const unsigned char *buf; + long len; + + ... ... + + ret = SSL_use_certificate_ASN1(ssl, len, buf); + } + + +4.12 X509 *SSL_get_peer_certificate(const SSL *ssl); + + Arguments : ssl - SSL point + + Return : peer certification + + Description : get peer certification + + Example : + + void example(void) + { + SSL *ssl; + X509 *peer; + + ... ... + + peer = SSL_get_peer_certificate(ssl); + } + From b97c00f2c1b367fb5d6e2602ccc21392282378ff Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 28 Sep 2016 20:41:11 +0800 Subject: [PATCH 037/285] components/openssl: add more .rst encoding type --- components/openssl/OpenSSL-APIs.rst | 1123 ++++++++++++++++++--------- 1 file changed, 748 insertions(+), 375 deletions(-) diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index 5a44794cd2..cb90a23c93 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -8,24 +8,31 @@ License 2.0 as described in the file LICENSE. Chapter Introduction ==================== -Chapter 1. SSL Context Method Create -Chapter 2. SSL Context Fucntion -Chapter 3. SSL Fucntion -Chapter 4. SSL X509 Certification and Private Key Function +- Chapter 1. SSL Context Method Create +- Chapter 2. SSL Context Fucntion +- Chapter 3. SSL Fucntion +- Chapter 4. SSL X509 Certification and Private Key Function Chapter 1. SSL Context Method Create ==================================== -1.1 const SSL_METHOD* SSLv23_client_method(void); - Arguments : none +1.1 const SSL_METHOD* ``SSLv23_client_method``(void) + + Arguments:: - Return : SSLV2 and 3 version SSL context client method point + none - Description : create the target SSL context method + Return:: - Example : + SSLV2 and 3 version SSL context client method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -35,15 +42,21 @@ Chapter 1. SSL Context Method Create } -1.2 const SSL_METHOD* TLSv1_client_method(void); +1.2 const SSL_METHOD* ``TLSv1_client_method``(void) - Arguments : none + Arguments:: - Return : TLSV1.0 version SSL context client method point + none - Description : create the target SSL context method + Return:: - Example : + TLSV1.0 version SSL context client method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -53,15 +66,21 @@ Chapter 1. SSL Context Method Create } -1.3 const SSL_METHOD* SSLv3_client_method(void); +1.3 const SSL_METHOD* ``SSLv3_client_method``(void) - Arguments : none + Arguments:: - Return : SSLV3.0 version SSL context client method point + none - Description : create the target SSL context method + Return:: - Example : + SSLV3.0 version SSL context client method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -71,15 +90,21 @@ Chapter 1. SSL Context Method Create } -1.4 const SSL_METHOD* TLSv1_1_client_method(void); +1.4 const SSL_METHOD* ``TLSv1_1_client_method``(void) - Arguments : none + Arguments:: - Return : TLSV1.1 version SSL context client method point + none - Description : create the target SSL context method + Return:: - Example : + TLSV1.1 version SSL context client method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -89,15 +114,21 @@ Chapter 1. SSL Context Method Create } -1.5 const SSL_METHOD* TLSv1_2_client_method(void); +1.5 const SSL_METHOD* ``TLSv1_2_client_method``(void) - Arguments : none + Arguments:: - Return : TLSV1.2 version SSL context client method point + none - Description : create the target SSL context method + Return:: - Example : + TLSV1.2 version SSL context client method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -107,15 +138,21 @@ Chapter 1. SSL Context Method Create } -1.6 const SSL_METHOD* SSLv23_server_method(void); +1.6 const SSL_METHOD* ``SSLv23_server_method``(void) - Arguments : none + Arguments:: - Return : SSLV2 and 3 version SSL context server method point + none - Description : create the target SSL context method + Return:: - Example : + SSLV2 and 3 version SSL context server method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -125,13 +162,19 @@ Chapter 1. SSL Context Method Create } -1.7 const SSL_METHOD* TLSv1_1_server_method(void); +1.7 const SSL_METHOD* ``TLSv1_1_server_method``(void) - Arguments : none + Arguments:: - Return : TLSV1.1 version SSL context server method point + none - Description : create the target SSL context method + Return:: + + TLSV1.1 version SSL context server method point + + Description:: + + create the target SSL context method Example : @@ -143,15 +186,21 @@ Chapter 1. SSL Context Method Create } -1.8 const SSL_METHOD* TLSv1_2_server_method(void); +1.8 const SSL_METHOD* ``TLSv1_2_server_method``(void) - Arguments : none + Arguments:: - Return : TLSV1.2 version SSL context server method point + none - Description : create the target SSL context method + Return:: - Example : + TLSV1.2 version SSL context server method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -161,15 +210,21 @@ Chapter 1. SSL Context Method Create } -1.9 const SSL_METHOD* TLSv1_server_method(void); +1.9 const SSL_METHOD* ``TLSv1_server_method``(void) - Arguments : none + Arguments:: - Return : TLSV1.0 version SSL context server method point + none - Description : create the target SSL context method + Return:: - Example : + TLSV1.0 version SSL context server method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -179,15 +234,21 @@ Chapter 1. SSL Context Method Create } -1.10 const SSL_METHOD* SSLv3_server_method(void); +1.10 const SSL_METHOD* ``SSLv3_server_method``(void) - Arguments : none + Arguments:: - Return : SSLV3.0 version SSL context server method point + none - Description : create the target SSL context method + Return:: - Example : + SSLV3.0 version SSL context server method point + + Description:: + + create the target SSL context method + + Example:: void example(void) { @@ -201,15 +262,22 @@ Chapter 1. SSL Context Method Create Chapter 2. SSL Context Fucntion =============================== -2.1 SSL_CTX* SSL_CTX_new(const SSL_METHOD *method); - Arguments : method - the SSL context method point +2.1 SSL_CTX* ``SSL_CTX_new``(const SSL_METHOD *method) + + Arguments:: - Return : context point + method - the SSL context method point - Description : create a SSL context + Return:: - Example : + context point + + Description:: + + create a SSL context + + Example:: void example(void) { @@ -219,15 +287,21 @@ Chapter 2. SSL Context Fucntion } -2.2 void SSL_CTX_free(SSL_CTX *ctx); +2.2 ``void SSL_CTX_free``(SSL_CTX *ctx) - Arguments : ctx - the SSL context point + Arguments:: - Return : none + ctx - the SSL context point - Description : free a SSL context + Return:: - Example : + none + + Description:: + + free a SSL context + + Example:: void example(void) { @@ -239,18 +313,23 @@ Chapter 2. SSL Context Fucntion } -2.3 int SSL_CTX_set_ssl_version(SSL_CTX *ctx, const SSL_METHOD *meth); +2.3 ``int SSL_CTX_set_ssl_version``(SSL_CTX *ctx, const SSL_METHOD *meth) - Arguments : ctx - SSL context point - meth - SSL method point + Arguments:: + + ctx - SSL context point + meth - SSL method point + + Return:: - Return : result 1 : OK 0 : failed - Description : set the SSL context version + Description:: - Example : + set the SSL context version + + Example:: void example(void) { @@ -263,15 +342,21 @@ Chapter 2. SSL Context Fucntion } -2.4 const SSL_METHOD *SSL_CTX_get_ssl_method(SSL_CTX *ctx); +2.4 const SSL_METHOD* ``SSL_CTX_get_ssl_method``(SSL_CTX *ctx) - Arguments : ctx - SSL context point + Arguments:: - Return : SSL context method + ctx - SSL context point - Description : get the SSL context method + Return:: - Example : + SSL context method + + Description:: + + get the SSL context method + + Example:: void example(void) { @@ -288,15 +373,22 @@ Chapter 2. SSL Context Fucntion Chapter 3. SSL Fucntion ======================= -3.1 SSL* SSL_new(SSL_CTX *ctx); - Arguments : ctx - SSL context point +3.1 SSL* ``SSL_new``(SSL_CTX *ctx) + + Arguments:: - Return : SSL method + ctx - SSL context point - Description : create a SSL + Return:: - Example : + SSL method + + Description:: + + create a SSL + + Example:: void example(void) { @@ -309,15 +401,21 @@ Chapter 3. SSL Fucntion } -3.2 void SSL_free(SSL *ssl); +3.2 void ``SSL_free``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : none + ssl - SSL point - Description : free SSL + Return:: - Example : + none + + Description:: + + free SSL + + Example:: void example(void) { @@ -329,18 +427,23 @@ Chapter 3. SSL Fucntion } -3.3 int SSL_do_handshake(SSL *ssl); +3.3 int ``SSL_do_handshake``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 1 : OK 0 : failed, connect is close by remote -1 : a error catch - Description : perform the SSL handshake + Description:: - Example : + perform the SSL handshake + + Example:: void example(void) { @@ -353,18 +456,23 @@ Chapter 3. SSL Fucntion } -3.4 int SSL_connect(SSL *ssl); +3.4 int ``SSL_connect``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 1 : OK 0 : failed, connect is close by remote -1 : a error catch - Description : connect to the remote SSL server + Description:: - Example : + connect to the remote SSL server + + Example:: void example(void) { @@ -377,18 +485,23 @@ Chapter 3. SSL Fucntion } -3.5 int SSL_accept(SSL *ssl); +3.5 int ``SSL_accept``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 1 : OK 0 : failed, connect is close by remote -1 : a error catch - Description : accept the remote connection + Description:: - Example : + accept the remote connection + + Example:: void example(void) { @@ -401,18 +514,23 @@ Chapter 3. SSL Fucntion } -3.6 int SSL_shutdown(SSL *ssl); +3.6 int ``SSL_shutdown``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 1 : OK 0 : failed, connect is close by remote -1 : a error catch - Description : shutdown the connection + Description:: - Example : + shutdown the connection + + Example:: void example(void) { @@ -425,17 +543,22 @@ Chapter 3. SSL Fucntion } -3.7 int SSL_clear(SSL *ssl); +3.7 int ``SSL_clear``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 1 : OK 0 : failed - Description : shutdown the connection + Description:: - Example : + shutdown the connection + + Example:: void example(void) { @@ -448,20 +571,25 @@ Chapter 3. SSL Fucntion } -3.8 int SSL_read(SSL *ssl, void *buffer, int len); +3.8 int ``SSL_read``(SSL *ssl, void *buffer, int len) - Arguments : ssl - point - buffer - data buffer point - len - data length + Arguments:: + + ssl - point + buffer - data buffer point + len - data length + + Return:: - Return : result > 0 : OK, and return received data bytes = 0 : no data received or connection is closed < 0 : an error catch - Description : read data from remote + Description:: - Example : + read data from remote + + Example:: void example(void) { @@ -475,20 +603,25 @@ Chapter 3. SSL Fucntion ret = SSL_read(ssl, buf, len); } -3.9 int SSL_write(SSL *ssl, const void *buffer, int len); +3.9 int ``SSL_write``(SSL *ssl, const void *buffer, int len) - Arguments : ssl - SSL point - buffer - data buffer point - len - data length + Arguments:: + + ssl - SSL point + buffer - data buffer point + len - data length + + Return:: - Return : result > 0 : OK, and return received data bytes = 0 : no data sent or connection is closed < 0 : an error catch - Description : send the data to remote + Description:: - Example : + send the data to remote + + Example:: void example(void) { @@ -503,15 +636,21 @@ Chapter 3. SSL Fucntion } -3.10 SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); +3.10 ``SSL_CTX *SSL_get_SSL_CTX``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : SSL context + ssl - SSL point + + Return:: + + SSL context - Description : get SSL context of the SSL + Description:: - Example : + get SSL context of the SSL + + Example:: void example(void) { @@ -524,15 +663,21 @@ Chapter 3. SSL Fucntion } -3.11 int SSL_get_shutdown(const SSL *ssl); +3.11 int ``SSL_get_shutdown``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : shutdown mode + ssl - SSL point + + Return:: + + shutdown mode - Description : get SSL shutdown mode + Description:: - Example : + get SSL shutdown mode + + Example:: void example(void) { @@ -545,15 +690,21 @@ Chapter 3. SSL Fucntion } -3.12 void SSL_set_shutdown(SSL *ssl, int mode); +3.12 void ``SSL_set_shutdown``(SSL *ssl, int mode) - Arguments : ssl - SSL point + Arguments:: - Return : shutdown mode + ssl - SSL point + + Return:: + + shutdown mode - Description : set SSL shutdown mode + Description:: - Example : + set SSL shutdown mode + + Example:: void example(void) { @@ -566,15 +717,21 @@ Chapter 3. SSL Fucntion } -3.13 const SSL_METHOD *SSL_get_ssl_method(SSL *ssl); +3.13 const SSL_METHOD* ``SSL_get_ssl_method``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : SSL method + ssl - SSL point + + Return:: + + SSL method - Description : set SSL shutdown mode + Description:: - Example : + set SSL shutdown mode + + Example:: void example(void) { @@ -587,18 +744,23 @@ Chapter 3. SSL Fucntion } -3.14 int SSL_set_ssl_method(SSL *ssl, const SSL_METHOD *method); +3.14 int ``SSL_set_ssl_method``(SSL *ssl, const SSL_METHOD *method) - Arguments : ssl - SSL point - meth - SSL method point + Arguments:: + + ssl - SSL point + meth - SSL method point + + Return:: - Return : result 1 : OK 0 : failed - Description : set the SSL method + Description:: - Example : + set the SSL method + + Example:: void example(void) { @@ -612,15 +774,21 @@ Chapter 3. SSL Fucntion } -3.15 int SSL_pending(const SSL *ssl); +3.15 int ``SSL_pending``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : data bytes + ssl - SSL point + + Return:: + + data bytes - Description : get received data bytes + Description:: - Example : + get received data bytes + + Example:: void example(void) { @@ -633,17 +801,22 @@ Chapter 3. SSL Fucntion } -3.16 int SSL_has_pending(const SSL *ssl); +3.16 int ``SSL_has_pending``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 1 : Yes 0 : No - Description : check if data is received + Description:: - Example : + check if data is received + + Example:: void example(void) { @@ -656,17 +829,22 @@ Chapter 3. SSL Fucntion } -3.17 int SSL_get_fd(const SSL *ssl); +3.17 int ``SSL_get_fd``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result >= 0 : socket id < 0 : a error catch - Description : get the socket of the SSL + Description:: - Example : + get the socket of the SSL + + Example:: void example(void) { @@ -679,17 +857,22 @@ Chapter 3. SSL Fucntion } -3.18 int SSL_get_rfd(const SSL *ssl); +3.18 int ``SSL_get_rfd``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result >= 0 : socket id < 0 : a error catch - Description : get the read only socket of the SSL + Description:: - Example : + get the read only socket of the SSL + + Example:: void example(void) { @@ -702,17 +885,22 @@ Chapter 3. SSL Fucntion } -3.19 int SSL_get_wfd(const SSL *ssl); +3.19 int ``SSL_get_wfd``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result >= 0 : socket id < 0 : a error catch - Description : get the write only socket of the SSL + Description:: - Example : + get the write only socket of the SSL + + Example:: void example(void) { @@ -725,18 +913,23 @@ Chapter 3. SSL Fucntion } -3.20 int SSL_set_fd(SSL *ssl, int fd); +3.20 int ``SSL_set_fd``(SSL *ssl, int fd) - Arguments : ssl - SSL point - fd - socket id + Arguments:: + + ssl - SSL point + fd - socket id + + Return:: - Return : result 1 : OK 0 : failed - Description : set socket to SSL + Description:: - Example : + set socket to SSL + + Example:: void example(void) { @@ -750,18 +943,23 @@ Chapter 3. SSL Fucntion } -3.21 int SSL_set_rfd(SSL *ssl, int fd); +3.21 int ``SSL_set_rfd``(SSL *ssl, int fd) - Arguments : ssl - SSL point - fd - socket id + Arguments:: + + ssl - SSL point + fd - socket id + + Return:: - Return : result 1 : OK 0 : failed - Description : set read only socket to SSL + Description:: - Example : + set read only socket to SSL + + Example:: void example(void) { @@ -775,18 +973,23 @@ Chapter 3. SSL Fucntion } -3.22 int SSL_set_wfd(SSL *ssl, int fd); +3.22 int ``SSL_set_wfd``(SSL *ssl, int fd) - Arguments : ssl - SSL point - fd - socket id + Arguments:: + + ssl - SSL point + fd - socket id + + Return:: - Return : result 1 : OK 0 : failed - Description : set write only socket to SSL + Description:: - Example : + set write only socket to SSL + + Example:: void example(void) { @@ -800,15 +1003,21 @@ Chapter 3. SSL Fucntion } -3.23 int SSL_version(const SSL *ssl); +3.23 int ``SSL_version``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : SSL version + ssl - SSL point + + Return:: + + SSL version - Description : get SSL version + Description:: - Example : + get SSL version + + Example:: void example(void) { @@ -821,15 +1030,21 @@ Chapter 3. SSL Fucntion } -3.24 const char *SSL_get_version(const SSL *ssl); +3.24 const char* ``SSL_get_version``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : SSL version string + ssl - SSL point + + Return:: + + SSL version string - Description : get the SSL current version string + Description:: - Example : + get the SSL current version string + + Example:: void example(void) { @@ -842,15 +1057,21 @@ Chapter 3. SSL Fucntion } -3.25 OSSL_HANDSHAKE_STATE SSL_get_state(const SSL *ssl); +3.25 OSSL_HANDSHAKE_STATE ``SSL_get_state``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : SSL state + ssl - SSL point + + Return:: + + SSL state - Description : get the SSL state + Description:: - Example : + get the SSL state + + Example:: void example(void) { @@ -863,15 +1084,21 @@ Chapter 3. SSL Fucntion } -3.26 const char *SSL_alert_desc_string(int value); +3.26 const char* ``SSL_alert_desc_string``(int value) - Arguments : value - SSL description + Arguments:: - Return : alert value string + value - SSL description + + Return:: + + alert value string - Description : get alert description string + Description:: - Example : + get alert description string + + Example:: void example(void) { @@ -884,15 +1111,21 @@ Chapter 3. SSL Fucntion } -3.27 const char *SSL_alert_desc_string_long(int value); +3.27 const char* ``SSL_alert_desc_string_long``(int value) - Arguments : value - SSL description + Arguments:: - Return : alert value long string + value - SSL description + + Return:: + + alert value long string - Description : get alert description long string + Description:: - Example : + get alert description long string + + Example:: void example(void) { @@ -905,15 +1138,21 @@ Chapter 3. SSL Fucntion } -3.28 const char *SSL_alert_type_string(int value); +3.28 const char* ``SSL_alert_type_string``(int value) - Arguments : value - SSL type description + Arguments:: - Return : alert type string + value - SSL type description + + Return:: + + alert type string - Description : get alert type string + Description:: - Example : + get alert type string + + Example:: void example(void) { @@ -926,15 +1165,21 @@ Chapter 3. SSL Fucntion } -3.29 const char *SSL_alert_type_string_long(int value); +3.29 const char* ``SSL_alert_type_string_long``(int value) - Arguments : value - SSL type description + Arguments:: - Return : alert type long string + value - SSL type description + + Return:: + + alert type long string - Description : get alert type long string + Description:: - Example : + get alert type long string + + Example:: void example(void) { @@ -946,15 +1191,21 @@ Chapter 3. SSL Fucntion str = SSL_alert_type_string_long(val); } -3.30 const char *SSL_rstate_string(SSL *ssl); +3.30 const char* ``SSL_rstate_string``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : state string + ssl - SSL point + + Return:: + + state string - Description : get the state string where SSL is reading + Description:: - Example : + get the state string where SSL is reading + + Example:: void example(void) { @@ -967,15 +1218,21 @@ Chapter 3. SSL Fucntion } -3.31 const char *SSL_rstate_string_long(SSL *ssl); +3.31 const char* ``SSL_rstate_string_long``(SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : state long string + ssl - SSL point + + Return:: + + state long string - Description : get the state long string where SSL is reading + Description:: - Example : + get the state long string where SSL is reading + + Example:: void example(void) { @@ -988,15 +1245,48 @@ Chapter 3. SSL Fucntion } -3.32 char *SSL_state_string(const SSL *ssl); +3.32 const char* ``SSL_state_string``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : state string + ssl - SSL point + + Return:: + + state string - Description : get the state string + Description:: - Example : + get the state string + + Example:: + + void example(void) + { + SSL *ssl; + const char *str; + + ... ... + + str = SSL_state_string(ssl); + } + + +3.33 char* ``SSL_state_string_long``(const SSL *ssl) + + Arguments:: + + ssl - SSL point + + Return:: + + state long string + + Description:: + + get the state long string + + Example:: void example(void) { @@ -1009,37 +1299,22 @@ Chapter 3. SSL Fucntion } -3.33 char *SSL_state_string_long(const SSL *ssl); +3.34 int ``SSL_get_error``(const SSL *ssl, int ret_code) - Arguments : ssl - SSL point + Arguments:: - Return : state long string + ssl - SSL point + ret_code - SSL return code + + Return:: + + SSL error number - Description : get the state long string + Description:: - Example : + get SSL error code - void example(void) - { - SSL *ssl; - char *str; - - ... ... - - str = SSL_state_string(ssl); - } - - -3.34 int SSL_get_error(const SSL *ssl, int ret_code); - - Arguments : ssl - SSL point - ret_code - SSL return code - - Return : SSL error number - - Description : get SSL error code - - Example : + Example:: void example(void) { @@ -1052,16 +1327,22 @@ Chapter 3. SSL Fucntion err = SSL_get_error(ssl, ret); } -3.35 void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); +3.35 void ``SSL_CTX_set_default_read_buffer_len``(SSL_CTX *ctx, size_t len) - Arguments : ctx - SSL context point - len - read buffer length + Arguments:: - Return : none + ctx - SSL context point + len - read buffer length + + Return:: + + none - Description : set the SSL context read buffer length + Description:: - Example : + set the SSL context read buffer length + + Example:: void example(void) { @@ -1074,16 +1355,22 @@ Chapter 3. SSL Fucntion } -3.36 void SSL_set_default_read_buffer_len(SSL *ssl, size_t len); +3.36 void ``SSL_set_default_read_buffer_len``(SSL *ssl, size_t len) - Arguments : ssl - SSL point - len - read buffer length + Arguments:: - Return : none + ssl - SSL point + len - read buffer length + + Return:: + + none - Description : set the SSL read buffer length + Description:: - Example : + set the SSL read buffer length + + Example:: void example(void) { @@ -1096,15 +1383,21 @@ Chapter 3. SSL Fucntion } -3.37 int SSL_want(const SSL *ssl); +3.37 int ``SSL_want``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : specifical statement + ssl - SSL point + + Return:: + + specifical statement - Description : get the SSL specifical statement + Description:: - Example : + get the SSL specifical statement + + Example:: void example(void) { @@ -1117,17 +1410,22 @@ Chapter 3. SSL Fucntion } -3.38 int SSL_want_nothing(const SSL *ssl); +3.38 int ``SSL_want_nothing``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 0 : false 1 : true - Description : check if SSL want nothing + Description:: - Example : + check if SSL want nothing + + Example:: void example(void) { @@ -1140,17 +1438,22 @@ Chapter 3. SSL Fucntion } -3.39 int SSL_want_read(const SSL *ssl); +3.39 int ``SSL_want_read``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 0 : false 1 : true - Description : check if SSL want to read + Description:: - Example : + check if SSL want to read + + Example:: void example(void) { @@ -1163,17 +1466,22 @@ Chapter 3. SSL Fucntion } -3.40 int SSL_want_write(const SSL *ssl); +3.40 int ``SSL_want_write``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: + + ssl - SSL point + + Return:: - Return : result 0 : false 1 : true - Description : check if SSL want to write + Description:: - Example : + check if SSL want to write + + Example:: void example(void) { @@ -1189,18 +1497,25 @@ Chapter 3. SSL Fucntion Chapter 4. SSL X509 Certification and Private Key Function ========================================================== -4.1 X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len); - Arguments : cert - a point pointed to X509 certification - buffer - a point pointed to the certification context memory point - length - certification bytes +4.1 X509* ``d2i_X509``(X509 **cert, const unsigned char *buffer, long len) + + Arguments:: - Return : X509 certification object point + cert - a point pointed to X509 certification + buffer - a point pointed to the certification context memory point + length - certification bytes + + Return:: + + X509 certification object point - Description : load a character certification context into system context. If '*cert' is pointed to the - certification, then load certification into it. Or create a new X509 certification object + Description:: - Example : + load a character certification context into system context. If '*cert' is pointed to the + certification, then load certification into it. Or create a new X509 certification object + + Example:: void example(void) { @@ -1214,18 +1529,23 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.2 int SSL_add_client_CA(SSL *ssl, X509 *x); +4.2 int ``SSL_add_client_CA``(SSL *ssl, X509 *x) - Arguments : ssl - SSL point - x - CA certification point + Arguments:: - Return : result + ssl - SSL point + x - CA certification point + + Return:: + 1 : OK 0 : failed - Description : add CA client certification into the SSL + Description:: - Example : + add CA client certification into the SSL + + Example:: void example(void) { @@ -1239,18 +1559,23 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.3 int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x); +4.3 int ``SSL_CTX_add_client_CA``(SSL_CTX *ctx, X509 *x) - Arguments : ctx - SSL context point - x - CA certification point + Arguments:: - Return : result + ctx - SSL context point + x - CA certification point + + Return:: + 1 : OK 0 : failed - Description : add CA client certification into the SSL context + Description:: - Example : + add CA client certification into the SSL context + + Example:: void example(void) { @@ -1264,15 +1589,21 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.4 X509 *SSL_get_certificate(const SSL *ssl); +4.4 X509* ``SSL_get_certificate``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : SSL certification point + ssl - SSL point + + Return:: + + SSL certification point - Description : get the SSL certification point + Description:: - Example : + get the SSL certification point + + Example:: void example(void) { @@ -1285,15 +1616,21 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.5 long SSL_get_verify_result(const SSL *ssl); +4.5 long ``SSL_get_verify_result``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : the result of verifying + ssl - SSL point + + Return:: + + the result of verifying - Description : get the verifying result of the SSL certification + Description:: - Example : + get the verifying result of the SSL certification + + Example:: void example(void) { @@ -1306,18 +1643,23 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.6 int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); +4.6 int ``SSL_CTX_use_certificate``(SSL_CTX *ctx, X509 *x) - Arguments : ctx - the SSL context point - pkey - certification object point + Arguments:: + + ctx - the SSL context point + pkey - certification object point + + Return:: - Return : result 1 : OK 0 : failed - Description : load the certification into the SSL_CTX or SSL object + Description:: - Example : + load the certification into the SSL_CTX or SSL object + + Example:: void example(void) { @@ -1331,19 +1673,24 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.7 int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); +4.7 int ``SSL_CTX_use_certificate_ASN1``(SSL_CTX *ctx, int len, const unsigned char *d) - Arguments : ctx - SSL context point - len - certification length - d - data point + Arguments:: - Return : result + ctx - SSL context point + len - certification length + d - data point + + Return:: + 1 : OK 0 : failed - Description : load the ASN1 certification into SSL context + Description:: - Example : + load the ASN1 certification into SSL context + + Example:: void example(void) { @@ -1358,18 +1705,23 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.8 int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); +4.8 int ``SSL_CTX_use_PrivateKey``(SSL_CTX *ctx, EVP_PKEY *pkey) - Arguments : ctx - SSL context point - pkey - private key object point + Arguments:: - Return : result + ctx - SSL context point + pkey - private key object point + + Return:: + 1 : OK 0 : failed - Description : load the private key into the context object + Description:: - Example : + load the private key into the context object + + Example:: void example(void) { @@ -1383,19 +1735,24 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.9 int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, const unsigned char *d, long len); +4.9 int ``SSL_CTX_use_PrivateKey_ASN1``(int pk, SSL_CTX *ctx, const unsigned char *d, long len) - Arguments : ctx - SSL context point - d - data point - len - private key length + Arguments:: + + ctx - SSL context point + d - data point + len - private key length + + Return:: - Return : result 1 : OK 0 : failed - Description : load the ASN1 private key into SSL context + Description:: - Example : + load the ASN1 private key into SSL context + + Example:: void example(void) { @@ -1411,19 +1768,24 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.10 int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); +4.10 int ``SSL_CTX_use_RSAPrivateKey_ASN1``(SSL_CTX *ctx, const unsigned char *d, long len) - Arguments : ctx - SSL context point - d - data point - len - private key length + Arguments:: + + ctx - SSL context point + d - data point + len - private key length + + Return:: - Return : result 1 : OK 0 : failed - Description : load the RSA ASN1 private key into SSL context + Description:: - Example : + load the RSA ASN1 private key into SSL context + + Example:: void example(void) { @@ -1438,19 +1800,24 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.11 int SSL_use_certificate_ASN1(SSL *ssl, int len, const unsigned char *d); +4.11 int ``SSL_use_certificate_ASN1``(SSL *ssl, int len, const unsigned char *d) - Arguments : ssl - SSL point - len - data bytes - d - data point + Arguments:: + + ssl - SSL point + len - data bytes + d - data point + + Return:: - Return : result 1 : OK 0 : failed - Description : load certification into the SSL + Description:: - Example : + load certification into the SSL + + Example:: void example(void) { @@ -1465,15 +1832,21 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.12 X509 *SSL_get_peer_certificate(const SSL *ssl); +4.12 X509* ``SSL_get_peer_certificate``(const SSL *ssl) - Arguments : ssl - SSL point + Arguments:: - Return : peer certification + ssl - SSL point + + Return:: + + peer certification - Description : get peer certification + Description:: - Example : + get peer certification + + Example:: void example(void) { From de587a2e0d1418d45c9b7880ed07a68237975f3d Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 28 Sep 2016 20:46:45 +0800 Subject: [PATCH 038/285] components/openssl: fix .rst file encoding error --- components/openssl/OpenSSL-APIs.rst | 132 ++++++++++++++-------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index cb90a23c93..6a8a68c1ff 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -18,7 +18,7 @@ Chapter 1. SSL Context Method Create ==================================== -1.1 const SSL_METHOD* ``SSLv23_client_method``(void) +1.1 const SSL_METHOD* ``SSLv23_client_method`` (void) Arguments:: @@ -42,7 +42,7 @@ Chapter 1. SSL Context Method Create } -1.2 const SSL_METHOD* ``TLSv1_client_method``(void) +1.2 const SSL_METHOD* ``TLSv1_client_method`` (void) Arguments:: @@ -66,7 +66,7 @@ Chapter 1. SSL Context Method Create } -1.3 const SSL_METHOD* ``SSLv3_client_method``(void) +1.3 const SSL_METHOD* ``SSLv3_client_method`` (void) Arguments:: @@ -90,7 +90,7 @@ Chapter 1. SSL Context Method Create } -1.4 const SSL_METHOD* ``TLSv1_1_client_method``(void) +1.4 const SSL_METHOD* ``TLSv1_1_client_method`` (void) Arguments:: @@ -114,7 +114,7 @@ Chapter 1. SSL Context Method Create } -1.5 const SSL_METHOD* ``TLSv1_2_client_method``(void) +1.5 const SSL_METHOD* ``TLSv1_2_client_method`` (void) Arguments:: @@ -138,7 +138,7 @@ Chapter 1. SSL Context Method Create } -1.6 const SSL_METHOD* ``SSLv23_server_method``(void) +1.6 const SSL_METHOD* ``SSLv23_server_method`` (void) Arguments:: @@ -162,7 +162,7 @@ Chapter 1. SSL Context Method Create } -1.7 const SSL_METHOD* ``TLSv1_1_server_method``(void) +1.7 const SSL_METHOD* ``TLSv1_1_server_method`` (void) Arguments:: @@ -186,7 +186,7 @@ Chapter 1. SSL Context Method Create } -1.8 const SSL_METHOD* ``TLSv1_2_server_method``(void) +1.8 const SSL_METHOD* ``TLSv1_2_server_method`` (void) Arguments:: @@ -210,7 +210,7 @@ Chapter 1. SSL Context Method Create } -1.9 const SSL_METHOD* ``TLSv1_server_method``(void) +1.9 const SSL_METHOD* ``TLSv1_server_method`` (void) Arguments:: @@ -234,7 +234,7 @@ Chapter 1. SSL Context Method Create } -1.10 const SSL_METHOD* ``SSLv3_server_method``(void) +1.10 const SSL_METHOD* ``SSLv3_server_method`` (void) Arguments:: @@ -263,7 +263,7 @@ Chapter 2. SSL Context Fucntion =============================== -2.1 SSL_CTX* ``SSL_CTX_new``(const SSL_METHOD *method) +2.1 SSL_CTX* ``SSL_CTX_new`` (const SSL_METHOD *method) Arguments:: @@ -287,7 +287,7 @@ Chapter 2. SSL Context Fucntion } -2.2 ``void SSL_CTX_free``(SSL_CTX *ctx) +2.2 ``void SSL_CTX_free`` (SSL_CTX *ctx) Arguments:: @@ -313,7 +313,7 @@ Chapter 2. SSL Context Fucntion } -2.3 ``int SSL_CTX_set_ssl_version``(SSL_CTX *ctx, const SSL_METHOD *meth) +2.3 ``int SSL_CTX_set_ssl_version`` (SSL_CTX *ctx, const SSL_METHOD *meth) Arguments:: @@ -342,7 +342,7 @@ Chapter 2. SSL Context Fucntion } -2.4 const SSL_METHOD* ``SSL_CTX_get_ssl_method``(SSL_CTX *ctx) +2.4 const SSL_METHOD* ``SSL_CTX_get_ssl_method`` (SSL_CTX *ctx) Arguments:: @@ -374,7 +374,7 @@ Chapter 3. SSL Fucntion ======================= -3.1 SSL* ``SSL_new``(SSL_CTX *ctx) +3.1 SSL* ``SSL_new`` (SSL_CTX *ctx) Arguments:: @@ -401,7 +401,7 @@ Chapter 3. SSL Fucntion } -3.2 void ``SSL_free``(SSL *ssl) +3.2 void ``SSL_free`` (SSL *ssl) Arguments:: @@ -427,7 +427,7 @@ Chapter 3. SSL Fucntion } -3.3 int ``SSL_do_handshake``(SSL *ssl) +3.3 int ``SSL_do_handshake`` (SSL *ssl) Arguments:: @@ -456,7 +456,7 @@ Chapter 3. SSL Fucntion } -3.4 int ``SSL_connect``(SSL *ssl) +3.4 int ``SSL_connect`` (SSL *ssl) Arguments:: @@ -485,7 +485,7 @@ Chapter 3. SSL Fucntion } -3.5 int ``SSL_accept``(SSL *ssl) +3.5 int ``SSL_accept`` (SSL *ssl) Arguments:: @@ -514,7 +514,7 @@ Chapter 3. SSL Fucntion } -3.6 int ``SSL_shutdown``(SSL *ssl) +3.6 int ``SSL_shutdown`` (SSL *ssl) Arguments:: @@ -543,7 +543,7 @@ Chapter 3. SSL Fucntion } -3.7 int ``SSL_clear``(SSL *ssl) +3.7 int ``SSL_clear`` (SSL *ssl) Arguments:: @@ -571,7 +571,7 @@ Chapter 3. SSL Fucntion } -3.8 int ``SSL_read``(SSL *ssl, void *buffer, int len) +3.8 int ``SSL_read`` (SSL *ssl, void *buffer, int len) Arguments:: @@ -603,7 +603,7 @@ Chapter 3. SSL Fucntion ret = SSL_read(ssl, buf, len); } -3.9 int ``SSL_write``(SSL *ssl, const void *buffer, int len) +3.9 int ``SSL_write`` (SSL *ssl, const void *buffer, int len) Arguments:: @@ -636,7 +636,7 @@ Chapter 3. SSL Fucntion } -3.10 ``SSL_CTX *SSL_get_SSL_CTX``(const SSL *ssl) +3.10 ``SSL_CTX *SSL_get_SSL_CTX`` (const SSL *ssl) Arguments:: @@ -663,7 +663,7 @@ Chapter 3. SSL Fucntion } -3.11 int ``SSL_get_shutdown``(const SSL *ssl) +3.11 int ``SSL_get_shutdown`` (const SSL *ssl) Arguments:: @@ -690,7 +690,7 @@ Chapter 3. SSL Fucntion } -3.12 void ``SSL_set_shutdown``(SSL *ssl, int mode) +3.12 void ``SSL_set_shutdown`` (SSL *ssl, int mode) Arguments:: @@ -717,7 +717,7 @@ Chapter 3. SSL Fucntion } -3.13 const SSL_METHOD* ``SSL_get_ssl_method``(SSL *ssl) +3.13 const SSL_METHOD* ``SSL_get_ssl_method`` (SSL *ssl) Arguments:: @@ -744,7 +744,7 @@ Chapter 3. SSL Fucntion } -3.14 int ``SSL_set_ssl_method``(SSL *ssl, const SSL_METHOD *method) +3.14 int ``SSL_set_ssl_method`` (SSL *ssl, const SSL_METHOD *method) Arguments:: @@ -774,7 +774,7 @@ Chapter 3. SSL Fucntion } -3.15 int ``SSL_pending``(const SSL *ssl) +3.15 int ``SSL_pending`` (const SSL *ssl) Arguments:: @@ -801,7 +801,7 @@ Chapter 3. SSL Fucntion } -3.16 int ``SSL_has_pending``(const SSL *ssl) +3.16 int ``SSL_has_pending`` (const SSL *ssl) Arguments:: @@ -829,7 +829,7 @@ Chapter 3. SSL Fucntion } -3.17 int ``SSL_get_fd``(const SSL *ssl) +3.17 int ``SSL_get_fd`` (const SSL *ssl) Arguments:: @@ -857,7 +857,7 @@ Chapter 3. SSL Fucntion } -3.18 int ``SSL_get_rfd``(const SSL *ssl) +3.18 int ``SSL_get_rfd`` (const SSL *ssl) Arguments:: @@ -885,7 +885,7 @@ Chapter 3. SSL Fucntion } -3.19 int ``SSL_get_wfd``(const SSL *ssl) +3.19 int ``SSL_get_wfd`` (const SSL *ssl) Arguments:: @@ -913,7 +913,7 @@ Chapter 3. SSL Fucntion } -3.20 int ``SSL_set_fd``(SSL *ssl, int fd) +3.20 int ``SSL_set_fd`` (SSL *ssl, int fd) Arguments:: @@ -943,7 +943,7 @@ Chapter 3. SSL Fucntion } -3.21 int ``SSL_set_rfd``(SSL *ssl, int fd) +3.21 int ``SSL_set_rfd`` (SSL *ssl, int fd) Arguments:: @@ -973,7 +973,7 @@ Chapter 3. SSL Fucntion } -3.22 int ``SSL_set_wfd``(SSL *ssl, int fd) +3.22 int ``SSL_set_wfd`` (SSL *ssl, int fd) Arguments:: @@ -1003,7 +1003,7 @@ Chapter 3. SSL Fucntion } -3.23 int ``SSL_version``(const SSL *ssl) +3.23 int ``SSL_version`` (const SSL *ssl) Arguments:: @@ -1030,7 +1030,7 @@ Chapter 3. SSL Fucntion } -3.24 const char* ``SSL_get_version``(const SSL *ssl) +3.24 const char* ``SSL_get_version`` (const SSL *ssl) Arguments:: @@ -1057,7 +1057,7 @@ Chapter 3. SSL Fucntion } -3.25 OSSL_HANDSHAKE_STATE ``SSL_get_state``(const SSL *ssl) +3.25 OSSL_HANDSHAKE_STATE ``SSL_get_state`` (const SSL *ssl) Arguments:: @@ -1084,7 +1084,7 @@ Chapter 3. SSL Fucntion } -3.26 const char* ``SSL_alert_desc_string``(int value) +3.26 const char* ``SSL_alert_desc_string`` (int value) Arguments:: @@ -1111,7 +1111,7 @@ Chapter 3. SSL Fucntion } -3.27 const char* ``SSL_alert_desc_string_long``(int value) +3.27 const char* ``SSL_alert_desc_string_long`` (int value) Arguments:: @@ -1138,7 +1138,7 @@ Chapter 3. SSL Fucntion } -3.28 const char* ``SSL_alert_type_string``(int value) +3.28 const char* ``SSL_alert_type_string`` (int value) Arguments:: @@ -1165,7 +1165,7 @@ Chapter 3. SSL Fucntion } -3.29 const char* ``SSL_alert_type_string_long``(int value) +3.29 const char* ``SSL_alert_type_string_long`` (int value) Arguments:: @@ -1191,7 +1191,7 @@ Chapter 3. SSL Fucntion str = SSL_alert_type_string_long(val); } -3.30 const char* ``SSL_rstate_string``(SSL *ssl) +3.30 const char* ``SSL_rstate_string`` (SSL *ssl) Arguments:: @@ -1218,7 +1218,7 @@ Chapter 3. SSL Fucntion } -3.31 const char* ``SSL_rstate_string_long``(SSL *ssl) +3.31 const char* ``SSL_rstate_string_long`` (SSL *ssl) Arguments:: @@ -1245,7 +1245,7 @@ Chapter 3. SSL Fucntion } -3.32 const char* ``SSL_state_string``(const SSL *ssl) +3.32 const char* ``SSL_state_string`` (const SSL *ssl) Arguments:: @@ -1272,7 +1272,7 @@ Chapter 3. SSL Fucntion } -3.33 char* ``SSL_state_string_long``(const SSL *ssl) +3.33 char* ``SSL_state_string_long`` (const SSL *ssl) Arguments:: @@ -1299,7 +1299,7 @@ Chapter 3. SSL Fucntion } -3.34 int ``SSL_get_error``(const SSL *ssl, int ret_code) +3.34 int ``SSL_get_error`` (const SSL *ssl, int ret_code) Arguments:: @@ -1327,7 +1327,7 @@ Chapter 3. SSL Fucntion err = SSL_get_error(ssl, ret); } -3.35 void ``SSL_CTX_set_default_read_buffer_len``(SSL_CTX *ctx, size_t len) +3.35 void ``SSL_CTX_set_default_read_buffer_len`` (SSL_CTX *ctx, size_t len) Arguments:: @@ -1355,7 +1355,7 @@ Chapter 3. SSL Fucntion } -3.36 void ``SSL_set_default_read_buffer_len``(SSL *ssl, size_t len) +3.36 void ``SSL_set_default_read_buffer_len`` (SSL *ssl, size_t len) Arguments:: @@ -1383,7 +1383,7 @@ Chapter 3. SSL Fucntion } -3.37 int ``SSL_want``(const SSL *ssl) +3.37 int ``SSL_want`` (const SSL *ssl) Arguments:: @@ -1410,7 +1410,7 @@ Chapter 3. SSL Fucntion } -3.38 int ``SSL_want_nothing``(const SSL *ssl) +3.38 int ``SSL_want_nothing`` (const SSL *ssl) Arguments:: @@ -1438,7 +1438,7 @@ Chapter 3. SSL Fucntion } -3.39 int ``SSL_want_read``(const SSL *ssl) +3.39 int ``SSL_want_read`` (const SSL *ssl) Arguments:: @@ -1466,7 +1466,7 @@ Chapter 3. SSL Fucntion } -3.40 int ``SSL_want_write``(const SSL *ssl) +3.40 int ``SSL_want_write`` (const SSL *ssl) Arguments:: @@ -1498,7 +1498,7 @@ Chapter 4. SSL X509 Certification and Private Key Function ========================================================== -4.1 X509* ``d2i_X509``(X509 **cert, const unsigned char *buffer, long len) +4.1 X509* ``d2i_X509`` (X509 **cert, const unsigned char *buffer, long len) Arguments:: @@ -1529,7 +1529,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.2 int ``SSL_add_client_CA``(SSL *ssl, X509 *x) +4.2 int ``SSL_add_client_CA`` (SSL *ssl, X509 *x) Arguments:: @@ -1559,7 +1559,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.3 int ``SSL_CTX_add_client_CA``(SSL_CTX *ctx, X509 *x) +4.3 int ``SSL_CTX_add_client_CA`` (SSL_CTX *ctx, X509 *x) Arguments:: @@ -1589,7 +1589,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.4 X509* ``SSL_get_certificate``(const SSL *ssl) +4.4 X509* ``SSL_get_certificate`` (const SSL *ssl) Arguments:: @@ -1616,7 +1616,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.5 long ``SSL_get_verify_result``(const SSL *ssl) +4.5 long ``SSL_get_verify_result`` (const SSL *ssl) Arguments:: @@ -1643,7 +1643,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.6 int ``SSL_CTX_use_certificate``(SSL_CTX *ctx, X509 *x) +4.6 int ``SSL_CTX_use_certificate`` (SSL_CTX *ctx, X509 *x) Arguments:: @@ -1673,7 +1673,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.7 int ``SSL_CTX_use_certificate_ASN1``(SSL_CTX *ctx, int len, const unsigned char *d) +4.7 int ``SSL_CTX_use_certificate_ASN1`` (SSL_CTX *ctx, int len, const unsigned char *d) Arguments:: @@ -1705,7 +1705,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.8 int ``SSL_CTX_use_PrivateKey``(SSL_CTX *ctx, EVP_PKEY *pkey) +4.8 int ``SSL_CTX_use_PrivateKey`` (SSL_CTX *ctx, EVP_PKEY *pkey) Arguments:: @@ -1735,7 +1735,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.9 int ``SSL_CTX_use_PrivateKey_ASN1``(int pk, SSL_CTX *ctx, const unsigned char *d, long len) +4.9 int ``SSL_CTX_use_PrivateKey_ASN1`` (int pk, SSL_CTX *ctx, const unsigned char *d, long len) Arguments:: @@ -1768,7 +1768,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.10 int ``SSL_CTX_use_RSAPrivateKey_ASN1``(SSL_CTX *ctx, const unsigned char *d, long len) +4.10 int ``SSL_CTX_use_RSAPrivateKey_ASN1`` (SSL_CTX *ctx, const unsigned char *d, long len) Arguments:: @@ -1800,7 +1800,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.11 int ``SSL_use_certificate_ASN1``(SSL *ssl, int len, const unsigned char *d) +4.11 int ``SSL_use_certificate_ASN1`` (SSL *ssl, int len, const unsigned char *d) Arguments:: @@ -1832,7 +1832,7 @@ Chapter 4. SSL X509 Certification and Private Key Function } -4.12 X509* ``SSL_get_peer_certificate``(const SSL *ssl) +4.12 X509* ``SSL_get_peer_certificate`` (const SSL *ssl) Arguments:: From 2033068a72fd122561be0d91b79c9a6b652f597e Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Sun, 9 Oct 2016 16:42:49 +0800 Subject: [PATCH 039/285] components/openssl: add internal openssl X509 debug function --- .../openssl/include/internal/ssl_methods.h | 6 ++- .../openssl/include/internal/ssl_types.h | 2 + components/openssl/include/openssl/ssl.h | 22 +++++++++++ components/openssl/include/platform/ssl_pm.h | 1 + components/openssl/library/ssl_methods.c | 2 +- components/openssl/library/ssl_x509.c | 9 +++++ components/openssl/platform/ssl_pm.c | 38 +++++++++++++++++++ 7 files changed, 77 insertions(+), 3 deletions(-) diff --git a/components/openssl/include/internal/ssl_methods.h b/components/openssl/include/internal/ssl_methods.h index 7a63b9e949..cd2f8c0533 100644 --- a/components/openssl/include/internal/ssl_methods.h +++ b/components/openssl/include/internal/ssl_methods.h @@ -71,12 +71,14 @@ #define IMPLEMENT_X509_METHOD(func_name, \ new, \ free, \ - load) \ + load, \ + show_info) \ const X509_METHOD* func_name(void) { \ static const X509_METHOD func_name##_data LOCAL_ATRR = { \ new, \ free, \ - load \ + load, \ + show_info \ }; \ return &func_name##_data; \ } diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index c571865c1e..19944c7819 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -275,6 +275,8 @@ struct x509_method_st { void (*x509_free)(X509 *x); int (*x509_load)(X509 *x, const unsigned char *buf, int len); + + int (*x509_show_info)(X509 *x); }; struct pkey_method_st { diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index 1d115214fd..d8400e66b5 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -447,6 +447,28 @@ int SSL_pending(const SSL *ssl); */ int SSL_want_nothing(const SSL *ssl); +/** + * @brief check if SSL want to read + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_read(const SSL *ssl); + +/** + * @brief check if SSL want to write + * + * @param ssl - SSL point + * + * @return result + * 0 : false + * 1 : true + */ +int SSL_want_write(const SSL *ssl); + /** * @brief get the SSL context current method * diff --git a/components/openssl/include/platform/ssl_pm.h b/components/openssl/include/platform/ssl_pm.h index cf1d213799..a516d57422 100644 --- a/components/openssl/include/platform/ssl_pm.h +++ b/components/openssl/include/platform/ssl_pm.h @@ -42,6 +42,7 @@ OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl); void ssl_pm_set_bufflen(SSL *ssl, int len); +int x509_pm_show_info(X509 *x); int x509_pm_new(X509 *x, X509 *m_x); void x509_pm_free(X509 *x); int x509_pm_load(X509 *x, const unsigned char *buffer, int len); diff --git a/components/openssl/library/ssl_methods.c b/components/openssl/library/ssl_methods.c index 8159511c49..0002360846 100644 --- a/components/openssl/library/ssl_methods.c +++ b/components/openssl/library/ssl_methods.c @@ -71,7 +71,7 @@ IMPLEMENT_SSL_METHOD(SSL3_VERSION, -1, TLS_method_func, SSLv3_method); */ IMPLEMENT_X509_METHOD(X509_method, x509_pm_new, x509_pm_free, - x509_pm_load); + x509_pm_load, x509_pm_show_info); /** * @brief get private key object method diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index d060419e6a..06e6e7b544 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -17,6 +17,14 @@ #include "ssl_dbg.h" #include "ssl_port.h" +/** + * @brief show X509 certification information + */ +int __X509_show_info(X509 *x) +{ + return X509_METHOD_CALL(show_info, x); +} + /** * @brief create a X509 certification object according to input X509 certification */ @@ -256,3 +264,4 @@ X509 *SSL_get_peer_certificate(const SSL *ssl) return ssl->session->peer; } + diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 4bc631382f..bbe290f2a3 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -31,6 +31,8 @@ #define DEBUG_LOAD_BUF_STRING(str) #endif +#define X509_INFO_STRING_LENGTH 1024 + struct ssl_pm { /* local socket file description */ @@ -370,6 +372,42 @@ OSSL_HANDSHAKE_STATE ssl_pm_get_state(const SSL *ssl) return state; } +int x509_pm_show_info(X509 *x) +{ + int ret; + char *buf; + mbedtls_x509_crt *x509_crt; + struct x509_pm *x509_pm = x->x509_pm; + + if (x509_pm->x509_crt) + x509_crt = x509_pm->x509_crt; + else if (x509_pm->ex_crt) + x509_crt = x509_pm->ex_crt; + else + x509_crt = NULL; + + if (!x509_crt) + return -1; + + buf = ssl_malloc(X509_INFO_STRING_LENGTH); + if (!buf) + SSL_RET(failed1, ""); + + ret = mbedtls_x509_crt_info(buf, X509_INFO_STRING_LENGTH - 1, "", x509_crt); + if (ret <= 0) + SSL_RET(failed2, ""); + buf[ret] = 0; + + SSL_PRINT("%s", buf); + + return 0; + +failed2: + ssl_free(buf); +failed1: + return -1; +} + int x509_pm_new(X509 *x, X509 *m_x) { struct x509_pm *x509_pm; From 47e83ee65e4861a79b4396fca00ee4fa9427886d Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Sun, 9 Oct 2016 17:49:16 +0800 Subject: [PATCH 040/285] components/openssl: add SSL any version function setting --- components/openssl/platform/ssl_pm.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index bbe290f2a3..539e954c78 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -111,16 +111,19 @@ int ssl_pm_new(SSL *ssl) if (ret) SSL_ERR(ret, failed2, "mbedtls_ssl_config_defaults:[-0x%x]\n", -ret); - if (TLS1_2_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_3; - else if (TLS1_1_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_2; - else if (TLS1_VERSION == ssl->version) - version = MBEDTLS_SSL_MINOR_VERSION_1; - else - version = MBEDTLS_SSL_MINOR_VERSION_0; + if (TLS_ANY_VERSION != ssl->version) { + if (TLS1_2_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_3; + else if (TLS1_1_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_2; + else if (TLS1_VERSION == ssl->version) + version = MBEDTLS_SSL_MINOR_VERSION_1; + else + version = MBEDTLS_SSL_MINOR_VERSION_0; - //mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + mbedtls_ssl_conf_max_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + mbedtls_ssl_conf_min_version(&ssl_pm->conf, MBEDTLS_SSL_MAJOR_VERSION_3, version); + } mbedtls_ssl_conf_rng(&ssl_pm->conf, mbedtls_ctr_drbg_random, &ssl_pm->ctr_drbg); From 37a68ad6052b94ba4f3e969021eb5318bb4eb30f Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Sun, 9 Oct 2016 19:02:31 +0800 Subject: [PATCH 041/285] components/openssl: fix SSL X509 show message, leaking memory --- components/openssl/platform/ssl_pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 539e954c78..eadd323e70 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -401,6 +401,8 @@ int x509_pm_show_info(X509 *x) SSL_RET(failed2, ""); buf[ret] = 0; + ssl_free(buf); + SSL_PRINT("%s", buf); return 0; From 034da95abb17974e5ed7ec0bfa9d1ba629636986 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Sun, 9 Oct 2016 19:18:18 +0800 Subject: [PATCH 042/285] components/openssl: change SSL read or write statement after success --- components/openssl/library/ssl_lib.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 267d23f25f..9740282d13 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -390,7 +390,8 @@ int SSL_read(SSL *ssl, void *buffer, int len) ret = SSL_METHOD_CALL(read, ssl, buffer, len); - ssl->rwstate = SSL_NOTHING; + if (ret == len) + ssl->rwstate = SSL_NOTHING; return ret; } @@ -428,12 +429,10 @@ int SSL_write(SSL *ssl, const void *buffer, int len) } } while (ret > 0 && send_bytes); - ssl->rwstate = SSL_NOTHING; - - send_bytes = len - send_bytes; - if (send_bytes >= 0) - ret = send_bytes; - else + if (ret >= 0) { + ret = len - send_bytes; + ssl->rwstate = SSL_NOTHING; + } else ret = -1; return ret; From ecefb1305a08ba5bad49894273a3c47f82fbe0ab Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 10 Oct 2016 10:40:00 +0800 Subject: [PATCH 043/285] components/openssl: change header file relationship of level --- components/openssl/include/internal/ssl_stack.h | 11 +++++++++++ components/openssl/include/internal/ssl_types.h | 11 ----------- components/openssl/include/openssl/ssl.h | 1 - 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/components/openssl/include/internal/ssl_stack.h b/components/openssl/include/internal/ssl_stack.h index b37c8dffa9..7a7051a026 100644 --- a/components/openssl/include/internal/ssl_stack.h +++ b/components/openssl/include/internal/ssl_stack.h @@ -7,6 +7,17 @@ #include "ssl_types.h" +#define STACK_OF(type) struct stack_st_##type + +#define SKM_DEFINE_STACK_OF(t1, t2, t3) \ + STACK_OF(t1); \ + static ossl_inline STACK_OF(t1) *sk_##t1##_new_null(void) \ + { \ + return (STACK_OF(t1) *)OPENSSL_sk_new_null(); \ + } \ + +#define DEFINE_STACK_OF(t) SKM_DEFINE_STACK_OF(t, t, t) + /** * @brief create a openssl stack object * diff --git a/components/openssl/include/internal/ssl_types.h b/components/openssl/include/internal/ssl_types.h index 19944c7819..5aaee94176 100644 --- a/components/openssl/include/internal/ssl_types.h +++ b/components/openssl/include/internal/ssl_types.h @@ -37,17 +37,6 @@ typedef void BIO; #define X509_METHOD_CALL(f, x, ...) x->method->x509_##f(x, ##__VA_ARGS__) #define EVP_PKEY_METHOD_CALL(f, k, ...) k->method->pkey_##f(k, ##__VA_ARGS__) -#define STACK_OF(type) struct stack_st_##type - -#define SKM_DEFINE_STACK_OF(t1, t2, t3) \ - STACK_OF(t1); \ - static ossl_inline STACK_OF(t1) *sk_##t1##_new_null(void) \ - { \ - return (STACK_OF(t1) *)OPENSSL_sk_new_null(); \ - } \ - -#define DEFINE_STACK_OF(t) SKM_DEFINE_STACK_OF(t, t, t) - typedef int (*OPENSSL_sk_compfunc)(const void *, const void *); struct stack_st; diff --git a/components/openssl/include/openssl/ssl.h b/components/openssl/include/openssl/ssl.h index d8400e66b5..7f8eb88302 100644 --- a/components/openssl/include/openssl/ssl.h +++ b/components/openssl/include/openssl/ssl.h @@ -19,7 +19,6 @@ extern "C" { #endif -#include "platform/ssl_port.h" #include "internal/ssl_x509.h" #include "internal/ssl_pkey.h" From 5d60a1153d04ac90dd037b39bc79392bca72c02c Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 10 Oct 2016 11:18:45 +0800 Subject: [PATCH 044/285] components/openssl: Modify the documentation of OpenSSL-APIs 1. add description of non-supported APIs 2. remove non-supported APIs now 3. add more supported APIs --- components/openssl/OpenSSL-APIs.rst | 197 ++++++++++------------------ 1 file changed, 68 insertions(+), 129 deletions(-) diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index 6a8a68c1ff..e7877b128c 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -1,10 +1,15 @@ OpenSSL-APIs -============ +------------ All original source code in this repository is Copyright (C) 2015-2016 Espressif Systems. This source code is licensed under the Apache License 2.0 as described in the file LICENSE. +OpenSSL APIs not mentioned in this article are not open to public for the time, +also do not have the corresponding function. +If user calls it directly, it will always return an error or may show cannot link +at compile time. + Chapter Introduction ==================== @@ -17,8 +22,7 @@ Chapter Introduction Chapter 1. SSL Context Method Create ==================================== - -1.1 const SSL_METHOD* ``SSLv23_client_method`` (void) +1.1 const SSL_METHOD* ``SSLv3_client_method`` (void) Arguments:: @@ -26,7 +30,7 @@ Chapter 1. SSL Context Method Create Return:: - SSLV2 and 3 version SSL context client method point + SSLV3.0 version SSL context client method point Description:: @@ -36,12 +40,11 @@ Chapter 1. SSL Context Method Create void example(void) { - const SSL_METHOD *method = SSLv23_client_method(); + const SSL_METHOD *method = SSLv3_client_method(); ... } - 1.2 const SSL_METHOD* ``TLSv1_client_method`` (void) Arguments:: @@ -65,32 +68,7 @@ Chapter 1. SSL Context Method Create ... } - -1.3 const SSL_METHOD* ``SSLv3_client_method`` (void) - - Arguments:: - - none - - Return:: - - SSLV3.0 version SSL context client method point - - Description:: - - create the target SSL context method - - Example:: - - void example(void) - { - const SSL_METHOD *method = SSLv3_client_method(); - - ... - } - - -1.4 const SSL_METHOD* ``TLSv1_1_client_method`` (void) +1.3 const SSL_METHOD* ``TLSv1_1_client_method`` (void) Arguments:: @@ -113,8 +91,7 @@ Chapter 1. SSL Context Method Create ... } - -1.5 const SSL_METHOD* ``TLSv1_2_client_method`` (void) +1.4 const SSL_METHOD* ``TLSv1_2_client_method`` (void) Arguments:: @@ -136,9 +113,31 @@ Chapter 1. SSL Context Method Create ... } + +1.5 const SSL_METHOD* ``TLS_client_method`` (void) + Arguments:: + + none + + Return:: + + TLSV1.2 version SSL context client method point + + Description:: + + create the default SSL context method, it's always to be TLSV1.2 + + Example:: + + void example(void) + { + const SSL_METHOD *method = TLSv1_2_client_method(); + + ... + } -1.6 const SSL_METHOD* ``SSLv23_server_method`` (void) +1.6 const SSL_METHOD* ``SSLv3_server_method`` (void) Arguments:: @@ -146,7 +145,7 @@ Chapter 1. SSL Context Method Create Return:: - SSLV2 and 3 version SSL context server method point + SSLV3.0 version SSL context server method point Description:: @@ -156,13 +155,35 @@ Chapter 1. SSL Context Method Create void example(void) { - const SSL_METHOD *method = SSLv23_server_method(); + const SSL_METHOD *method = SSLv3_server_method(); ... } +1.7 const SSL_METHOD* ``TLSv1_server_method`` (void) -1.7 const SSL_METHOD* ``TLSv1_1_server_method`` (void) + Arguments:: + + none + + Return:: + + TLSV1.0 version SSL context server method point + + Description:: + + create the target SSL context method + + Example:: + + void example(void) + { + const SSL_METHOD *method = TLSv1_server_method(); + + ... + } + +1.8 const SSL_METHOD* ``TLSv1_1_server_method`` (void) Arguments:: @@ -186,7 +207,7 @@ Chapter 1. SSL Context Method Create } -1.8 const SSL_METHOD* ``TLSv1_2_server_method`` (void) +1.9 const SSL_METHOD* ``TLSv1_2_server_method`` (void) Arguments:: @@ -209,8 +230,7 @@ Chapter 1. SSL Context Method Create ... } - -1.9 const SSL_METHOD* ``TLSv1_server_method`` (void) +1.10 const SSL_METHOD* ``TLS_server_method`` (void) Arguments:: @@ -218,47 +238,22 @@ Chapter 1. SSL Context Method Create Return:: - TLSV1.0 version SSL context server method point + TLSV1.2 version SSL context server method point Description:: - create the target SSL context method + create the default SSL context method, it's always to be TLSV1.2 Example:: void example(void) { - const SSL_METHOD *method = TLSv1_server_method(); + const SSL_METHOD *method = TLSv1_2_server_method(); ... } -1.10 const SSL_METHOD* ``SSLv3_server_method`` (void) - - Arguments:: - - none - - Return:: - - SSLV3.0 version SSL context server method point - - Description:: - - create the target SSL context method - - Example:: - - void example(void) - { - const SSL_METHOD *method = SSLv3_server_method(); - - ... - } - - - Chapter 2. SSL Context Fucntion =============================== @@ -1326,64 +1321,8 @@ Chapter 3. SSL Fucntion err = SSL_get_error(ssl, ret); } - -3.35 void ``SSL_CTX_set_default_read_buffer_len`` (SSL_CTX *ctx, size_t len) - Arguments:: - - ctx - SSL context point - len - read buffer length - - Return:: - - none - - Description:: - - set the SSL context read buffer length - - Example:: - - void example(void) - { - SSL_CTX *ctx; - size_t len; - - ... ... - - SSL_CTX_set_default_read_buffer_len(ctx, len); - } - - -3.36 void ``SSL_set_default_read_buffer_len`` (SSL *ssl, size_t len) - - Arguments:: - - ssl - SSL point - len - read buffer length - - Return:: - - none - - Description:: - - set the SSL read buffer length - - Example:: - - void example(void) - { - SSL *ssl; - size_t len; - - ... ... - - SSL_set_default_read_buffer_len(ctx, len); - } - - -3.37 int ``SSL_want`` (const SSL *ssl) +3.35 int ``SSL_want`` (const SSL *ssl) Arguments:: @@ -1410,7 +1349,7 @@ Chapter 3. SSL Fucntion } -3.38 int ``SSL_want_nothing`` (const SSL *ssl) +3.36 int ``SSL_want_nothing`` (const SSL *ssl) Arguments:: @@ -1438,7 +1377,7 @@ Chapter 3. SSL Fucntion } -3.39 int ``SSL_want_read`` (const SSL *ssl) +3.37 int ``SSL_want_read`` (const SSL *ssl) Arguments:: @@ -1466,7 +1405,7 @@ Chapter 3. SSL Fucntion } -3.40 int ``SSL_want_write`` (const SSL *ssl) +3.38 int ``SSL_want_write`` (const SSL *ssl) Arguments:: From 1a6dd44d0302138565fdbc9465f4fb156db2055a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 19 Sep 2016 18:00:03 +1000 Subject: [PATCH 045/285] hwcrypto bignum: Use mbedtls_mpi data structures for all bignum data Still doesn't solve the problem of multiplying two numbers where one is >2048 bits, needed for RSA support. --- components/mbedtls/port/esp_bignum.c | 135 ++++++++---------- .../mbedtls/port/include/mbedtls/esp_config.h | 4 +- 2 files changed, 58 insertions(+), 81 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 59bdc87260..caae0161f0 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -207,98 +207,80 @@ static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint m return( mpi_montmul( A, &U, N, mm, T ) ); } +#if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ -/* Allocate parameters used by hardware MPI multiply, - and copy mbedtls_mpi structures into them */ -static int mul_pram_alloc(const mbedtls_mpi *A, const mbedtls_mpi *B, char **pA, char **pB, char **pX, size_t *bites) +/* Number of words used to hold 'mpi', rounded up to nearest + 16 words (512 bits) to match hardware support +*/ +static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) { - char *sa, *sb, *sx; -// int algn; - int words, bytes; - int abytes, bbytes; - - if (A->n > B->n) - words = A->n; - else - words = B->n; - - bytes = (words / 16 + ((words % 16) ? 1 : 0 )) * 16 * 4 * 2; - - abytes = A->n * 4; - bbytes = B->n * 4; - - sa = malloc(bytes); - if (!sa) { - return -1; - } - - sb = malloc(bytes); - if (!sb) { - free(sa); - return -1; - } - - sx = malloc(bytes); - if (!sx) { - free(sa); - free(sb); - return -1; - } - - memcpy(sa, A->p, abytes); - memset(sa + abytes, 0, bytes - abytes); - - memcpy(sb, B->p, bbytes); - memset(sb + bbytes, 0, bytes - bbytes); - - *pA = sa; - *pB = sb; - - *pX = sx; - - *bites = bytes * 4; - - return 0; + size_t res; + for(res = mpi->n; res > 0; res-- ) { + if( mpi->p[res - 1] != 0 ) + break; + } + res = (res + 0xF) & ~0xF; + return res; } -#if defined(MBEDTLS_MPI_MUL_MPI_ALT) - int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) { int ret = -1; - size_t i, j; - char *s1 = NULL, *s2 = NULL, *dest = NULL; - size_t bites; + size_t words_a, words_b, words_x, words_mult; mbedtls_mpi TA, TB; mbedtls_mpi_init( &TA ); mbedtls_mpi_init( &TB ); - if( X == A ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); A = &TA; } - if( X == B ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); B = &TB; } + /* Count words needed for A & B in hardware */ + words_a = hardware_words_needed(A); + words_b = hardware_words_needed(B); - for( i = A->n; i > 0; i-- ) - if( A->p[i - 1] != 0 ) - break; + /* Take a copy of A if either X == A OR if A isn't long enough + to hold the number of words needed for hardware. - for( j = B->n; j > 0; j-- ) - if( B->p[j - 1] != 0 ) - break; + (can't grow A directly as it is const) - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, i + j ) ); + TODO: growing the input operands is only necessary because the + ROM functions only take one length argument. It should be + possible for us to just copy the used data only into the + hardware buffers, and set the remaining bits to zero - saving + RAM. But we need to reimplement ets_bigint_mult_prepare() in + software for this. + */ + if( X == A || A->n < words_a) { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_a) ); + A = &TA; + } + /* Same for B */ + if( X == B || B->n < words_b ) { + MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_b) ); + B = &TB; + } + + /* Result X has to have room for double the larger operand */ + words_mult = (words_a > words_b ? words_a : words_b); + words_x = words_mult * 2; + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, words_x ) ); + /* TODO: check if lset here is necessary, hardware should zero */ MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); - if (mul_pram_alloc(A, B, &s1, &s2, &dest, &bites)) { - goto cleanup; - } - esp_mpi_acquire_hardware(); - if (ets_bigint_mult_prepare((uint32_t *)s1, (uint32_t *)s2, bites)){ - ets_bigint_wait_finish(); - if (ets_bigint_mult_getz((uint32_t *)dest, bites) == true) { - memcpy(X->p, dest, (i + j) * 4); - ret = 0; - } else { + + if(words_mult * 32 > 2048) { + printf("WARNING: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); + } + + if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { + ets_bigint_wait_finish(); + /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is + copied to output X->p. + */ + if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { + ret = 0; + } else { printf("ets_bigint_mult_getz failed\n"); } } else{ @@ -307,11 +289,6 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi esp_mpi_release_hardware(); X->s = A->s * B->s; - - free(s1); - free(s2); - free(dest); - cleanup: mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TA ); diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index 5a69ff78e4..4ddd9821c4 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -253,8 +253,8 @@ Disabled as number of limbs limited by bug. Internal TW#7112. */ -//#define MBEDTLS_MPI_EXP_MOD_ALT -//#define MBEDTLS_MPI_MUL_MPI_ALT +#define MBEDTLS_MPI_EXP_MOD_ALT +#define MBEDTLS_MPI_MUL_MPI_ALT /** * \def MBEDTLS_MD2_PROCESS_ALT From 6b3bc4d8c5bb3eacf1076417074bccdca73971d5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Sep 2016 21:02:07 +1000 Subject: [PATCH 046/285] hwcrypto bignum: Implement multiplication modulo Fixes case where hardware bignum multiplication fails due to either operand >2048 bits. --- components/mbedtls/port/esp_bignum.c | 245 ++++++++++++++++-- .../mbedtls/port/include/mbedtls/esp_config.h | 4 +- 2 files changed, 223 insertions(+), 26 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index caae0161f0..d6b79e32f5 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -52,6 +52,140 @@ static void esp_mpi_release_hardware( void ) _lock_release(&mpi_lock); } +/* Given a & b, determine u & v such that + + gcd(a,b) = d = au + bv + + Underlying algorithm comes from: + http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf + http://www.hackersdelight.org/hdcodetxt/mont64.c.txt + */ +static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, + mbedtls_mpi *u, mbedtls_mpi *v) +{ + mbedtls_mpi ta, tb; + + mbedtls_mpi_init(&ta); + mbedtls_mpi_copy(&ta, a); + mbedtls_mpi_init(&tb); + mbedtls_mpi_copy(&tb, b); + + mbedtls_mpi_lset(u, 1); + mbedtls_mpi_lset(v, 0); + + /* Loop invariant: + ta = u*2*a - v*b. */ + while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { + mbedtls_mpi_shift_r(&ta, 1); + if (mbedtls_mpi_get_bit(u, 0) == 0) { + // Remove common factor of 2 in u & v + mbedtls_mpi_shift_r(u, 1); + mbedtls_mpi_shift_r(v, 1); + } + else { + /* u = (u + b) >> 1 */ + mbedtls_mpi_add_mpi(u, u, b); + mbedtls_mpi_shift_r(u, 1); + /* v = (v >> 1) + a */ + mbedtls_mpi_shift_r(v, 1); + mbedtls_mpi_add_mpi(v, v, a); + } + } + mbedtls_mpi_free(&ta); + mbedtls_mpi_free(&tb); + + /* u = u * 2, so 1 = u*a - v*b */ + mbedtls_mpi_shift_l(u, 1); +} + +/* inner part of MPI modular multiply, after Rinv & Mprime are calculated */ +static int mpi_mul_mpi_mod_inner(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, mbedtls_mpi *Rinv, uint32_t Mprime, size_t num_words) +{ + int ret; + mbedtls_mpi TA, TB; + size_t num_bits = num_words * 32; + + mbedtls_mpi_grow(Rinv, num_words); + + /* TODO: fill memory blocks directly so this isn't needed */ + mbedtls_mpi_init(&TA); + mbedtls_mpi_copy(&TA, A); + mbedtls_mpi_grow(&TA, num_words); + A = &TA; + mbedtls_mpi_init(&TB); + mbedtls_mpi_copy(&TB, B); + mbedtls_mpi_grow(&TB, num_words); + B = &TB; + + esp_mpi_acquire_hardware(); + + if(ets_bigint_mod_mult_prepare(A->p, B->p, M->p, Mprime, + Rinv->p, num_bits, false)) { + mbedtls_mpi_grow(X, num_words); + ets_bigint_wait_finish(); + if(ets_bigint_mod_mult_getz(M->p, X->p, num_bits)) { + X->s = A->s * B->s; + ret = 0; + } else { + printf("ets_bigint_mod_mult_getz failed\n"); + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + } else { + printf("ets_bigint_mod_mult_prepare failed\n"); + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + esp_mpi_release_hardware(); + + /* unclear why this is necessary, but the result seems + to come back rotated 32 bits to the right... */ + uint32_t last_word = X->p[num_words-1]; + X->p[num_words-1] = 0; + mbedtls_mpi_shift_l(X, 32); + X->p[0] = last_word; + + mbedtls_mpi_free(&TA); + mbedtls_mpi_free(&TB); + + return ret; +} + +/* X = (A * B) mod M + + Not an mbedTLS function + + num_bits guaranteed to be a multiple of 512 already. + + TODO: ensure M is odd + */ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, size_t num_bits) +{ + int ret = 0; + mbedtls_mpi RR, Rinv, Mprime; + uint32_t Mprime_int; + size_t num_words = num_bits / 32; + + /* Rinv & Mprime are calculated via extended binary gcd + algorithm, see references on extended_binary_gcd above. + */ + mbedtls_mpi_init(&Rinv); + mbedtls_mpi_init(&RR); + mbedtls_mpi_set_bit(&RR, num_bits+32, 1); + mbedtls_mpi_init(&Mprime); + extended_binary_gcd(&RR, M, &Rinv, &Mprime); + + /* M' is mod 2^32 */ + Mprime_int = Mprime.p[0]; + + ret = mpi_mul_mpi_mod_inner(X, A, B, M, &Rinv, Mprime_int, num_words); + + mbedtls_mpi_free(&RR); + mbedtls_mpi_free(&Mprime); + mbedtls_mpi_free(&Rinv); + + return ret; +} + + /* * Helper for mbedtls_mpi multiplication * copied/trimmed from mbedtls bignum.c @@ -223,6 +357,53 @@ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) return res; } + +/* Special-case multiply, where we use hardware montgomery mod + multiplication to solve the case where A or B are >2048 bits so + can't do standard multiplication. + + the modulus here is chosen with M=(2^num_bits-1) + to guarantee the output isn't actually modulo anything. This means + we don't need to calculate M' and Rinv, they are predictable + as follows: + M' = 1 + Rinv = (1 << (num_bits - 32) + + (See RSA Accelerator section in Technical Reference for derivation + of M', Rinv) +*/ +static int esp_mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, size_t num_words) + { + mbedtls_mpi M, Rinv; + int ret; + size_t mprime; + size_t num_bits = num_words * 32; + + mbedtls_mpi_init(&M); + mbedtls_mpi_init(&Rinv); + + /* TODO: it may be faster to just use 4096-bit arithmetic every time, + and make these constants rather than runtime derived + derived. */ + /* M = (2^num_words)-1 */ + mbedtls_mpi_grow(&M, num_words); + for(int i = 0; i < num_words*32; i++) { + mbedtls_mpi_set_bit(&M, i, 1); + } + + /* Rinv = (2^num_words-32) */ + mbedtls_mpi_grow(&Rinv, num_words); + mbedtls_mpi_set_bit(&Rinv, num_bits - 32, 1); + + mprime = 1; + + ret = mpi_mul_mpi_mod_inner(X, A, B, &M, &Rinv, mprime, num_words); + + mbedtls_mpi_free(&M); + mbedtls_mpi_free(&Rinv); + return ret; + } + int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) { int ret = -1; @@ -236,6 +417,8 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi words_a = hardware_words_needed(A); words_b = hardware_words_needed(B); + words_mult = (words_a > words_b ? words_a : words_b); + /* Take a copy of A if either X == A OR if A isn't long enough to hold the number of words needed for hardware. @@ -248,47 +431,63 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi RAM. But we need to reimplement ets_bigint_mult_prepare() in software for this. */ - if( X == A || A->n < words_a) { + if( X == A || A->n < words_mult) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_a) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_mult) ); A = &TA; } /* Same for B */ - if( X == B || B->n < words_b ) { + if( X == B || B->n < words_mult ) { MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_b) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_mult) ); B = &TB; } /* Result X has to have room for double the larger operand */ - words_mult = (words_a > words_b ? words_a : words_b); words_x = words_mult * 2; MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, words_x ) ); /* TODO: check if lset here is necessary, hardware should zero */ MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); - esp_mpi_acquire_hardware(); + /* If either operand is over 2048 bits, we can't use the standard hardware multiplier + (it assumes result is double longest operand, and result is max 4096 bits.) + However, we can fail over to mod_mult for up to 4096 bits. + */ if(words_mult * 32 > 2048) { - printf("WARNING: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); - } - - if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { - ets_bigint_wait_finish(); - /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is - copied to output X->p. + /* TODO: check if there's an overflow condition if words_a & words_b are both + the bit lengths of the operands, result could be 1 bit longer */ - if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { - ret = 0; - } else { - printf("ets_bigint_mult_getz failed\n"); - } - } else{ - printf("Baseline multiplication failed\n"); - } - esp_mpi_release_hardware(); + if((words_a + words_b) * 32 > 4096) { + printf("ERROR: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + else { + ret = esp_mpi_mult_mpi_failover_mod_mult(X, A, B, words_a + words_b); + } + } + else { - X->s = A->s * B->s; + /* normal mpi multiplication */ + esp_mpi_acquire_hardware(); + if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { + ets_bigint_wait_finish(); + /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is + copied to output X->p. + */ + if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { + X->s = A->s * B->s; + ret = 0; + } else { + printf("ets_bigint_mult_getz failed\n"); + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + } else{ + printf("Baseline multiplication failed\n"); + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + esp_mpi_release_hardware(); + } cleanup: mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TA ); diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index 4ddd9821c4..e4f4af271a 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -250,10 +250,8 @@ /* The following MPI (bignum) functions have ESP32 hardware support, Uncommenting these macros will use the hardware-accelerated implementations. - - Disabled as number of limbs limited by bug. Internal TW#7112. */ -#define MBEDTLS_MPI_EXP_MOD_ALT +//#define MBEDTLS_MPI_EXP_MOD_ALT #define MBEDTLS_MPI_MUL_MPI_ALT /** From 9632c8e56c49db6351dd8a1b964d61ac67cff342 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Sep 2016 21:24:58 +1000 Subject: [PATCH 047/285] RSA Accelerator: Add mod_exp, refactor to avoid memory allocation & copying Not fully working at the moment, mod_exp has a bug. --- components/esp32/include/soc/hwcrypto_reg.h | 37 + components/esp32/include/soc/soc.h | 1 + components/mbedtls/port/esp_bignum.c | 1073 +++++++---------- .../mbedtls/port/include/mbedtls/esp_config.h | 2 +- 4 files changed, 502 insertions(+), 611 deletions(-) create mode 100644 components/esp32/include/soc/hwcrypto_reg.h diff --git a/components/esp32/include/soc/hwcrypto_reg.h b/components/esp32/include/soc/hwcrypto_reg.h new file mode 100644 index 0000000000..4f38b1ba93 --- /dev/null +++ b/components/esp32/include/soc/hwcrypto_reg.h @@ -0,0 +1,37 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __HWCRYPTO_REG_H__ +#define __HWCRYPTO_REG_H__ + +#include "soc.h" + +/* registers for RSA acceleration via Multiple Precision Integer ops */ +#define RSA_MEM_M_BLOCK_BASE ((DR_REG_RSA_BASE)+0x000) +/* RB & Z use the same memory block, depending on phase of operation */ +#define RSA_MEM_RB_BLOCK_BASE ((DR_REG_RSA_BASE)+0x200) +#define RSA_MEM_Z_BLOCK_BASE ((DR_REG_RSA_BASE)+0x200) +#define RSA_MEM_Y_BLOCK_BASE ((DR_REG_RSA_BASE)+0x400) +#define RSA_MEM_X_BLOCK_BASE ((DR_REG_RSA_BASE)+0x600) + +#define RSA_M_DASH_REG (DR_REG_RSA_BASE + 0x800) +#define RSA_MODEXP_MODE_REG (DR_REG_RSA_BASE + 0x804) +#define RSA_START_MODEXP_REG (DR_REG_RSA_BASE + 0x808) +#define RSA_MULT_MODE_REG (DR_REG_RSA_BASE + 0x80c) +#define RSA_MULT_START_REG (DR_REG_RSA_BASE + 0x810) + +#define RSA_INTERRUPT_REG (DR_REG_RSA_BASE + 0X814) + +#define RSA_CLEAN_ADDR (DR_REG_RSA_BASE + 0X818) + +#endif diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index 4ffdfb069e..65698ec856 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -141,6 +141,7 @@ //}} #define DR_REG_DPORT_BASE 0x3ff00000 +#define DR_REG_RSA_BASE 0x3ff02000 #define DR_REG_UART_BASE 0x3ff40000 #define DR_REG_SPI1_BASE 0x3ff42000 #define DR_REG_SPI0_BASE 0x3ff43000 diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index d6b79e32f5..55e70c47ac 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -23,9 +23,19 @@ #include #include #include +#include +#include #include "mbedtls/bignum.h" #include "mbedtls/bn_mul.h" #include "rom/bigint.h" +#include "soc/hwcrypto_reg.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "bignum"; #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) @@ -35,6 +45,38 @@ static _lock_t mpi_lock; +/* Temporary debugging function to print an MPI number to + stdout. Happens to be in a format compatible with Python. +*/ +void mbedtls_mpi_printf(const char *name, const mbedtls_mpi *X) +{ + static char buf[1024]; + size_t n; + memset(buf, 0, sizeof(buf)); + printf("%s = 0x", name); + mbedtls_mpi_write_string(X, 16, buf, sizeof(buf)-1, &n); + if(n) { + puts(buf); + } else { + puts("TOOLONG"); + } +} + +/* Temporary debug function to dump a memory block's contents to stdout + TODO remove + */ +static void __attribute__((unused)) dump_memory_block(const char *label, uint32_t addr) +{ + printf("Dumping %s @ %08x\n", label, addr); + for(int i = 0; i < (4096 / 8); i += 4) { + if(i % 32 == 0) { + printf("\n %04x:", i); + } + printf("%08x ", REG_READ(addr + i)); + } + printf("Done\n"); +} + /* At the moment these hardware locking functions aren't exposed publically for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, please raise a feature request. @@ -52,299 +94,11 @@ static void esp_mpi_release_hardware( void ) _lock_release(&mpi_lock); } -/* Given a & b, determine u & v such that - - gcd(a,b) = d = au + bv - - Underlying algorithm comes from: - http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf - http://www.hackersdelight.org/hdcodetxt/mont64.c.txt - */ -static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, - mbedtls_mpi *u, mbedtls_mpi *v) -{ - mbedtls_mpi ta, tb; - - mbedtls_mpi_init(&ta); - mbedtls_mpi_copy(&ta, a); - mbedtls_mpi_init(&tb); - mbedtls_mpi_copy(&tb, b); - - mbedtls_mpi_lset(u, 1); - mbedtls_mpi_lset(v, 0); - - /* Loop invariant: - ta = u*2*a - v*b. */ - while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { - mbedtls_mpi_shift_r(&ta, 1); - if (mbedtls_mpi_get_bit(u, 0) == 0) { - // Remove common factor of 2 in u & v - mbedtls_mpi_shift_r(u, 1); - mbedtls_mpi_shift_r(v, 1); - } - else { - /* u = (u + b) >> 1 */ - mbedtls_mpi_add_mpi(u, u, b); - mbedtls_mpi_shift_r(u, 1); - /* v = (v >> 1) + a */ - mbedtls_mpi_shift_r(v, 1); - mbedtls_mpi_add_mpi(v, v, a); - } - } - mbedtls_mpi_free(&ta); - mbedtls_mpi_free(&tb); - - /* u = u * 2, so 1 = u*a - v*b */ - mbedtls_mpi_shift_l(u, 1); -} - -/* inner part of MPI modular multiply, after Rinv & Mprime are calculated */ -static int mpi_mul_mpi_mod_inner(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, mbedtls_mpi *Rinv, uint32_t Mprime, size_t num_words) -{ - int ret; - mbedtls_mpi TA, TB; - size_t num_bits = num_words * 32; - - mbedtls_mpi_grow(Rinv, num_words); - - /* TODO: fill memory blocks directly so this isn't needed */ - mbedtls_mpi_init(&TA); - mbedtls_mpi_copy(&TA, A); - mbedtls_mpi_grow(&TA, num_words); - A = &TA; - mbedtls_mpi_init(&TB); - mbedtls_mpi_copy(&TB, B); - mbedtls_mpi_grow(&TB, num_words); - B = &TB; - - esp_mpi_acquire_hardware(); - - if(ets_bigint_mod_mult_prepare(A->p, B->p, M->p, Mprime, - Rinv->p, num_bits, false)) { - mbedtls_mpi_grow(X, num_words); - ets_bigint_wait_finish(); - if(ets_bigint_mod_mult_getz(M->p, X->p, num_bits)) { - X->s = A->s * B->s; - ret = 0; - } else { - printf("ets_bigint_mod_mult_getz failed\n"); - ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; - } - } else { - printf("ets_bigint_mod_mult_prepare failed\n"); - ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; - } - esp_mpi_release_hardware(); - - /* unclear why this is necessary, but the result seems - to come back rotated 32 bits to the right... */ - uint32_t last_word = X->p[num_words-1]; - X->p[num_words-1] = 0; - mbedtls_mpi_shift_l(X, 32); - X->p[0] = last_word; - - mbedtls_mpi_free(&TA); - mbedtls_mpi_free(&TB); - - return ret; -} - -/* X = (A * B) mod M - - Not an mbedTLS function - - num_bits guaranteed to be a multiple of 512 already. - - TODO: ensure M is odd - */ -int esp_mpi_mul_mpi_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *M, size_t num_bits) -{ - int ret = 0; - mbedtls_mpi RR, Rinv, Mprime; - uint32_t Mprime_int; - size_t num_words = num_bits / 32; - - /* Rinv & Mprime are calculated via extended binary gcd - algorithm, see references on extended_binary_gcd above. - */ - mbedtls_mpi_init(&Rinv); - mbedtls_mpi_init(&RR); - mbedtls_mpi_set_bit(&RR, num_bits+32, 1); - mbedtls_mpi_init(&Mprime); - extended_binary_gcd(&RR, M, &Rinv, &Mprime); - - /* M' is mod 2^32 */ - Mprime_int = Mprime.p[0]; - - ret = mpi_mul_mpi_mod_inner(X, A, B, M, &Rinv, Mprime_int, num_words); - - mbedtls_mpi_free(&RR); - mbedtls_mpi_free(&Mprime); - mbedtls_mpi_free(&Rinv); - - return ret; -} - - -/* - * Helper for mbedtls_mpi multiplication - * copied/trimmed from mbedtls bignum.c - */ -static void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mpi_uint b ) -{ - mbedtls_mpi_uint c = 0, t = 0; - - for( ; i >= 16; i -= 16 ) - { - MULADDC_INIT - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_STOP - } - - for( ; i >= 8; i -= 8 ) - { - MULADDC_INIT - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - - MULADDC_CORE MULADDC_CORE - MULADDC_CORE MULADDC_CORE - MULADDC_STOP - } - - - for( ; i > 0; i-- ) - { - MULADDC_INIT - MULADDC_CORE - MULADDC_STOP - } - - t++; - - do { - *d += c; c = ( *d < c ); d++; - } - while( c != 0 ); -} - - -/* - * Helper for mbedtls_mpi subtraction - * Copied/adapter from mbedTLS bignum.c - */ -static void mpi_sub_hlp( size_t n, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d ) -{ - size_t i; - mbedtls_mpi_uint c, z; - - for( i = c = 0; i < n; i++, s++, d++ ) - { - z = ( *d < c ); *d -= c; - c = ( *d < *s ) + z; *d -= *s; - } - - while( c != 0 ) - { - z = ( *d < c ); *d -= c; - c = z; i++; d++; - } -} - - -/* The following 3 Montgomery arithmetic function are - copied from mbedTLS bigint.c verbatim as they are static. - - TODO: find a way to support making the versions in mbedtls - non-static. -*/ - -/* - * Fast Montgomery initialization (thanks to Tom St Denis) - */ -static void mpi_montg_init( mbedtls_mpi_uint *mm, const mbedtls_mpi *N ) -{ - mbedtls_mpi_uint x, m0 = N->p[0]; - unsigned int i; - - x = m0; - x += ( ( m0 + 2 ) & 4 ) << 1; - - for( i = biL; i >= 8; i /= 2 ) - x *= ( 2 - ( m0 * x ) ); - - *mm = ~x + 1; -} - -/* - * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) - */ -static int mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi *N, mbedtls_mpi_uint mm, - const mbedtls_mpi *T ) -{ - size_t i, n, m; - mbedtls_mpi_uint u0, u1, *d; - - if( T->n < N->n + 1 || T->p == NULL ) - return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); - - memset( T->p, 0, T->n * ciL ); - - d = T->p; - n = N->n; - m = ( B->n < n ) ? B->n : n; - - for( i = 0; i < n; i++ ) - { - /* - * T = (T + u0*B + u1*N) / 2^biL - */ - u0 = A->p[i]; - u1 = ( d[0] + u0 * B->p[0] ) * mm; - - mpi_mul_hlp( m, B->p, d, u0 ); - mpi_mul_hlp( n, N->p, d, u1 ); - - *d++ = u0; d[n + 1] = 0; - } - - memcpy( A->p, d, ( n + 1 ) * ciL ); - - if( mbedtls_mpi_cmp_abs( A, N ) >= 0 ) - mpi_sub_hlp( n, N->p, A->p ); - else - /* prevent timing attacks */ - mpi_sub_hlp( n, A->p, T->p ); - - return( 0 ); -} - -/* - * Montgomery reduction: A = A * R^-1 mod N - */ -static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint mm, const mbedtls_mpi *T ) -{ - mbedtls_mpi_uint z = 1; - mbedtls_mpi U; - - U.n = U.s = (int) z; - U.p = &z; - - return( mpi_montmul( A, &U, N, mm, T ) ); -} - -#if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ - /* Number of words used to hold 'mpi', rounded up to nearest - 16 words (512 bits) to match hardware support + 16 words (512 bits) to match hardware support. + + Note that mpi->N (size of memory buffer) may be higher than this + number, if the high bits are mostly zeroes. */ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) { @@ -357,356 +111,455 @@ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) return res; } +/* Copy mbedTLS MPI bignum 'mpi' to hardware memory block at 'mem_base'. -/* Special-case multiply, where we use hardware montgomery mod - multiplication to solve the case where A or B are >2048 bits so - can't do standard multiplication. - - the modulus here is chosen with M=(2^num_bits-1) - to guarantee the output isn't actually modulo anything. This means - we don't need to calculate M' and Rinv, they are predictable - as follows: - M' = 1 - Rinv = (1 << (num_bits - 32) - - (See RSA Accelerator section in Technical Reference for derivation - of M', Rinv) + If num_words is higher than the number of words in the bignum then + these additional words will be zeroed in the memory buffer. */ -static int esp_mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B, size_t num_words) - { - mbedtls_mpi M, Rinv; - int ret; - size_t mprime; - size_t num_bits = num_words * 32; - - mbedtls_mpi_init(&M); - mbedtls_mpi_init(&Rinv); - - /* TODO: it may be faster to just use 4096-bit arithmetic every time, - and make these constants rather than runtime derived - derived. */ - /* M = (2^num_words)-1 */ - mbedtls_mpi_grow(&M, num_words); - for(int i = 0; i < num_words*32; i++) { - mbedtls_mpi_set_bit(&M, i, 1); - } - - /* Rinv = (2^num_words-32) */ - mbedtls_mpi_grow(&Rinv, num_words); - mbedtls_mpi_set_bit(&Rinv, num_bits - 32, 1); - - mprime = 1; - - ret = mpi_mul_mpi_mod_inner(X, A, B, &M, &Rinv, mprime, num_words); - - mbedtls_mpi_free(&M); - mbedtls_mpi_free(&Rinv); - return ret; - } - -int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B ) +static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, size_t num_words) { - int ret = -1; - size_t words_a, words_b, words_x, words_mult; - - mbedtls_mpi TA, TB; - - mbedtls_mpi_init( &TA ); mbedtls_mpi_init( &TB ); - - /* Count words needed for A & B in hardware */ - words_a = hardware_words_needed(A); - words_b = hardware_words_needed(B); - - words_mult = (words_a > words_b ? words_a : words_b); - - /* Take a copy of A if either X == A OR if A isn't long enough - to hold the number of words needed for hardware. - - (can't grow A directly as it is const) - - TODO: growing the input operands is only necessary because the - ROM functions only take one length argument. It should be - possible for us to just copy the used data only into the - hardware buffers, and set the remaining bits to zero - saving - RAM. But we need to reimplement ets_bigint_mult_prepare() in - software for this. - */ - if( X == A || A->n < words_mult) { - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TA, A ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TA, words_mult) ); - A = &TA; + for(size_t i = 0; i < mpi->n && i < num_words; i++) { + REG_WRITE(mem_base + i * 4, mpi->p[i]); } - /* Same for B */ - if( X == B || B->n < words_mult ) { - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &TB, B ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &TB, words_mult) ); - B = &TB; + for(size_t i = mpi->n; i < num_words; i++) { + REG_WRITE(mem_base + i * 4, 0); } - - /* Result X has to have room for double the larger operand */ - words_x = words_mult * 2; - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, words_x ) ); - /* TODO: check if lset here is necessary, hardware should zero */ - MBEDTLS_MPI_CHK( mbedtls_mpi_lset( X, 0 ) ); - - /* If either operand is over 2048 bits, we can't use the standard hardware multiplier - (it assumes result is double longest operand, and result is max 4096 bits.) - - However, we can fail over to mod_mult for up to 4096 bits. - */ - if(words_mult * 32 > 2048) { - /* TODO: check if there's an overflow condition if words_a & words_b are both - the bit lengths of the operands, result could be 1 bit longer - */ - if((words_a + words_b) * 32 > 4096) { - printf("ERROR: %d bit operands (%d bits * %d bits) too large for hardware unit\n", words_mult * 32, mbedtls_mpi_bitlen(A), mbedtls_mpi_bitlen(B)); - ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - else { - ret = esp_mpi_mult_mpi_failover_mod_mult(X, A, B, words_a + words_b); - } - } - else { - - /* normal mpi multiplication */ - esp_mpi_acquire_hardware(); - if (ets_bigint_mult_prepare(A->p, B->p, words_mult * 32)) { - ets_bigint_wait_finish(); - /* NB: argument to bigint_mult_getz is length of inputs, double this number (words_x) is - copied to output X->p. - */ - if (ets_bigint_mult_getz(X->p, words_mult * 32) == true) { - X->s = A->s * B->s; - ret = 0; - } else { - printf("ets_bigint_mult_getz failed\n"); - ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - } else{ - printf("Baseline multiplication failed\n"); - ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - } - esp_mpi_release_hardware(); - } -cleanup: - - mbedtls_mpi_free( &TB ); mbedtls_mpi_free( &TA ); - - return( ret ); } -#endif /* MBEDTLS_MPI_MUL_MPI_ALT */ +/* Read mbedTLS MPI bignum back from hardware memory block. -#if defined(MBEDTLS_MPI_EXP_MOD_ALT) -/* - * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + Reads num_words words from block. + + Can return a failure result if fails to grow the MPI result. +*/ +static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_words) +{ + int ret = 0; + size_t x_n = x->n; + + /* this code is written in non-intuitive way, to only grow the + result if it is absolutely necessary - ie if all the high bits + are zero, the bignum won't be grown to fit them. */ + for(int i = num_words - 1; i >= 0; i--) { + uint32_t value = REG_READ(mem_base + i * 4); + if(value != 0 && x_n <= i) { + MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, i+1) ); + x_n = i+1; + } + if(x_n > i) { + x->p[i] = value; + } + } + /* Zero any remaining limbs in the bignum, if the buffer was + always bigger than num_words */ + for(size_t i = num_words; i < x->n; i++) { + x->p[i] = 0; + } + + cleanup: + return ret; +} + +/* Given a & b, determine u & v such that + + gcd(a,b) = d = au - bv + + This is suitable for calculating values for montgomery multiplication: + + gcd(R, M) = R * Rinv - M * Mprime = 1 + + Conditions which must be true: + - argument 'a' (R) is a power of 2. + - argument 'b' (M) is odd. + + Underlying algorithm comes from: + http://www.hackersdelight.org/hdcodetxt/mont64.c.txt + http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf */ -int mbedtls_mpi_exp_mod( mbedtls_mpi* X, const mbedtls_mpi* A, const mbedtls_mpi* E, const mbedtls_mpi* N, mbedtls_mpi* _RR ) +static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, + mbedtls_mpi *u, mbedtls_mpi *v) +{ + mbedtls_mpi a_, ta; + + /* These checks degrade performance, TODO remove them... */ + assert(b->p[0] & 1); + assert(mbedtls_mpi_bitlen(a) == mbedtls_mpi_lsb(a)+1); + assert(mbedtls_mpi_cmp_mpi(a, b) > 0); + + mbedtls_mpi_lset(u, 1); + mbedtls_mpi_lset(v, 0); + + /* 'a' needs to be half its real value for this algorithm + TODO see if we can halve the number in the caller to avoid + allocating a bignum here. + */ + mbedtls_mpi_init(&a_); + mbedtls_mpi_copy(&a_, a); + mbedtls_mpi_shift_r(&a_, 1); + + mbedtls_mpi_init(&ta); + mbedtls_mpi_copy(&ta, &a_); + + //mbedtls_mpi_printf("a", &a_); + //mbedtls_mpi_printf("b", b); + + /* Loop invariant: + 2*ta = u*2*a - v*b. + + Loop until ta == 0 + */ + while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { + //mbedtls_mpi_printf("ta", &ta); + //mbedtls_mpi_printf("u", u); + //mbedtls_mpi_printf("v", v); + //printf("2*ta == u*2*a - v*b\n"); + + mbedtls_mpi_shift_r(&ta, 1); + if (mbedtls_mpi_get_bit(u, 0) == 0) { + // Remove common factor of 2 in u & v + mbedtls_mpi_shift_r(u, 1); + mbedtls_mpi_shift_r(v, 1); + } + else { + /* u = (u + b) >> 1 */ + mbedtls_mpi_add_mpi(u, u, b); + mbedtls_mpi_shift_r(u, 1); + /* v = (v - a) >> 1 */ + mbedtls_mpi_shift_r(v, 1); + mbedtls_mpi_add_mpi(v, v, &a_); + } + } + mbedtls_mpi_free(&ta); + mbedtls_mpi_free(&a_); +} + +/* Execute RSA operation. op_reg specifies which 'START' register + to write to. +*/ +static inline void execute_op(uint32_t op_reg) +{ + /* Clear interrupt status, start operation */ + REG_WRITE(RSA_INTERRUPT_REG, 1); + REG_WRITE(op_reg, 1); + + /* TODO: use interrupt instead of busywaiting */ + while(REG_READ(RSA_INTERRUPT_REG) != 1) + { } + + /* clear the interrupt */ + REG_WRITE(RSA_INTERRUPT_REG, 1); +} + +/* Sub-stages of modulo multiplication/exponentiation operations */ +static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words); +inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); + +/* Z = (X * Y) mod M + + Not an mbedTLS function + */ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M) { int ret; - size_t wbits, wsize, one = 1; - size_t i, j, nblimbs; - size_t bufsize, nbits; - mbedtls_mpi_uint ei, mm, state; - mbedtls_mpi RR, T, W[ 2 << MBEDTLS_MPI_WINDOW_SIZE ], Apos; - int neg; + size_t num_words = hardware_words_needed(M); - if( mbedtls_mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) - return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + /* Calculate and load the first stage montgomery multiplication */ + MBEDTLS_MPI_CHK( modular_op_prepare(X, M, num_words) ); - if( mbedtls_mpi_cmp_int( E, 0 ) < 0 ) - return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA ); + execute_op(RSA_MULT_START_REG); - /* - * Init temps and window size - */ - mpi_montg_init( &mm, N ); - mbedtls_mpi_init( &RR ); mbedtls_mpi_init( &T ); - mbedtls_mpi_init( &Apos ); - memset( W, 0, sizeof( W ) ); + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); - i = mbedtls_mpi_bitlen( E ); + esp_mpi_release_hardware(); - wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : - ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + cleanup: + return ret; +} - if( wsize > MBEDTLS_MPI_WINDOW_SIZE ) - wsize = MBEDTLS_MPI_WINDOW_SIZE; +#if defined(MBEDTLS_MPI_EXP_MOD_ALT) - j = N->n + 1; - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, j ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[1], j ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &T, j * 2 ) ); +/* + * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) + */ +int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) +{ + int ret; + size_t z_words = hardware_words_needed(Z); + size_t x_words = hardware_words_needed(X); + size_t y_words = hardware_words_needed(Y); + size_t m_words = hardware_words_needed(M); + size_t num_words; - /* - * Compensate for negative A (and correct at the end) - */ - neg = ( A->s == -1 ); - if( neg ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &Apos, A ) ); - Apos.s = 1; - A = &Apos; + mbedtls_mpi_printf("X",X); + mbedtls_mpi_printf("Y",Y); + mbedtls_mpi_printf("M",M); + + /* "all numbers must be the same length", so choose longest number + as cardinal length of operation... + */ + num_words = z_words; + if (x_words > num_words) { + num_words = x_words; + } + if (y_words > num_words) { + num_words = y_words; + } + if (m_words > num_words) { + num_words = m_words; + } + printf("num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); + + /* TODO: _RR parameter currently ignored */ + + ret = modular_op_prepare(X, M, num_words); + if (ret != 0) { + return ret; } - /* - * If 1st call, pre-compute R^2 mod N - */ - if( _RR == NULL || _RR->p == NULL ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_lset( &RR, 1 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_shift_l( &RR, N->n * 2 * biL ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &RR, &RR, N ) ); + mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); - if( _RR != NULL ) - memcpy( _RR, &RR, sizeof( mbedtls_mpi) ); - } - else - memcpy( &RR, _RR, sizeof( mbedtls_mpi) ); + //dump_memory_block("X_BLOCK", RSA_MEM_X_BLOCK_BASE); + //dump_memory_block("Y_BLOCK", RSA_MEM_Y_BLOCK_BASE); + //dump_memory_block("M_BLOCK", RSA_MEM_M_BLOCK_BASE); - /* - * W[1] = A * R^2 * R^-1 mod N = A * R mod N - */ - if( mbedtls_mpi_cmp_mpi( A, N ) >= 0 ) - MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &W[1], A, N ) ); - else - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[1], A ) ); + REG_WRITE(RSA_MODEXP_MODE_REG, (num_words / 16) - 1); - mpi_montmul( &W[1], &RR, N, mm, &T ); + execute_op(RSA_START_MODEXP_REG); - /* - * X = R^2 * R^-1 mod N = R mod N - */ - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( X, &RR ) ); - mpi_montred( X, N, mm, &T ); + //dump_memory_block("Z_BLOCK", RSA_MEM_Z_BLOCK_BASE); - if( wsize > 1 ) - { - /* - * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) - */ - j = one << ( wsize - 1 ); + /* TODO: only need to read m_words not num_words, provided result is correct... */ + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[j], N->n + 1 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[j], &W[1] ) ); + esp_mpi_release_hardware(); - for( i = 0; i < wsize - 1; i++ ) - mpi_montmul( &W[j], &W[j], N, mm, &T ); + mbedtls_mpi_printf("Z",Z); + printf("print (Z == (X ** Y) %% M)\n"); - /* - * W[i] = W[i - 1] * W[1] - */ - for( i = j + 1; i < ( one << wsize ); i++ ) - { - MBEDTLS_MPI_CHK( mbedtls_mpi_grow( &W[i], N->n + 1 ) ); - MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &W[i], &W[i - 1] ) ); - - mpi_montmul( &W[i], &W[1], N, mm, &T ); - } - } - - nblimbs = E->n; - bufsize = 0; - nbits = 0; - wbits = 0; - state = 0; - - while( 1 ) - { - if( bufsize == 0 ) - { - if( nblimbs == 0 ) - break; - - nblimbs--; - - bufsize = sizeof( mbedtls_mpi_uint ) << 3; - } - - bufsize--; - - ei = (E->p[nblimbs] >> bufsize) & 1; - - /* - * skip leading 0s - */ - if( ei == 0 && state == 0 ) - continue; - - if( ei == 0 && state == 1 ) - { - /* - * out of window, square X - */ - mpi_montmul( X, X, N, mm, &T ); - continue; - } - - /* - * add ei to current window - */ - state = 2; - - nbits++; - wbits |= ( ei << ( wsize - nbits ) ); - - if( nbits == wsize ) - { - /* - * X = X^wsize R^-1 mod N - */ - for( i = 0; i < wsize; i++ ) - mpi_montmul( X, X, N, mm, &T ); - - /* - * X = X * W[wbits] R^-1 mod N - */ - mpi_montmul( X, &W[wbits], N, mm, &T ); - - state--; - nbits = 0; - wbits = 0; - } - } - - /* - * process the remaining bits - */ - for( i = 0; i < nbits; i++ ) - { - mpi_montmul( X, X, N, mm, &T ); - - wbits <<= 1; - - if( ( wbits & ( one << wsize ) ) != 0 ) - mpi_montmul( X, &W[1], N, mm, &T ); - } - - /* - * X = A^E * R * R^-1 mod N = A^E mod N - */ - mpi_montred( X, N, mm, &T ); - - if( neg ) - { - X->s = -1; - MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( X, N, X ) ); - } - -cleanup: - - for( i = ( one << ( wsize - 1 ) ); i < ( one << wsize ); i++ ) - mbedtls_mpi_free( &W[i] ); - - mbedtls_mpi_free( &W[1] ); mbedtls_mpi_free( &T ); mbedtls_mpi_free( &Apos ); - - if( _RR == NULL || _RR->p == NULL ) - mbedtls_mpi_free( &RR ); - - return( ret ); + return ret; } #endif /* MBEDTLS_MPI_EXP_MOD_ALT */ + +/* The common parts of modulo multiplication and modular sliding + * window exponentiation: + * + * @param X first multiplication factor and/or base of exponent. + * @param M modulo value for result + * @param num_words size of modulo operation, in words (limbs). + * Should already be rounded up to a multiple of 16 words (512 bits) & range checked. + * + * Steps: + * Calculate Rinv & Mprime based on M & num_words + * Load all coefficients to memory + * Set mode register + * + * @note This function calls esp_mpi_acquire_hardware. If successful, + * returns 0 and it becomes the callers responsibility to call + * esp_mpi_release_hardware(). If failure is returned, the caller does + * not need to call esp_mpi_release_hardware(). + */ +static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words) +{ + int ret = 0; + mbedtls_mpi RR, Rinv, Mprime; + size_t num_bits; + + /* Calculate number of bits */ + num_bits = num_words * 32; + + if(num_bits > 4096) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + + /* Rinv & Mprime are calculated via extended binary gcd + algorithm, see references on extended_binary_gcd() above. + */ + mbedtls_mpi_init(&Rinv); + mbedtls_mpi_init(&RR); + mbedtls_mpi_init(&Mprime); + + mbedtls_mpi_set_bit(&RR, num_bits, 1); /* R = b^n where b = 2^32, n=num_words, + ie R = 2^N (where N=num_bits) */ + /* calculate Rinv & Mprime */ + extended_binary_gcd(&RR, M, &Rinv, &Mprime); + + /* Block of debugging data, output suitable to paste into Python + TODO remove + */ + mbedtls_mpi_printf("R", &RR); + mbedtls_mpi_printf("M", M); + mbedtls_mpi_printf("Rinv", &Rinv); + mbedtls_mpi_printf("Mprime", &Mprime); + printf("print (R * Rinv - M * Mprime == 1)\n"); + printf("print (Rinv == (R * R) %% M)\n"); + + esp_mpi_acquire_hardware(); + + /* Load M, X, Rinv, M-prime (M-prime is mod 2^32) */ + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, num_words); + REG_WRITE(RSA_M_DASH_REG, Mprime.p[0]); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + + mbedtls_mpi_free(&Rinv); + mbedtls_mpi_free(&RR); + mbedtls_mpi_free(&Mprime); + + return ret; +} + +/* Second & final step of a modular multiply - load second multiplication + * factor Y, run the multiply, read back the result into Z. + * + * @param Z result value + * @param X first multiplication factor (used to set sign of result). + * @param Y second multiplication factor. + * @param num_words size of modulo operation, in words (limbs). + * Should already be rounded up to a multiple of 16 words (512 bits) & range checked. + * + * Caller must have already called esp_mpi_acquire_hardware(). + */ +inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) +{ + int ret; + /* Load Y to X input memory block, rerun */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, Y, num_words); + + execute_op(RSA_MULT_START_REG); + + /* Read result into Z */ + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); + + Z->s = X->s * Y->s; + + return ret; +} + +#if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ + +static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); + +/* Z = X * Y */ +int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y ) +{ + int ret; + size_t words_x, words_y, words_mult, words_z; + + /* Count words needed for X & Y in hardware */ + words_x = hardware_words_needed(X); + words_y = hardware_words_needed(Y); + + words_mult = (words_x > words_y ? words_x : words_y); + + /* Result Z has to have room for double the larger factor */ + words_z = words_mult * 2; + + /* If either factor is over 2048 bits, we can't use the standard hardware multiplier + (it assumes result is double longest factor, and result is max 4096 bits.) + + However, we can fail over to mod_mult for up to 4096 bits of result (modulo + multiplication doesn't have the same restriction, so result is simply the + number of bits in X plus number of bits in in Y.) + */ + if (words_mult * 32 > 2048) { + /* Calculate new length of Z */ + words_z = words_x + words_y; + if (words_z * 32 > 4096) { + ESP_LOGE(TAG, "ERROR: %d bit result (%d bits * %d bits) too large for hardware unit\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + else { + return mpi_mult_mpi_failover_mod_mult(Z, X, Y, words_z); + } + } + + /* Otherwise, we can use the (faster) multiply hardware unit */ + + esp_mpi_acquire_hardware(); + + /* Copy X (right-extended) & Y (left-extended) to memory block */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, words_mult); + mpi_to_mem_block(RSA_MEM_Z_BLOCK_BASE + words_mult * 4, Y, words_mult); + /* NB: as Y is left-extended, we don't zero the bottom words_mult words of Y block. + This is OK for now because zeroing is done by hardware when we do esp_mpi_acquire_hardware(). + */ + + REG_WRITE(RSA_M_DASH_REG, 0); + + /* "mode" register loaded with number of 512-bit blocks in result, + plus 7 (for range 9-12). (this is ((N~ / 32) - 1) + 8)) + */ + REG_WRITE(RSA_MULT_MODE_REG, (words_z / 16) + 7); + + execute_op(RSA_MULT_START_REG); + + /* Read back the result */ + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, words_z); + + Z->s = X->s * Y->s; + + esp_mpi_release_hardware(); + + return ret; +} + +/* Special-case of mbedtls_mpi_mult_mpi(), where we use hardware montgomery mod + multiplication to solve the case where A or B are >2048 bits so + can't use the standard multiplication method. + + This case is simpler than esp_mpi_mul_mpi_mod() as we control the arguments: + + * Modulus is chosen with M=(2^num_bits - 1) (ie M=R-1), so output + isn't actually modulo anything. + * Therefore of of M' and Rinv are predictable as follows: + M' = 1 + Rinv = 1 + + (See RSA Accelerator section in Technical Reference * + extended_binary_gcd() function above for more about M', Rinv) +*/ +static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) + { + int ret = 0; + + /* Load coefficients to hardware */ + esp_mpi_acquire_hardware(); + + /* M = 2^num_words - 1, so block is entirely FF */ + for(int i = 0; i < num_words; i++) { + REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX); + } + /* Mprime = 1 */ + REG_WRITE(RSA_M_DASH_REG, 1); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + + /* Load X */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + + /* Rinv = 1 */ + REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1); + for(int i = 1; i < num_words; i++) { + REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0); + } + + execute_op(RSA_MULT_START_REG); + + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); + + esp_mpi_release_hardware(); + + cleanup: + return ret; +} + +#endif /* MBEDTLS_MPI_MUL_MPI_ALT */ + #endif /* MBEDTLS_MPI_MUL_MPI_ALT || MBEDTLS_MPI_EXP_MOD_ALT */ diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index e4f4af271a..2b47d84ea4 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -251,7 +251,7 @@ Uncommenting these macros will use the hardware-accelerated implementations. */ -//#define MBEDTLS_MPI_EXP_MOD_ALT +#define MBEDTLS_MPI_EXP_MOD_ALT #define MBEDTLS_MPI_MUL_MPI_ALT /** From ce7b8059de18fd53a78848f2987716045c126f05 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 12 Oct 2016 17:08:05 +1100 Subject: [PATCH 048/285] RSA Accelerator: Remove timing-sensitive optimisations Avoid potentially leaking timing information about number of bits set in MPI values. --- components/mbedtls/port/esp_bignum.c | 37 +++++++++++++--------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 55e70c47ac..0a835c9e8d 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -97,15 +97,21 @@ static void esp_mpi_release_hardware( void ) /* Number of words used to hold 'mpi', rounded up to nearest 16 words (512 bits) to match hardware support. - Note that mpi->N (size of memory buffer) may be higher than this + Note that mpi->n (size of memory buffer) may be higher than this number, if the high bits are mostly zeroes. + + This implementation may cause the caller to leak a small amount of + timing information when an operation is performed (length of a + given mpi value, rounded to nearest 512 bits), but not all mbedTLS + RSA operations succeed if we use mpi->N as-is (buffers are too long). */ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) { - size_t res; - for(res = mpi->n; res > 0; res-- ) { - if( mpi->p[res - 1] != 0 ) - break; + size_t res = 1; + for(size_t i = 0; i < mpi->n; i++) { + if( mpi->p[i] != 0 ) { + res = i + 1; + } } res = (res + 0xF) & ~0xF; return res; @@ -135,23 +141,14 @@ static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, s static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_words) { int ret = 0; - size_t x_n = x->n; - /* this code is written in non-intuitive way, to only grow the - result if it is absolutely necessary - ie if all the high bits - are zero, the bignum won't be grown to fit them. */ - for(int i = num_words - 1; i >= 0; i--) { - uint32_t value = REG_READ(mem_base + i * 4); - if(value != 0 && x_n <= i) { - MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, i+1) ); - x_n = i+1; - } - if(x_n > i) { - x->p[i] = value; - } + MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, num_words) ); + + for(int i = 0; i < num_words; i++) { + x->p[i] = REG_READ(mem_base + i * 4); } - /* Zero any remaining limbs in the bignum, if the buffer was - always bigger than num_words */ + /* Zero any remaining limbs in the bignum, if the buffer is bigger + than num_words */ for(size_t i = num_words; i < x->n; i++) { x->p[i] = 0; } From 28d83e766a64b52b3e5a1ed6e64aa6fe6797c667 Mon Sep 17 00:00:00 2001 From: Yinling Date: Mon, 17 Oct 2016 17:03:54 +0800 Subject: [PATCH 049/285] fix bug that deploy when test failed: test report will be a single stage. The result of test report will be calculated from the result of all test jobs in test stage. So it will only deploy when all test job passed. --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd4049358a..5718cc542d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,7 @@ stages: - build - test + - test_report - deploy before_script: @@ -106,13 +107,12 @@ test_build_system: - ./make/test_build_system.sh test_report: - stage: deploy + stage: test_report only: - master - triggers tags: - test_report - allow_failure: true variables: LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test" @@ -121,7 +121,7 @@ test_report: when: always paths: - $REPORT_PATH - expire_in: 6 mos + expire_in: 12 mos script: # clone test bench - git clone $GITLAB_SSH_SERVER/yinling/auto_test_script.git From da706111965e471e9d0425e1b853aa63cc4e64d3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 13 Oct 2016 11:46:51 +1100 Subject: [PATCH 050/285] Deep sleep: Any source named rtc_wake_stub* is linked as RTC wake stub code Also move esp_deepsleep.h documentation out to docs/deep-sleep-stub.rst --- components/esp32/cpu_start.c | 8 +++ components/esp32/deepsleep.c | 3 +- components/esp32/include/esp_attr.h | 9 ++- components/esp32/include/esp_deepsleep.h | 36 +--------- components/esp32/include/esp_system.h | 1 + components/esp32/ld/esp32.common.ld | 44 ++++++++---- docs/deep-sleep-stub.rst | 87 ++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 54 deletions(-) create mode 100644 docs/deep-sleep-stub.rst diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 7b2ccdc609..35f2efd477 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -19,6 +19,7 @@ #include "rom/ets_sys.h" #include "rom/uart.h" +#include "rom/rtc.h" #include "soc/cpu.h" #include "soc/dport_reg.h" @@ -59,6 +60,8 @@ extern void app_main(void); extern int _bss_start; extern int _bss_end; +extern int _rtc_bss_start; +extern int _rtc_bss_end; extern int _init_start; extern void (*__init_array_start)(void); extern void (*__init_array_end)(void); @@ -89,6 +92,11 @@ void IRAM_ATTR call_start_cpu0() memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start)); + /* Unless waking from deep sleep (implying RTC memory is intact), clear RTC bss */ + if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) { + memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start)); + } + // Initialize heap allocator heap_alloc_caps_init(); diff --git a/components/esp32/deepsleep.c b/components/esp32/deepsleep.c index 61268bce6b..742ff8cf40 100644 --- a/components/esp32/deepsleep.c +++ b/components/esp32/deepsleep.c @@ -40,8 +40,7 @@ void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub) } void RTC_IRAM_ATTR esp_default_wake_deep_sleep(void) { - // - //mmu_init(0); + /* Clear MMU for CPU 0 */ REG_SET_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MMU_IA_CLR); REG_CLR_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MMU_IA_CLR); } diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index 156d2957f9..78aa3bd190 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -20,22 +20,21 @@ //and all variables in shared RAM. These macros can be used to redirect //particular functions/variables to other memory regions. -// Forces code into IRAM instead of flash +// Forces code into IRAM instead of flash. #define IRAM_ATTR __attribute__((section(".iram1"))) // Forces data into DRAM instead of flash #define DRAM_ATTR __attribute__((section(".dram1"))) -// Forces code into RTC fast memory +// Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst" #define RTC_IRAM_ATTR __attribute__((section(".rtc.text"))) -// Forces data into RTC slow memory +// Forces data into RTC slow memory. See "docs/deep-sleep-stub.rst" // Any variable marked with this attribute will keep its value // during a deep sleep / wake cycle. #define RTC_DATA_ATTR __attribute__((section(".rtc.data"))) -// Forces read-only data into RTC slow memory -// Makes constant data available to RTC wake stubs (see esp_deepsleep.h) +// Forces read-only data into RTC slow memory. See "docs/deep-sleep-stub.rst" #define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata"))) #endif /* __ESP_ATTR_H__ */ diff --git a/components/esp32/include/esp_deepsleep.h b/components/esp32/include/esp_deepsleep.h index 3683a8eeab..59b3129185 100644 --- a/components/esp32/include/esp_deepsleep.h +++ b/components/esp32/include/esp_deepsleep.h @@ -54,37 +54,7 @@ void system_deep_sleep(uint64_t time_in_us); * to run code immediately when the chip wakes from * sleep. * - * For example: - * @code - * void RTC_IRAM_ATTR esp_wake_deep_sleep(void) { - * esp_default_wake_deep_sleep(); - * // Add additional functionality here - * } - * - * (Implementing this function is not required for normal operation, - * in the usual case your app will start normally when waking from - * deep sleep.) - * - * esp_wake_deep_sleep() functionality is limited: - * - * - Runs immediately on wake, so most of the SoC is freshly reset - - * flash is unmapped and hardware is otherwise uninitialised. - * - * - Can only call functions implemented in ROM, or marked RTC_IRAM_ATTR. - * - * - Static variables marked RTC_DATA_ATTR will have initial values on - * cold boot, and maintain these values between sleep/wake cycles. - * - * - Read-only data should be marked RTC_RODATA_ATTR. Strings must be - * declared as variables also using RTC_RODATA_ATTR, like this: - * RTC_RODATA_ATTR const char message[] = "Hello from very early boot!\n"; - * - * - Any other static memory will not be initialised (either to zero, - * or to any predefined value). - * - * - * - If you implement your own stub, the first call the stub makes - should be to esp_default_wake_deep_sleep(). + * See docs/deep-sleep-stub.rst for details. */ void esp_wake_deep_sleep(void); @@ -115,9 +85,7 @@ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void); /* The default esp-idf-provided esp_wake_deep_sleep() stub. - If you replace esp_wake_deep_sleep() in your program, or use - esp_set_deep_sleep_wake_stub(), then it is recommended you call - esp_default_wake_deep_sleep() as the first function in your stub. + See docs/deep-sleep-stub.rst for details. */ void esp_default_wake_deep_sleep(void); diff --git a/components/esp32/include/esp_system.h b/components/esp32/include/esp_system.h index 84133366d7..8c6564c55b 100644 --- a/components/esp32/include/esp_system.h +++ b/components/esp32/include/esp_system.h @@ -16,6 +16,7 @@ #define __ESP_SYSTEM_H__ #include +#include #include "esp_err.h" #include "esp_deepsleep.h" diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index a3c6367840..991259a5e2 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -3,8 +3,38 @@ ENTRY(call_start_cpu0); SECTIONS { + /* RTC fast memory holds RTC wake stub code, + including from any source file named rtc_wake_stub*.c + */ + .rtc.text : + { + . = ALIGN(4); + *(.rtc.literal .rtc.text) + *rtc_wake_stub*.o(.literal .text .literal.* .text.*) + } >rtc_iram_seg + + /* RTC slow memory holds RTC wake stub + data/rodata, including from any source file + named rtc_wake_stub*.c + */ + .rtc.data : + { + *(.rtc.data) + *(.rtc.rodata) + *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + } > rtc_slow_seg + + /* RTC bss, from any source file named rtc_wake_stub*.c */ + .rtc.bss (NOLOAD) : + { + _rtc_bss_start = ABSOLUTE(.); + *rtc_wake_stub*.o(.bss .bss.*) + *rtc_wake_stub*.o(COMMON) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + /* Send .iram0 code to iram */ - .iram0.vectors : + .iram0.vectors : { /* Vectors go to IRAM */ _init_start = ABSOLUTE(.); @@ -153,16 +183,4 @@ SECTIONS _text_end = ABSOLUTE(.); _etext = .; } >iram0_2_seg - - .rtc.text : - { - . = ALIGN(4); - *(.rtc.literal .rtc.text) - } >rtc_iram_seg - - .rtc.data : - { - *(.rtc.data) - *(.rtc.rodata) - } > rtc_slow_seg } diff --git a/docs/deep-sleep-stub.rst b/docs/deep-sleep-stub.rst new file mode 100644 index 0000000000..983f8bbf26 --- /dev/null +++ b/docs/deep-sleep-stub.rst @@ -0,0 +1,87 @@ +Deep Sleep Wake Stubs +--------------------- + +ESP32 supports running a "deep sleep wake stub" when coming out of deep sleep. This function runs immediately as soon as the chip wakes up - before any normal initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, the SoC can go back to sleep or continue to start ESP-IDF normally. + +Deep sleep wake stub code is loaded into "RTC Fast Memory" and any data which it uses must also be loaded into RTC memory. RTC memory regions hold their contents during deep sleep. + +Rules for Wake Stubs +==================== + +Wake stub code must be carefully written: + +* As the SoC has freshly woken from sleep, most of the peripherals are in reset states. The SPI flash is unmapped. + +* The wake stub code can only call functions implemented in ROM or loaded into RTC Fast Memory (see below.) + +* The wake stub code can only access data loaded in RTC memory. All other RAM will be unintiailised and have random contents. The wake stub can use other RAM for temporary storage, but the contents will be overwritten when the SoC goes back to sleep or starts ESP-IDF. + +* RTC memory must include any read-only data (.rodata) used by the stub. + +* Data in RTC memory is initialised whenever the SoC restarts, except when waking from deep sleep. When waking from deep sleep, the values which were present before going to sleep are kept. + +* Wake stub code is a part of the main esp-idf app. During normal running of esp-idf, functions can call the wake stub functions or access RTC memory. It is as if these were regular parts of the app. + +Implementing A Stub +=================== + +The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()` then this will override the default. + +If supplying a custom wake stub, the first thing it does should be to call ``esp_default_wake_deep_sleep()``. + +It is not necessary to implement ``esp_wake_deep_sleep()`` in your app in order to use deep sleep. It is only necessary if you want to have special behaviour immediately on wake. + +If you want to swap between different deep sleep stubs at runtime, it is also possible to do this by calling the ``esp_set_deep_sleep_wake_stub()`` function. This is not necessary if you only use the default ``esp_wake_deep_sleep()`` function. + +All of these functions are declared in the ``esp_deepsleep.h`` header under components/esp32. + +Loading Code Into RTC Memory +============================ + +Wake stub code must be resident in RTC Fast Memory. This can be done in one of two ways. + +The first way is to use the ``RTC_IRAM_ATTR`` attribute to place a function into RTC memory:: + + void RTC_IRAM_ATTR esp_wake_deep_sleep(void) { + esp_default_wake_deep_sleep(); + // Add additional functionality here + } + +The second way is to place the function into any source file whose name starts with ``rtc_wake_stub``. Files names ``rtc_wake_stub*`` have their contents automatically put into RTC memory by the linker. + +The first way is simpler for very short and simple code, or for source files where you want to mix "normal" and "RTC" code. The second way is simpler when you want to write longer pieces of code for RTC memory. + + +Loading Data Into RTC Memory +============================ + +Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP. + +Specifying this data can be done in one of two ways: + +The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify any data (writeable or read-only, respectivley) which should be loaded into RTC slow memory:: + + RTC_DATA_ATTR int wake_count; + + void RTC_IRAM_ATTR esp_wake_deep_sleep(void) { + esp_default_wake_deep_sleep(); + static RTC_RODATA_ATTR const char fmt_str[] = "Wake count %d\n"; + ets_printf(fmt_str, wake_count++); + } + +Unfortunately, any string constants used in this way must be declared as arrays and marked with RTC_RODATA_ATTR, as shown in the example above. + +The second way is to place the data into any source file whose name starts with ``rtc_wake_stub``. + +For example, the equivalent example in ``rtc_wake_stub_counter.c``:: + + int wake_count; + + void RTC_IRAM_ATTR esp_wake_deep_sleep(void) { + esp_default_wake_deep_sleep(); + ets_printf("Wake count %d\n", wake_count++); + } + +The second way is a better option if you need to use strings, or write other more complex code. + + From 288f4f63f0a108003e29c3a041b86de3bb64976a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 24 Oct 2016 09:17:10 +0800 Subject: [PATCH 051/285] Add UART driver 1. add uart.h and uart.c 2. add ESP_ERR_TIMEOUT in esp_err.h 3. add UART AHB FIFO address in uart_reg.h 4. modify xRingbufferSendFromISR return value in ringbuffer.c 5. add #include "soc/gpio_sig_map.h" in gpio.h --- components/driver/include/driver/gpio.h | 1 + components/driver/include/driver/uart.h | 688 +++++++++++++++++ components/driver/uart.c | 938 ++++++++++++++++++++++++ components/esp32/include/esp_err.h | 1 + components/esp32/include/soc/uart_reg.h | 4 +- components/freertos/ringbuf.c | 3 +- 6 files changed, 1632 insertions(+), 3 deletions(-) create mode 100644 components/driver/include/driver/uart.h create mode 100644 components/driver/uart.c diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 9b47c88e69..c821b640eb 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -20,6 +20,7 @@ #include "soc/gpio_struct.h" #include "soc/rtc_io_reg.h" #include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" #include "rom/gpio.h" #include "esp_attr.h" diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h new file mode 100644 index 0000000000..cd0725d2ce --- /dev/null +++ b/components/driver/include/driver/uart.h @@ -0,0 +1,688 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_UART_H_ +#define _DRIVER_UART_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include "soc/uart_reg.h" +#include "soc/uart_struct.h" +#include "esp_err.h" +#include "driver/periph_ctrl.h" +#include + +extern const char* UART_TAG; +#define UART_FIFO_LEN (128) //Do Not Change it +#define UART_INTR_MASK 0x1ff +#define UART_LINE_INV_MASK (0x3f << 19) + +typedef enum { + UART_DATA_5_BITS = 0x0, //word length: 5bits + UART_DATA_6_BITS = 0x1, //word length: 6bits + UART_DATA_7_BITS = 0x2, //word length: 7bits + UART_DATA_8_BITS = 0x3, //word length: 8bits + UART_DATA_MAX_BITS = 0X4, +} uart_word_length_t; + +typedef enum { + UART_STOP_BITS_1 = 0x1, //stop bit: 1bit + UART_STOP_BITS_1_5 = 0x2, //stop bit: 1.5bits + UART_STOP_BITS_2 = 0x3, //stop bit: 2bits + UART_STOP_BITS_MAX = 0x4, +} uart_stop_bits_t; + +typedef enum { + UART_NUM_0 = 0x0, //base address 0x3ff40000 + UART_NUM_1 = 0x1, //base address 0x3ff50000 + UART_NUM_2 = 0x2, //base address 0x3ff6E000 + UART_NUM_MAX, +} uart_port_t; + +typedef enum { + UART_PARITY_DISABLE = 0x0, //Disable UART parity + UART_PARITY_EVEN = 0x10, //Enable UART even parity + UART_PARITY_ODD = 0x11 //Enable UART odd parity +} uart_parity_t; + +typedef enum { + UART_BITRATE_300 = 300, + UART_BITRATE_600 = 600, + UART_BITRATE_1200 = 1200, + UART_BITRATE_2400 = 2400, + UART_BITRATE_4800 = 4800, + UART_BITRATE_9600 = 9600, + UART_BITRATE_19200 = 19200, + UART_BITRATE_38400 = 38400, + UART_BITRATE_57600 = 57600, + UART_BITRATE_74880 = 74880, + UART_BITRATE_115200 = 115200, + UART_BITRATE_230400 = 230400, + UART_BITRATE_460800 = 460800, + UART_BITRATE_921600 = 921600, + UART_BITRATE_1843200 = 1843200, + UART_BITRATE_3686400 = 3686400, + UART_BITRATE_MAX = 5000000, +} uart_baudrate_t; //you can set any rate you need in this range + +typedef enum { + UART_HW_FLOWCTRL_DISABLE = 0x0, //disable hardware flow control + UART_HW_FLOWCTRL_RTS = 0x1, //enable RX hardware flow control (rts) + UART_HW_FLOWCTRL_CTS = 0x2, //enable TX hardware flow control (cts) + UART_HW_FLOWCTRL_CTS_RTS = 0x3, //enable hardware flow control + UART_HW_FLOWCTRL_MAX = 0x4, +} uart_hw_flowcontrol_t; + +typedef enum { + UART_INVERSE_DISABLE = 0x0, //Disable UART wire output inverse + UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, //UART RXD input inverse + UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, //UART CTS input inverse + UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, //UART TXD output inverse + UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, //UART RTS output inverse +} uart_inverse_t; + +typedef struct { + uart_baudrate_t baud_rate; //UART baudrate + uart_word_length_t data_bits; //UART byte size + uart_parity_t parity; //UART parity mode + uart_stop_bits_t stop_bits; //UART stop bits + uart_hw_flowcontrol_t flow_ctrl; //UART hw flow control mode(cts/rts) + uint8_t rx_flow_ctrl_thresh ; //UART hw RTS threshold +} uart_config_t; + +typedef struct { + uint32_t intr_enable_mask; //UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator + uint8_t rx_timeout_thresh; //UART timeout interrupt threshold(unit: time of sending one byte) + uint8_t txfifo_empty_intr_thresh; //UART TX empty interrupt threshold. + uint8_t rxfifo_full_thresh; //UART RX full interrupt threshold. +} uart_intr_config_t; + + +typedef enum { + UART_DATA, + UART_BREAK, + UART_BUFFER_FULL, + UART_FIFO_OVF, + UART_FRAME_ERR, + UART_PARITY_ERR, + UART_EVENT_MAX, +} uart_event_type_t; + +typedef struct { + uart_event_type_t type; + union { + struct { + size_t size; + } data; + + }; +} uart_event_t; + + + +/** + * @brief Set UART data bits. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_word_length_t data_bit : UART data bits + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit); + +/** + * @brief Get UART data bits. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1) : Parameter error + * UART_DATA_5_BITS (0): UART word length: 5 bits. + * UART_DATA_6_BITS (1): UART word length: 6 bits. + * UART_DATA_7_BITS (2): UART word length: 7 bits. + * UART_DATA_8_BITS (3): UART word length: 8 bits. + */ +int uart_get_word_length(uart_port_t uart_num); + +/** + * @brief Set UART stop bits. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_stop_bits_t bit_num : UART stop bits + * + * @return ESP_OK : Success + * ESP_FAIL: Fail + */ +esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); + +/** + * @brief Set UART stop bits. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1): Parameter error + * UART_STOP_BITS_1 (1): 1 stop bit + * UART_STOP_BITS_1_5 (2): 1.5 stop bits + * UART_STOP_BITS_1 (3): 2 stop bits + */ +int uart_get_stop_bits(uart_port_t uart_num); + +/** + * @brief Set UART parity. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_parity_t parity_mode : the enum of uart parity configuration + * + * @return null + */ +esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); + +/** + * @brief Get UART parity mode. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1): Parameter error + * UART_PARITY_ODD (0x11): Odd parity check mode + * UART_PARITY_EVEN (0x10): Even parity check mode + * UART_PARITY_DISABLE(0x0) : parity check disabled + * + */ +int uart_get_parity(uart_port_t uart_num); + +/** + * @brief Set UART baud rate. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t baud_rate : UART baud-rate, we can choose one from uart_baudrate_t, or set a value. + * + * @return null + */ +esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); + +/** + * @brief Get UART bit-rate. + * + * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL(-1): Parameter error + * Others (>0): UART baud-rate + * + */ +int uart_get_baudrate(uart_port_t uart_num); + +/** + * @brief Set UART line inverse mode + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t inverse_mask : Choose the wires that need to be inversed + * (Should be chosen from uart_inverse_t, combine with OR-OPERATION) + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; + + +/** + * @brief Set hardware flow control. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_hw_flowcontrol_t flow_ctrl : Hardware flow control mode + * @param uint8_t rx_thresh : Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); + +/** + * @brief Get hardware flow control mode + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_FAIL (-1): Parameter error + * UART_HW_FLOWCTRL_DISABLE (0): UART hw flow control disabled + * UART_HW_FLOWCTRL_RTS (1): UART RX flow control enabled + * UART_HW_FLOWCTRL_CTS (2): UART TX flow control enabled + * UART_HW_FLOWCTRL_CTS_RTS (3): UART TX/RX flow control enabled + */ +int uart_get_hw_flow_ctrl(uart_port_t uart_num); + +/** + * @brief Clear UART interrupt status + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t clr_mask : Bit mask of the status that to be cleared. + * enable_mask should be chosen from the fields of register UART_INT_CLR_REG + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); + +/** + * @brief Set UART interrupt enable + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t enable_mask : Bit mask of the enable bits. + * enable_mask should be chosen from the fields of register UART_INT_ENA_REG + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); + +/** + * @brief Clear UART interrupt enable bits + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint32_t disable_mask : Bit mask of the disable bits. + * Disable_mask should be chosen from the fields of register UART_INT_ENA_REG + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); + + +/** + * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_enable_rx_intr(uart_port_t uart_num); + +/** + * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_disable_rx_intr(uart_port_t uart_num); + +/** + * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_disable_tx_intr(uart_port_t uart_num); + +/** + * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int enable : 1: enable; 0: disable + * @param int thresh : Threshold of TX interrupt, 0 ~ UART_FIFO_LEN + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); + +/** +* @brief register UART interrupt handler(ISR). + * UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. + * + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint8_t uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param void (* fn)(void* ) : Interrupt handler function. + * Note that the handler function MUST be defined with attribution of "IRAM_ATTR" for now. + * @param void * arg : parameter for handler function + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); + +/** + * @brief Set UART pin number + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param tx_io_num : UART TX pin GPIO number + * @param rx_io_num : UART RX pin GPIO number + * @param rts_io_num : UART RTS pin GPIO number + * @param cts_io_num : UART CTS pin GPIO number + * + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num); + +/** + * @brief UART set RTS level (before inverse) + * UART rx hardware flow control should not be set. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int level : 1: RTS output low(active) + * 0: RTS output high(block) + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_rts(uart_port_t uart_num, int level); + +/** + * @brief UART set DTR level (before inverse) + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int level : 1: DTR output low + * 0: DTR output high + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_set_dtr(uart_port_t uart_num, int level); + +/** +* @brief UART parameter configure + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_config_t *uart_config: UART parameter settings + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); + +/** +* @brief UART interrupt configure + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_intr_config_t *p_intr_conf: UART interrupt settings + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf); + +/** + * @brief Install UART driver. + * UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param int buffer_size : UART ring buffer size + * @param int queue_size : UART event queue size/depth. + * @param int uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue); + +/** + * @brief Uninstall UART driver. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_driver_delete(uart_port_t uart_num); + +/** + * @brief Wait UART TX FIFO empty + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks + * + * @return ESP_OK : Success + * ESP_ERR_TIMEOUT: Timeout + */ +esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait); + +/** + * @brief Send data to the UART port from a given buffer and length, + * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param char* buffer : data buffer address + * @param uint32_t len : data length to send + * + * @return The number of data that pushed to the TX FIFO + */ +int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); + +/** + * @brief Send data to the UART port from a given buffer and length, + * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param char* src : data buffer address + * @param size_t size : data length to send + * + * @return The number of data that sent out. + */ +int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); + +/** + * @brief Send data to the UART port from a given buffer and length, + * This function will not return until all the data and the break signal have been sent out. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param char* src : data buffer address + * @param size_t size : data length to send + * @param int brk_len : break signal length (unit: one bit's time@current_baudrate) + * + * @return The number of data that sent out. + */ +int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); + +/** +* @brief UART read one char + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param TickType_t ticks_to_wait : ticks to wait. + * + * @return -1 : Error + * Others : return a char data from uart fifo. + */ +int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); + +/** +* @brief UART read bytes from UART buffer + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint8_t* buf : pointer to the buffer. + * @param uint32_t length : data length + * @param TickType_t ticks_to_wait: timeout time( FreeRTOS ti c + * + * @return -1 : Error + * Others : return a char data from uart fifo. + */ +int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait); + +/** + * @brief UART ring buffer flush + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error + */ +esp_err_t uart_flush(uart_port_t uart_num); + +/** + * @brief Set the serial output port for ets_printf function, not effective for ESP_LOGX macro. + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return ESP_OK : Success + * ESP_FAIL: Parameter error, or UART driver not installed. + */ +esp_err_t uart_set_print_port(uart_port_t uart_no); + +/** + * @brief Get the current serial port for ets_printf function + * + * + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return null + */ +int uart_get_print_port(); + +/***************************EXAMPLE********************************** + * + * + * ----------------EXAMPLE OF UART SETTING --------------------- + * //1. Setup UART + * #include "freertos/queue.h" + * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h + * //a. Set UART parameter + * int uart_num = 0; //uart port number + * uart_config_t uart_config = { + * .baud_rate = UART_BITRATE_115200, //baudrate + * .data_bits = UART_DATA_8_BITS, //data bit mode + * .parity = UART_PARITY_DISABLE, //parity mode + * .stop_bits = UART_STOP_BITS_1, //stop bit mode + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts) + * .rx_flow_ctrl_thresh = 120, //flow control threshold + * }; + * uart_param_config(uart_num, &uart_config); + * //b1. Setup UART driver(with UART queue) + * QueueHandle_t uart_queue; + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example + * //b2. Setup UART driver(without UART queue) + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example + * + *-----------------------------------------------------------------------------* + * //2. Set UART pin + * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. + * + *-----------------------------------------------------------------------------* + * //3. Read data from UART. + * uint8_t data[128]; + * int length = 0; + * length = uart_read_bytes(uart_num, data, sizeof(data), 100); + * + *-----------------------------------------------------------------------------* + * //4. Write data to UART. + * char* test_str = "This is a test string.\n" + * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); + * + *-----------------------------------------------------------------------------* + * //5. Write data to UART, end with a break signal. + * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); + * + *-----------------------------------------------------------------------------* + * + * //6. an example of echo test with hardware flow control on UART1 + * void uart_loop_back_test() + * { + * int uart_num = 1; + * uart_config_t uart_config = { + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + * .rx_flow_ctrl_thresh = 122, + * }; + * uart_param_config(uart_num, &uart_config); //Config UART1 parameters + * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) + * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level + * uart_driver_install(uart_num, 1024 * 2, 10, 17, NULL); //Install UART driver( We don't need an event queue here) + * uint8_t data[1000]; + * while(1) { + * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART + * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART + * } + * } + * + *-----------------------------------------------------------------------------* + * //7. An example of using UART event queue on UART0. + * + * #include "freertos/queue.h" + * QueueHandle_t uart0_queue; //A queue to handle UART event. + * void uart_task(void *pvParameters) + * { + * int uart_num = (int)pvParameters; + * uart_event_t event; + * uint8_t dtmp[1000]; + * for(;;) { + * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { //Waiting for UART event. + * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); + * switch(event.type) { + * case UART_DATA: //Event of UART receving data + * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); + * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); + * ESP_LOGI(UART_TAG, "uart read: %d\n", len); + * break; + * case UART_FIFO_OVF: //Event of HW FIFO overflow detected + * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); + * while(1); + * break; + * case UART_BUFFER_FULL: //Event of UART ring buffer full + * ESP_LOGI(UART_TAG, "ring buffer full\n"); + * break; + * case UART_BREAK: + * ESP_LOGI(UART_TAG, "uart rx break\n"); //Event of UART RX break detected + * break; + * case UART_PARITY_ERR: //Event of UART parity check error + * ESP_LOGI(UART_TAG, "uart parity error\n"); + * break; + * case UART_FRAME_ERR: //Event of UART frame error + * ESP_LOGI(UART_TAG, "uart frame error\n"); + * break; + * default: //Others + * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); + * break; + * } + * } + * } + * vTaskDelete(NULL); + * } + * + * void uart_queue_test() + * { + * int uart_num = 0; + * uart_config_t uart_config = { + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + * .rx_flow_ctrl_thresh = 122, + * }; + * uart_param_config(uart_num, &uart_config); //Set UART parameters + * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) + * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level + * uart_driver_install(uart_num, 1024 * 2, 10, 17, &uart0_queue); //Install UART driver, and get the queue. + * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * } + * + * + ***************************END OF EXAMPLE**********************************/ + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_UART_H_*/ diff --git a/components/driver/uart.c b/components/driver/uart.c new file mode 100644 index 0000000000..157e0c49c7 --- /dev/null +++ b/components/driver/uart.c @@ -0,0 +1,938 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "malloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/dport_reg.h" +#include "rom/ets_sys.h" +#include "soc/uart_struct.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include "soc/uart_struct.h" + +const char* UART_TAG = "UART"; +#define UART_CHECK(a, str) if (!(a)) { \ + ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + return ESP_FAIL; \ + } +#define DEFAULT_EMPTY_THRESH 10 +#define DEFAULT_FULL_THRESH 120 +#define DEFAULT_TOUT_THRESH 10 +#define UART_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) +#define UART_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +#define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +typedef struct { + uart_port_t uart_num; + SemaphoreHandle_t tx_fifo_sem; + SemaphoreHandle_t tx_mutex; + SemaphoreHandle_t tx_done_sem; + SemaphoreHandle_t tx_brk_sem; + SemaphoreHandle_t rx_sem; + QueueHandle_t xQueueUart; + int queue_size; + int intr_num; + RingbufHandle_t ring_buffer; + bool buffer_full_flg; + bool tx_waiting; + int cur_remain; + uint8_t* rd_ptr; + uint8_t* head_ptr; + uint8_t data_buf[UART_FIFO_LEN]; + uint8_t data_len; +} uart_obj_t; + +static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; +static uart_dev_t* UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; +static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; + +//Fill UART tx_fifo and return a number, +//This function by itself is not thread-safe, always call from within a muxed section. +static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) +{ + uint8_t i = 0; + uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; + uint8_t tx_remain_fifo_cnt = (UART_FIFO_LEN - tx_fifo_cnt); + uint8_t copy_cnt = (len >= tx_remain_fifo_cnt ? tx_remain_fifo_cnt : len); + for(i = 0; i < copy_cnt; i++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), buffer[i]); + } + return copy_cnt; +} + +esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.bit_num = data_bit; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_word_length(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + return UART[uart_num]->conf0.bit_num; +} + +esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((stop_bit < UART_STOP_BITS_MAX), "stop bit error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.stop_bit_num = stop_bit; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_stop_bits(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + return UART[uart_num]->conf0.stop_bit_num; +} + +esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.parity = parity_mode & 0x1; + UART[uart_num]->conf0.parity_en = (parity_mode >> 1) & 0x1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_parity(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + int val = UART[uart_num]->conf0.val; + if(val & UART_PARITY_EN_M) { + if(val & UART_PARITY_M) { + return UART_PARITY_ODD; + } else { + return UART_PARITY_EVEN; + } + } else { + return UART_PARITY_DISABLE; + } +} + +esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((baud_rate < UART_BITRATE_MAX), "baud_rate error"); + uint32_t clk_div = (((UART_CLK_FREQ) << 4) / baud_rate); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->clk_div.div_int = clk_div >> 4; + UART[uart_num]->clk_div.div_frag = clk_div & 0xf; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_baudrate(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + uint32_t clk_div = (UART[uart_num]->clk_div.div_int << 4) | UART[uart_num]->clk_div.div_frag; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uint32_t baudrate = ((UART_CLK_FREQ) << 4) / clk_div; + return baudrate; +} + +esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((((inverse_mask & UART_LINE_INV_MASK) == 0) && (inverse_mask != 0)), "inverse_mask error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + CLEAR_PERI_REG_MASK(UART_CONF0_REG(uart_num), UART_LINE_INV_MASK); + SET_PERI_REG_MASK(UART_CONF0_REG(uart_num), inverse_mask); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +//only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((rx_thresh < UART_FIFO_LEN), "rx flow thresh error"); + UART_CHECK((flow_ctrl < UART_HW_FLOWCTRL_MAX), "hw_flowctrl mode error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + if(flow_ctrl & UART_HW_FLOWCTRL_RTS) { + UART[uart_num]->conf1.rx_flow_thrhd = rx_thresh; + UART[uart_num]->conf1.rx_flow_en = 1; + } else { + UART[uart_num]->conf1.rx_flow_en = 0; + } + if(flow_ctrl & UART_HW_FLOWCTRL_CTS) { + UART[uart_num]->conf0.tx_flow_en = 1; + } else { + UART[uart_num]->conf0.tx_flow_en = 0; + } + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_get_hw_flow_ctrl(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + uart_hw_flowcontrol_t val = UART_HW_FLOWCTRL_DISABLE; + if(UART[uart_num]->conf1.rx_flow_en) { + val |= UART_HW_FLOWCTRL_RTS; + } + if(UART[uart_num]->conf0.tx_flow_en) { + val |= UART_HW_FLOWCTRL_CTS; + } + return val; +} + +static esp_err_t uart_reset_fifo(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.rxfifo_rst = 1; + UART[uart_num]->conf0.rxfifo_rst = 0; + UART[uart_num]->conf0.txfifo_rst = 1; + UART[uart_num]->conf0.txfifo_rst = 0; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + //intr_clr register is write-only + UART[uart_num]->int_clr.val = clr_mask; + return ESP_OK; +} + +esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + SET_PERI_REG_MASK(UART_INT_CLR_REG(uart_num), enable_mask); + SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), enable_mask); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), disable_mask); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_enable_rx_intr(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_disable_rx_intr(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_disable_tx_intr(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_ena.txfifo_empty = 0; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((thresh < UART_FIFO_LEN), "empty intr threshold error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_clr.txfifo_empty = 1; + UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V; + UART[uart_num]->int_ena.txfifo_empty = enable & 0x1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + ESP_INTR_ENABLE(p_uart_obj[uart_num]->intr_num); + return ESP_OK; +} + +esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + ESP_INTR_DISABLE(uart_intr_num); + switch(uart_num) { + case UART_NUM_1: + intr_matrix_set(xPortGetCoreID(), ETS_UART1_INTR_SOURCE, uart_intr_num); + break; + case UART_NUM_2: + intr_matrix_set(xPortGetCoreID(), ETS_UART2_INTR_SOURCE, uart_intr_num); + break; + case UART_NUM_0: + default: + intr_matrix_set(xPortGetCoreID(), ETS_UART0_INTR_SOURCE, uart_intr_num); + break; + } + xt_set_interrupt_handler(uart_intr_num, fn, arg); + ESP_INTR_ENABLE(uart_intr_num); + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +//internal signal can be output to multiple GPIO pads +//only one GPIO pad can connect with input signal +esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((tx_io_num < 0 || tx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[tx_io_num] != 0), "tx_io_num error"); + UART_CHECK((rx_io_num < 0 || rx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rx_io_num] != 0), "rx_io_num error"); + UART_CHECK((rts_io_num < 0 || rts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rts_io_num] != 0), "rts_io_num error"); + UART_CHECK((cts_io_num < 0 || cts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[cts_io_num] != 0), "cts_io_num error"); + + int tx_sig, rx_sig, rts_sig, cts_sig; + switch(uart_num) { + case UART_NUM_0: + tx_sig = U0TXD_OUT_IDX; + rx_sig = U0RXD_IN_IDX; + rts_sig = U0RTS_OUT_IDX; + cts_sig = U0CTS_IN_IDX; + break; + case UART_NUM_1: + tx_sig = U1TXD_OUT_IDX; + rx_sig = U1RXD_IN_IDX; + rts_sig = U1RTS_OUT_IDX; + cts_sig = U1CTS_IN_IDX; + break; + case UART_NUM_2: + tx_sig = U2TXD_OUT_IDX; + rx_sig = U2RXD_IN_IDX; + rts_sig = U2RTS_OUT_IDX; + cts_sig = U2CTS_IN_IDX; + break; + case UART_NUM_MAX: + default: + tx_sig = U0TXD_OUT_IDX; + rx_sig = U0RXD_IN_IDX; + rts_sig = U0RTS_OUT_IDX; + cts_sig = U0CTS_IN_IDX; + break; + } + if(tx_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[tx_io_num], PIN_FUNC_GPIO); + gpio_set_direction(tx_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(tx_io_num, tx_sig, 0, 0); + } + + if(rx_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rx_io_num], PIN_FUNC_GPIO); + gpio_set_direction(rx_io_num, GPIO_MODE_INPUT); + gpio_matrix_in(rx_io_num, rx_sig, 0); + } + if(rts_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rts_io_num], PIN_FUNC_GPIO); + gpio_set_direction(rts_io_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(rts_io_num, rts_sig, 0, 0); + } + if(cts_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[cts_io_num], PIN_FUNC_GPIO); + gpio_set_direction(cts_io_num, GPIO_MODE_INPUT); + gpio_matrix_in(cts_io_num, cts_sig, 0); + } + return ESP_OK; +} + +esp_err_t uart_set_rts(uart_port_t uart_num, int level) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.sw_rts = level & 0x1; + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_set_dtr(uart_port_t uart_num, int level) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->conf0.sw_dtr = level & 0x1; + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_config), "param null\n"); + if(uart_num == UART_NUM_0) { + periph_module_enable(PERIPH_UART0_MODULE); + } else if(uart_num == UART_NUM_1) { + periph_module_enable(PERIPH_UART1_MODULE); + } else if(uart_num == UART_NUM_2) { + periph_module_enable(PERIPH_UART2_MODULE); + } + uart_set_hw_flow_ctrl(uart_num, uart_config->flow_ctrl, uart_config->rx_flow_ctrl_thresh); + uart_set_baudrate(uart_num, uart_config->baud_rate); + UART[uart_num]->conf0.val = ( + (uart_config->parity << UART_PARITY_S) + | (uart_config->stop_bits << UART_STOP_BIT_NUM_S) + | (uart_config->data_bits << UART_BIT_NUM_S) + | ((uart_config->flow_ctrl & UART_HW_FLOWCTRL_CTS) ? UART_TX_FLOW_EN : 0x0) + | UART_TICK_REF_ALWAYS_ON_M); + return ESP_OK; +} + +esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_intr_conf), "param null\n"); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_clr.val = UART_INTR_MASK; + if(p_intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { + UART[uart_num]->conf1.rx_tout_thrhd = ((p_intr_conf->rx_timeout_thresh) & UART_RX_TOUT_THRHD_V); + UART[uart_num]->conf1.rx_tout_en = 1; + } else { + UART[uart_num]->conf1.rx_tout_en = 0; + } + if(p_intr_conf->intr_enable_mask & UART_RXFIFO_FULL_INT_ENA_M) { + UART[uart_num]->conf1.rxfifo_full_thrhd = p_intr_conf->rxfifo_full_thresh; + } + if(p_intr_conf->intr_enable_mask & UART_TXFIFO_EMPTY_INT_ENA_M) { + UART[uart_num]->conf1.txfifo_empty_thrhd = p_intr_conf->txfifo_empty_intr_thresh; + } + UART[uart_num]->int_ena.val = p_intr_conf->intr_enable_mask; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_FAIL; +} + +//internal isr handler for default driver code. +static void IRAM_ATTR uart_rx_intr_handler_default(void *param) +{ + uart_obj_t *p_uart = (uart_obj_t*) param; + uint8_t uart_num = p_uart->uart_num; + uart_dev_t* uart_reg = UART[uart_num]; + + uint8_t buf_idx = 0; + uint32_t uart_intr_status = UART[uart_num]->int_st.val; + static int rx_fifo_len = 0; + uart_event_t uart_event; + portBASE_TYPE HPTaskAwoken = 0; + while(uart_intr_status != 0x0) { + buf_idx = 0; + uart_event.type = UART_EVENT_MAX; + if(uart_intr_status & UART_TXFIFO_EMPTY_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.txfifo_empty = 0; + uart_reg->int_clr.txfifo_empty = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + if(p_uart->tx_waiting == true) { + p_uart->tx_waiting = false; + xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); + } + } + else if((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M)) { + if(p_uart->buffer_full_flg == false) { + //Get the buffer from the FIFO + rx_fifo_len = uart_reg->status.rxfifo_cnt; + p_uart->data_len = rx_fifo_len; + memset(p_uart->data_buf, 0, sizeof(p_uart->data_buf)); + while(buf_idx < rx_fifo_len) { + p_uart->data_buf[buf_idx++] = uart_reg->fifo.rw_byte; + } + //After Copying the Data From FIFO ,Clear intr_status + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_clr.rxfifo_tout = 1; + uart_reg->int_clr.rxfifo_full = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_event.type = UART_DATA; + uart_event.data.size = rx_fifo_len; + if(pdFALSE == xRingbufferSendFromISR(p_uart->ring_buffer, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.rxfifo_full = 0; + uart_reg->int_ena.rxfifo_tout = 0; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + p_uart->buffer_full_flg = true; + uart_event.type = UART_BUFFER_FULL; + } else { + uart_event.type = UART_DATA; + } + } else { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.rxfifo_full = 0; + uart_reg->int_ena.rxfifo_tout = 0; + uart_reg->int_clr.val = UART_RXFIFO_FULL_INT_CLR_M | UART_RXFIFO_TOUT_INT_CLR_M; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_event.type = UART_BUFFER_FULL; + } + } else if(uart_intr_status & UART_RXFIFO_OVF_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->conf0.rxfifo_rst = 1; + uart_reg->conf0.rxfifo_rst = 0; + uart_reg->int_clr.rxfifo_ovf = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_event.type = UART_FIFO_OVF; + } else if(uart_intr_status & UART_BRK_DET_INT_ST_M) { + uart_reg->int_clr.brk_det = 1; + uart_event.type = UART_BREAK; + } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { + uart_reg->int_clr.parity_err = 1; + uart_event.type = UART_FRAME_ERR; + } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { + uart_reg->int_clr.frm_err = 1; + uart_event.type = UART_PARITY_ERR; + } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->conf0.txd_brk = 0; + uart_reg->int_ena.tx_brk_done = 0; + uart_reg->int_clr.tx_brk_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + } else if(uart_intr_status & UART_TX_BRK_IDLE_DONE_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_brk_idle_done = 0; + uart_reg->int_clr.tx_brk_idle_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + } else if(uart_intr_status & UART_TX_DONE_INT_ST_M) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_done = 0; + uart_reg->int_clr.tx_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + xSemaphoreGiveFromISR(p_uart_obj[uart_num]->tx_done_sem, &HPTaskAwoken); + } + else { + uart_reg->int_clr.val = uart_intr_status; /*simply clear all other intr status*/ + uart_event.type = UART_EVENT_MAX; + } + + if(uart_event.type != UART_EVENT_MAX && p_uart->xQueueUart) { + xQueueSendFromISR(p_uart->xQueueUart, (void * )&uart_event, &HPTaskAwoken); + } + uart_intr_status = uart_reg->int_st.val; + } +} + +/**************************************************************/ +esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + if(p_uart_obj[uart_num] == NULL) { + ESP_INTR_DISABLE(uart_intr_num); + p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGE(UART_TAG, "UART driver malloc error\n"); + return ESP_FAIL; + } + p_uart_obj[uart_num]->uart_num = uart_num; + p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); + + p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->rx_sem = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->intr_num = uart_intr_num; + p_uart_obj[uart_num]->queue_size = queue_size; + + if(uart_queue) { + p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); + *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; + ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); + } else { + p_uart_obj[uart_num]->xQueueUart = NULL; + } + p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->tx_waiting = false; + p_uart_obj[uart_num]->rd_ptr = NULL; + p_uart_obj[uart_num]->cur_remain = 0; + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->ring_buffer = xRingbufferCreate(buffer_size, 0); + } else { + ESP_LOGE(UART_TAG, "UART driver already installed\n"); + return ESP_FAIL; + } + uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + uart_intr_config_t uart_intr = { + .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M + | UART_RXFIFO_TOUT_INT_ENA_M + | UART_FRM_ERR_INT_ENA_M + | UART_RXFIFO_OVF_INT_ENA_M + | UART_BRK_DET_INT_ENA_M, + .rxfifo_full_thresh = DEFAULT_FULL_THRESH, + .rx_timeout_thresh = DEFAULT_TOUT_THRESH, + .txfifo_empty_intr_thresh = DEFAULT_EMPTY_THRESH + }; + uart_intr_config(uart_num, &uart_intr); + ESP_INTR_ENABLE(uart_intr_num); + return ESP_OK; +} + +//Make sure no other tasks are still using UART before you call this function +esp_err_t uart_driver_delete(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGI(UART_TAG, "ALREADY NULL\n"); + return ESP_OK; + } + ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); + uart_disable_rx_intr(uart_num); + uart_disable_tx_intr(uart_num); + uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); + + if(p_uart_obj[uart_num]->tx_fifo_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_fifo_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_done_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_done_sem); + p_uart_obj[uart_num]->tx_done_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_brk_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); + p_uart_obj[uart_num]->tx_brk_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_mutex) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); + p_uart_obj[uart_num]->tx_mutex = NULL; + } + if(p_uart_obj[uart_num]->rx_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->rx_sem); + p_uart_obj[uart_num]->rx_sem = NULL; + } + if(p_uart_obj[uart_num]->xQueueUart) { + vQueueDelete(p_uart_obj[uart_num]->xQueueUart); + p_uart_obj[uart_num]->xQueueUart = NULL; + } + if(p_uart_obj[uart_num]->ring_buffer) { + vRingbufferDelete(p_uart_obj[uart_num]->ring_buffer); + p_uart_obj[uart_num]->ring_buffer = NULL; + } + free(p_uart_obj[uart_num]); + p_uart_obj[uart_num] = NULL; + return ESP_OK; +} + +esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + BaseType_t res; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + //Take tx_mutex + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)ticks_to_wait); + if(res == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + //take 1st tx_done_sem + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); + if(res == pdFALSE) { + ESP_LOGE(UART_TAG, "take uart done sem error, should not get here.\n"); + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_ERR_TIMEOUT; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + if(UART[uart_num]->status.txfifo_cnt == 0) { + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_OK; + } + uart_enable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); + //take 2nd tx_done_sem, wait given from ISR + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); + if(res == pdFALSE) { + uart_disable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_ERR_TIMEOUT; + } + xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return ESP_OK; +} + +static esp_err_t uart_set_break(uart_port_t uart_num, int break_num) +{ + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->idle_conf.tx_brk_num = break_num; + UART[uart_num]->conf0.txd_brk = 1; + UART[uart_num]->int_clr.tx_brk_done = 1; + UART[uart_num]->int_ena.tx_brk_done = 1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ESP_OK; +} + +int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK(buffer, "buffer null"); + if(len == 0) { + return 0; + } + xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + int tx_len = uart_fill_fifo(uart_num, buffer, len); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return tx_len; +} + +static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool brk_en, int brk_len) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK(src, "buffer null"); + if(size == 0) { + return 0; + } + //lock for uart_tx + xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + size_t original_size = size; + while(size) { + //semaphore for tx_fifo available + if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { + size_t sent = uart_fill_fifo(uart_num, (char*) src, size); + if(sent < size) { + p_uart_obj[uart_num]->tx_waiting = true; + uart_enable_tx_intr(uart_num, 1, DEFAULT_EMPTY_THRESH); + } + size -= sent; + src += sent; + } + } + if(brk_en) { + uart_set_break(uart_num, brk_len); + xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); + } + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + return original_size; +} + +int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK(src, "buffer null"); + return uart_tx_all(uart_num, src, size, 0, 0); +} + +int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((size > 0), "uart size error"); + UART_CHECK((src), "uart data null"); + UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); + return uart_tx_all(uart_num, src, size, 1, brk_len); +} + +int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + uint8_t* data; + size_t size; + int val; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + return -1; + } + if(p_uart_obj[uart_num]->cur_remain == 0) { + ticks_to_wait = ticks_end - xTaskGetTickCount(); + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + if(data) { + p_uart_obj[uart_num]->head_ptr = data; + p_uart_obj[uart_num]->rd_ptr = data; + p_uart_obj[uart_num]->cur_remain = size; + } else { + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return -1; + } + } + val = *(p_uart_obj[uart_num]->rd_ptr); + p_uart_obj[uart_num]->rd_ptr++; + p_uart_obj[uart_num]->cur_remain--; + if(p_uart_obj[uart_num]->cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->rd_ptr = NULL; + if(p_uart_obj[uart_num]->buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + if(res == pdTRUE) { + p_uart_obj[uart_num]->buffer_full_flg = false; + uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); + } + } + } + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return val; +} + +int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((buf), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + + uint8_t* data = NULL; + size_t size; + size_t copy_len = 0; + int len_tmp; + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + return -1; + } + while(length) { + if(p_uart_obj[uart_num]->cur_remain == 0) { + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + if(data) { + p_uart_obj[uart_num]->head_ptr = data; + p_uart_obj[uart_num]->rd_ptr = data; + p_uart_obj[uart_num]->cur_remain = size; + } else { + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return copy_len; + } + } + if(p_uart_obj[uart_num]->cur_remain > length) { + len_tmp = length; + } else { + len_tmp = p_uart_obj[uart_num]->cur_remain; + } + memcpy(buf + copy_len, p_uart_obj[uart_num]->rd_ptr, len_tmp); + p_uart_obj[uart_num]->rd_ptr += len_tmp; + p_uart_obj[uart_num]->cur_remain -= len_tmp; + copy_len += len_tmp; + length -= len_tmp; + if(p_uart_obj[uart_num]->cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->rd_ptr = NULL; + if(p_uart_obj[uart_num]->buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + if(res == pdTRUE) { + p_uart_obj[uart_num]->buffer_full_flg = false; + uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); + } + } + } + } + xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + return copy_len; +} + +esp_err_t uart_flush(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + + uart_obj_t* p_uart = p_uart_obj[uart_num]; + uint8_t* data; + size_t size; + //rx sem protect the ring buffer read related functions + xSemaphoreTake(p_uart->rx_sem, (portTickType)portMAX_DELAY); + while(true) { + if(p_uart->head_ptr) { + vRingbufferReturnItem(p_uart->ring_buffer, p_uart->head_ptr); + p_uart->rd_ptr = NULL; + p_uart->cur_remain = 0; + p_uart->head_ptr = NULL; + } + data = (uint8_t*) xRingbufferReceive(p_uart->ring_buffer, &size, (portTickType) 0); + if(data == NULL) { + break; + } + vRingbufferReturnItem(p_uart->ring_buffer, data); + } + p_uart->rd_ptr = NULL; + p_uart->cur_remain = 0; + p_uart->head_ptr = NULL; + xSemaphoreGive(p_uart->rx_sem); + uart_wait_tx_fifo_empty(uart_num, portMAX_DELAY); + uart_reset_fifo(uart_num); + return ESP_OK; +} + +//----------------------------------- +//Should not enable hw flow control the debug print port. +//Use uart_tx_all_chars() as a thread-safe function to send data. +static int s_uart_print_nport = UART_NUM_0; +static void uart2_write_char(char chr) +{ + uart_tx_all_chars(UART_NUM_2, (const char*)&chr, 1); +} + +static void uart1_write_char(char chr) +{ + uart_tx_all_chars(UART_NUM_1, (const char*)&chr, 1); +} + +static void uart0_write_char(char chr) +{ + uart_tx_all_chars(UART_NUM_0, (const char*)&chr, 1); +} + +static void uart_ignore_char(char chr) +{ + +} + +//Only effective to ets_printf function, not ESP_LOGX macro. +esp_err_t uart_set_print_port(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((p_uart_obj[uart_num]), "UART driver error"); + + s_uart_print_nport = uart_num; + switch(s_uart_print_nport) { + case UART_NUM_0: + ets_install_putc1(uart0_write_char); + break; + case UART_NUM_1: + ets_install_putc1(uart1_write_char); + break; + case UART_NUM_2: + ets_install_putc1(uart2_write_char); + break; + case UART_NUM_MAX: + default: + ets_install_putc1(uart_ignore_char); + break; + } + return ESP_OK; +} + +int uart_get_print_port() +{ + return s_uart_print_nport; +} + diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index 4f013f91ab..5504c3d682 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -31,6 +31,7 @@ typedef int32_t esp_err_t; #define ESP_ERR_NO_MEM 0x101 #define ESP_ERR_INVALID_ARG 0x102 #define ESP_ERR_INVALID_STATE 0x103 +#define ESP_ERR_TIMEOUT 0x104 /** * Macro which can be used to check the error code, diff --git a/components/esp32/include/soc/uart_reg.h b/components/esp32/include/soc/uart_reg.h index 155700b293..8cac4bb832 100644 --- a/components/esp32/include/soc/uart_reg.h +++ b/components/esp32/include/soc/uart_reg.h @@ -18,8 +18,10 @@ #include "soc.h" #define REG_UART_BASE( i ) (DR_REG_UART_BASE + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) ) - +#define REG_UART_AHB_BASE(i) (0x60000000 + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) ) +#define UART_FIFO_AHB_REG(i) (REG_UART_AHB_BASE(i) + 0x0) #define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0) + /* UART_RXFIFO_RD_BYTE : RO ;bitpos:[7:0] ;default: 8'b0 ; */ /*description: This register stores one byte data read by rx fifo.*/ #define UART_RXFIFO_RD_BYTE 0x000000FF diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 6045c5a695..3651753147 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -371,8 +371,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da //Does not fit in the remaining space in the ringbuffer. write_succeeded=pdFALSE; } else { - copyItemToRingbuf(rb, data, dataSize); - write_succeeded=pdTRUE; + write_succeeded = copyItemToRingbuf(rb, data, dataSize); } portEXIT_CRITICAL_ISR(&rb->mux); if (write_succeeded) { From 9098e64398e1af7418e7c27c6bba23f80b55810f Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 24 Oct 2016 09:38:34 +0800 Subject: [PATCH 052/285] modify GPIO number check --- components/driver/uart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index 157e0c49c7..f585c0965d 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -312,10 +312,10 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((tx_io_num < 0 || tx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[tx_io_num] != 0), "tx_io_num error"); - UART_CHECK((rx_io_num < 0 || rx_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rx_io_num] != 0), "rx_io_num error"); - UART_CHECK((rts_io_num < 0 || rts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[rts_io_num] != 0), "rts_io_num error"); - UART_CHECK((cts_io_num < 0 || cts_io_num < GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[cts_io_num] != 0), "cts_io_num error"); + UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); + UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); + UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); + UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { From 74aff2b9d2762578f1da7ff736e402c909755620 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 24 Oct 2016 15:57:23 +0800 Subject: [PATCH 053/285] Update UART driver 1. Use esp_log API for LEDC and GPIO code. 2. Modify some API return value. 3. Add ledc_set_pin() for LEDC 4. Modify typo in uart.h Questions: In uart driver ISR handler, I used xxxFromISR , like xSemaphoreGiveFromISR, do those FromISR functions need to be put in IRAM? --- components/driver/gpio.c | 123 ++++--------- components/driver/include/driver/gpio.h | 2 + components/driver/include/driver/ledc.h | 33 +++- components/driver/include/driver/uart.h | 71 ++++---- components/driver/ledc.c | 223 ++++++------------------ components/driver/uart.c | 40 +++-- 6 files changed, 169 insertions(+), 323 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 14dfc00b43..da0fedb895 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -18,34 +18,13 @@ #include "freertos/xtensa_api.h" #include "driver/gpio.h" #include "soc/soc.h" +#include "esp_log.h" -//TODO: move debug options to menuconfig -#define GPIO_DBG_ENABLE (0) -#define GPIO_WARNING_ENABLE (0) -#define GPIO_ERROR_ENABLE (0) -#define GPIO_INFO_ENABLE (0) -//DBG INFOR -#if GPIO_INFO_ENABLE -#define GPIO_INFO ets_printf -#else -#define GPIO_INFO(...) -#endif -#if GPIO_WARNING_ENABLE -#define GPIO_WARNING(format,...) do{\ - ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define GPIO_WARNING(...) -#endif -#if GPIO_ERROR_ENABLE -#define GPIO_ERROR(format,...) do{\ - ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define GPIO_ERROR(...) -#endif +const char* GPIO_TAG = "GPIO"; +#define GPIO_CHECK(a, str, ret_val) if (!(a)) { \ + ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret_val); \ + } const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_0, @@ -90,33 +69,17 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_39 }; -static int is_valid_gpio(int gpio_num) -{ - if(gpio_num >= GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[gpio_num] == 0) { - GPIO_ERROR("GPIO io_num=%d does not exist\n",gpio_num); - return 0; - } - return 1; -} - esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } - if(intr_type >= GPIO_INTR_MAX) { - GPIO_ERROR("Unknown GPIO intr:%u\n",intr_type); - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error\n", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_type = intr_type; return ESP_OK; } esp_err_t gpio_intr_enable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(xPortGetCoreID() == 0) { GPIO.pin[gpio_num].int_ena = GPIO_PRO_CPU_INTR_ENA; //enable pro cpu intr } else { @@ -127,18 +90,14 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num) esp_err_t gpio_intr_disable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_ena = 0; //disable GPIO intr return ESP_OK; } static esp_err_t gpio_output_disable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1tc = (0x1 << gpio_num); } else { @@ -149,13 +108,7 @@ static esp_err_t gpio_output_disable(gpio_num_t gpio_num) static esp_err_t gpio_output_enable(gpio_num_t gpio_num) { - if(gpio_num >= 34) { - GPIO_ERROR("io_num=%d can only be input\n",gpio_num); - return ESP_ERR_INVALID_ARG; - } - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error\n", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1ts = (0x1 << gpio_num); } else { @@ -166,9 +119,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num) esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level) { - if(!GPIO_IS_VALID_GPIO(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(level) { if(gpio_num < 32) { GPIO.out_w1ts = (1 << gpio_num); @@ -196,9 +147,8 @@ int gpio_get_level(gpio_num_t gpio_num) esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error\n", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; switch(pull) { case GPIO_PULLUP_ONLY: @@ -218,7 +168,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); break; default: - GPIO_ERROR("Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull); + ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull); ret = ESP_ERR_INVALID_ARG; break; } @@ -227,11 +177,9 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); if(gpio_num >= 34 && (mode & (GPIO_MODE_DEF_OUTPUT))) { - GPIO_ERROR("io_num=%d can only be input\n",gpio_num); + ESP_LOGE(GPIO_TAG, "io_num=%d can only be input\n",gpio_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -268,52 +216,51 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) uint32_t io_num = 0; uint64_t bit_valid = 0; if(pGPIOConfig->pin_bit_mask == 0 || pGPIOConfig->pin_bit_mask >= (((uint64_t) 1) << GPIO_PIN_COUNT)) { - GPIO_ERROR("GPIO_PIN mask error \n"); + ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error \n"); return ESP_ERR_INVALID_ARG; } if((pGPIOConfig->mode) & (GPIO_MODE_DEF_OUTPUT)) { //GPIO 34/35/36/37/38/39 can only be used as input mode; if((gpio_pin_mask & ( GPIO_SEL_34 | GPIO_SEL_35 | GPIO_SEL_36 | GPIO_SEL_37 | GPIO_SEL_38 | GPIO_SEL_39))) { - GPIO_ERROR("GPIO34-39 can only be used as input mode\n"); + ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode\n"); return ESP_ERR_INVALID_ARG; } } do { io_reg = GPIO_PIN_MUX_REG[io_num]; if(((gpio_pin_mask >> io_num) & BIT(0)) && io_reg) { - GPIO_INFO("Gpio%02d |Mode:",io_num); + ESP_LOGI(GPIO_TAG, "Gpio%02d |Mode:",io_num); if((pGPIOConfig->mode) & GPIO_MODE_DEF_INPUT) { - GPIO_INFO("INPUT "); + ESP_LOGI(GPIO_TAG, "INPUT "); PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[io_num]); } else { PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[io_num]); } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OD) { - GPIO_INFO("OD "); + ESP_LOGI(GPIO_TAG, "OD "); GPIO.pin[io_num].pad_driver = 1; /*0x01 Open-drain */ } else { GPIO.pin[io_num].pad_driver = 0; /*0x00 Normal gpio output */ } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OUTPUT) { - GPIO_INFO("OUTPUT "); + ESP_LOGI(GPIO_TAG, "OUTPUT "); gpio_output_enable(io_num); } else { gpio_output_disable(io_num); } - GPIO_INFO("|"); if(pGPIOConfig->pull_up_en) { - GPIO_INFO("PU "); + ESP_LOGI(GPIO_TAG, "PU "); PIN_PULLUP_EN(io_reg); } else { PIN_PULLUP_DIS(io_reg); } if(pGPIOConfig->pull_down_en) { - GPIO_INFO("PD "); + ESP_LOGI(GPIO_TAG, "PD "); PIN_PULLDWN_EN(io_reg); } else { PIN_PULLDWN_DIS(io_reg); } - GPIO_INFO("Intr:%d |\n",pGPIOConfig->intr_type); + ESP_LOGI(GPIO_TAG, "Intr:%d |\n",pGPIOConfig->intr_type); gpio_set_intr_type(io_num, pGPIOConfig->intr_type); if(pGPIOConfig->intr_type) { gpio_intr_enable(io_num); @@ -322,7 +269,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) } PIN_FUNC_SELECT(io_reg, PIN_FUNC_GPIO); /*function number 2 is GPIO_FUNC for each pin */ } else if(bit_valid && (io_reg == 0)) { - GPIO_WARNING("io_num=%d does not exist\n",io_num); + ESP_LOGW(GPIO_TAG, "io_num=%d does not exist\n",io_num); } io_num++; } while(io_num < GPIO_PIN_COUNT); @@ -331,9 +278,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg) { - if(fn == NULL) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(fn, "GPIO ISR null\n", ESP_ERR_INVALID_ARG); ESP_INTR_DISABLE(gpio_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num); xt_set_interrupt_handler(gpio_intr_num, fn, arg); @@ -344,15 +289,13 @@ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * ar /*only level interrupt can be used for wake-up function*/ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; if((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) { GPIO.pin[gpio_num].int_type = intr_type; GPIO.pin[gpio_num].wakeup_enable = 0x1; } else { - GPIO_ERROR("GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num); + ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num); ret = ESP_ERR_INVALID_ARG; } return ret; @@ -360,9 +303,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num) { - if(!is_valid_gpio(gpio_num)) { - return ESP_ERR_INVALID_ARG; - } + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].wakeup_enable = 0; return ESP_OK; } diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index c821b640eb..5ed99e5976 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -28,6 +28,8 @@ extern "C" { #endif +extern const char* GPIO_TAG; + #define GPIO_SEL_0 (BIT(0)) /* Pin 0 selected */ #define GPIO_SEL_1 (BIT(1)) /* Pin 1 selected */ #define GPIO_SEL_2 (BIT(2)) /* Pin 2 selected */ diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 79a6c7f9f3..d6ce9b86c9 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -26,6 +26,7 @@ extern "C" { #endif +extern const char* LEDC_TAG; #define LEDC_APB_CLK_HZ (APB_CLK_FREQ) #define LEDC_REF_CLK_HZ (1*1000000) @@ -338,6 +339,22 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); */ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); +/** + * @brief Set LEDC output signal to GPIO + * + * @param[in] gpio_num : GPIO number for LEDC signal output + * + * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * + * @param[in] channel : LEDC channel index(0-7), select from ledc_channel_t + * + * + * @return ESP_ERR_INVALID_ARG: parameter error + * ESP_OK: success + * + */ +esp_err_t ledc_set_pin(int gpio_num,ledc_mode_t speed_mode, ledc_channel_t ledc_channel); + /***************************EXAMPLE********************************** * * @@ -349,21 +366,21 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * ledc_timer_config_t timer_conf = { * .bit_num = LEDC_TIMER_12_BIT, //set timer counter bit number * .freq_hz = 1000, //set frequency of pwm, here, 1000Hz - * .speed_mode = LEDC_HIGH_SPEED_MODE //timer mode, + * .speed_mode = LEDC_HIGH_SPEED_MODE, //timer mode, * .timer_num = LEDC_TIMER_0, //timer number * }; * ledc_timer_config(&timer_conf); //setup timer. * * //3. set LEDC channel * ledc_channel_config_t ledc_conf = { - * .channel = LEDC_CHANNEL_0; //set LEDC channel 0 - * .duty = 1000; //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) - * .gpio_num = 16; //GPIO number - * .intr_type = LEDC_INTR_FADE_END; //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. - * .speed_mode = LEDC_HIGH_SPEED_MODE; //set LEDC mode, from ledc_mode_t - * .timer_sel = LEDC_TIMER_0; //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same + * .channel = LEDC_CHANNEL_0, //set LEDC channel 0 + * .duty = 1000, //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) + * .gpio_num = 16, //GPIO number + * .intr_type = LEDC_INTR_FADE_END, //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. + * .speed_mode = LEDC_HIGH_SPEED_MODE, //set LEDC mode, from ledc_mode_t + * .timer_sel = LEDC_TIMER_0, //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same * } - * ledc_channel_config(&ledc_conf); //setup the configuration + * ledc_channel_config(&ledc_conf); //setup the configuration * * ----------------EXAMPLE OF SETTING DUTY --- ----------------- * uint32_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index cd0725d2ce..dafdf54a84 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -27,7 +27,7 @@ extern "C" { #include extern const char* UART_TAG; -#define UART_FIFO_LEN (128) //Do Not Change it +#define UART_FIFO_LEN (128) //Do not change this, this value describes the length of the gardware FIFO in the ESP32 #define UART_INTR_MASK 0x1ff #define UART_LINE_INV_MASK (0x3f << 19) @@ -150,13 +150,10 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1) : Parameter error - * UART_DATA_5_BITS (0): UART word length: 5 bits. - * UART_DATA_6_BITS (1): UART word length: 6 bits. - * UART_DATA_7_BITS (2): UART word length: 7 bits. - * UART_DATA_8_BITS (3): UART word length: 8 bits. + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*data_bit) */ -int uart_get_word_length(uart_port_t uart_num); +esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit); /** * @brief Set UART stop bits. @@ -174,12 +171,10 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1): Parameter error - * UART_STOP_BITS_1 (1): 1 stop bit - * UART_STOP_BITS_1_5 (2): 1.5 stop bits - * UART_STOP_BITS_1 (3): 2 stop bits + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*stop_bit) */ -int uart_get_stop_bits(uart_port_t uart_num); +esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); /** * @brief Set UART parity. @@ -196,13 +191,11 @@ esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1): Parameter error - * UART_PARITY_ODD (0x11): Odd parity check mode - * UART_PARITY_EVEN (0x10): Even parity check mode - * UART_PARITY_DISABLE(0x0) : parity check disabled + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*parity_mode) * */ -int uart_get_parity(uart_port_t uart_num); +esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); /** * @brief Set UART baud rate. @@ -219,11 +212,11 @@ esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); * * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL(-1): Parameter error - * Others (>0): UART baud-rate + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*baudrate) * */ -int uart_get_baudrate(uart_port_t uart_num); +esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** * @brief Set UART line inverse mode @@ -253,13 +246,10 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ * @brief Get hardware flow control mode * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL (-1): Parameter error - * UART_HW_FLOWCTRL_DISABLE (0): UART hw flow control disabled - * UART_HW_FLOWCTRL_RTS (1): UART RX flow control enabled - * UART_HW_FLOWCTRL_CTS (2): UART TX flow control enabled - * UART_HW_FLOWCTRL_CTS_RTS (3): UART TX/RX flow control enabled + * @return ESP_FAIL : Parameter error + * ESP_OK : Success, result will be put in (*flow_ctrl) */ -int uart_get_hw_flow_ctrl(uart_port_t uart_num); +esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl); /** * @brief Clear UART interrupt status @@ -453,6 +443,7 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks * * @return ESP_OK : Success + * ESP_FAIL : Parameter error * ESP_ERR_TIMEOUT: Timeout */ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait); @@ -465,7 +456,8 @@ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait * @param char* buffer : data buffer address * @param uint32_t len : data length to send * - * @return The number of data that pushed to the TX FIFO + * @return -1 : Parameter error + * OTHERS(>=0): The number of data that pushed to the TX FIFO */ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); @@ -477,7 +469,8 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); * @param char* src : data buffer address * @param size_t size : data length to send * - * @return The number of data that sent out. + * @return -1 : Parameter error + * OTHERS(>=0): The number of data that pushed to the TX FIFO */ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); @@ -490,7 +483,8 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); * @param size_t size : data length to send * @param int brk_len : break signal length (unit: one bit's time@current_baudrate) * - * @return The number of data that sent out. + * @return -1 : Parameter error + * OTHERS(>=0): The number of data that pushed to the TX FIFO */ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); @@ -498,20 +492,20 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s * @brief UART read one char * * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param TickType_t ticks_to_wait : ticks to wait. + * @param TickType_t ticks_to_wait : Timeout, count in RTOS ticks * * @return -1 : Error - * Others : return a char data from uart fifo. + * Others : return a char data from UART. */ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief UART read bytes from UART buffer * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint8_t* buf : pointer to the buffer. - * @param uint32_t length : data length - * @param TickType_t ticks_to_wait: timeout time( FreeRTOS ti c + * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uint8_t* buf : pointer to the buffer. + * @param uint32_t length : data length + * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks * * @return -1 : Error * Others : return a char data from uart fifo. @@ -542,9 +536,9 @@ esp_err_t uart_set_print_port(uart_port_t uart_no); * @brief Get the current serial port for ets_printf function * * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @return null + * @return current print port: 0: UART0; + * 1: UART1; + * 2: UART2; */ int uart_get_print_port(); @@ -637,7 +631,6 @@ int uart_get_print_port(); * break; * case UART_FIFO_OVF: //Event of HW FIFO overflow detected * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); - * while(1); * break; * case UART_BUFFER_FULL: //Event of UART ring buffer full * ESP_LOGI(UART_TAG, "ring buffer full\n"); diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 386c93dfa6..771c4a17df 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -18,86 +18,19 @@ #include "freertos/xtensa_api.h" #include "soc/gpio_sig_map.h" #include "driver/ledc.h" +#include "esp_log.h" -//TODO: to use APIs in esp_log.h. -#define LEDC_DBG_WARING_ENABLE (0) -#define LEDC_DBG_ERROR_ENABLE (0) -#define LEDC_INFO_ENABLE (0) -#define LEDC_DBG_ENABLE (0) - -//DBG INFOR -#if LEDC_DBG_ENABLE -#define LEDC_DBG(format,...) do{\ - ets_printf("[dbg][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_DBG(...) -#endif - -#if LEDC_INFO_ENABLE -#define LEDC_INFO(format,...) do{\ - ets_printf("[info][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_INFO(...) -#endif - -#if LEDC_DBG_WARING_ENABLE -#define LEDC_WARING(format,...) do{\ - ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_WARING(...) -#endif -#if LEDC_DBG_ERROR_ENABLE -#define LEDC_ERROR(format,...) do{\ - ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\ - ets_printf(format,##__VA_ARGS__);\ -}while(0) -#else -#define LEDC_ERROR(...) -#endif - +const char* LEDC_TAG = "LEDC"; static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; - -static bool ledc_is_valid_channel(uint32_t channel) -{ - if(channel > LEDC_CHANNEL_7) { - LEDC_ERROR("LEDC CHANNEL ERR: %d\n",channel); - return false; - } - return true; -} - -static bool ledc_is_valid_mode(uint32_t mode) -{ - if(mode >= LEDC_SPEED_MODE_MAX) { - LEDC_ERROR("LEDC MODE ERR: %d\n",mode); - return false; - } - return true; -} - -static bool ledc_is_valid_timer(int timer) -{ - if(timer > LEDC_TIMER_3) { - LEDC_ERROR("LEDC TIMER ERR: %d\n", timer); - return false; - } - return true; -} +#define LEDC_CHECK(a, str, ret_val) if (!(a)) { \ + ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret_val); \ + } esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num; LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src; @@ -125,12 +58,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_idx)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx; portEXIT_CRITICAL(&ledc_spinlock); @@ -139,12 +68,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1; LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0; @@ -154,12 +79,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1; portEXIT_CRITICAL(&ledc_spinlock); @@ -168,12 +89,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_timer(timer_sel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0; portEXIT_CRITICAL(&ledc_spinlock); @@ -182,9 +99,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); uint32_t value; uint32_t intr_type = type; portENTER_CRITICAL(&ledc_spinlock); @@ -200,9 +115,7 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg) { - if(fn == NULL) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(fn, "ledc isr null\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); ESP_INTR_DISABLE(ledc_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num); @@ -218,16 +131,13 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) int bit_num = timer_conf->bit_num; int timer_num = timer_conf->timer_num; int speed_mode = timer_conf->speed_mode; - - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { - LEDC_ERROR("freq_hz=%u bit_num=%u\n", freq_hz, bit_num); + ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u\n", freq_hz, bit_num); return ESP_ERR_INVALID_ARG; } if(timer_num > LEDC_TIMER_3) { - LEDC_ERROR("Time Select %u\n", timer_num); + ESP_LOGE(LEDC_TAG, "Time Select %u\n", timer_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -239,7 +149,7 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) /*Selet the reference tick*/ div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; if(div_param <= 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { - LEDC_ERROR("div param err,div_param=%u\n", div_param); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", (uint32_t)div_param); ret = ESP_FAIL; } timer_clk_src = LEDC_REF_TICK; @@ -254,6 +164,21 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) return ret; } +esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) +{ + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO); + gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); + if(speed_mode == LEDC_HIGH_SPEED_MODE) { + gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0); + } else { + + } + return ESP_OK; +} + esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) { uint32_t speed_mode = ledc_conf->speed_mode; @@ -262,21 +187,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) uint32_t timer_select = ledc_conf->timer_sel; uint32_t intr_type = ledc_conf->intr_type; uint32_t duty = ledc_conf->duty; - - if(!ledc_is_valid_channel(ledc_channel)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!GPIO_IS_VALID_OUTPUT_GPIO(gpio_num)) { - LEDC_ERROR("GPIO number error: IO%d\n ", gpio_num); - return ESP_ERR_INVALID_ARG; - } - if(timer_select > LEDC_TIMER_3) { - LEDC_ERROR("Time Select %u\n", timer_select); - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; /*set channel parameters*/ /* channel parameters decide how the waveform looks like in one period*/ @@ -288,7 +202,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select); /*set interrupt type*/ ledc_enable_intr_type(speed_mode, ledc_channel, intr_type); - LEDC_INFO("LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n", + ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n", ledc_channel, gpio_num, duty, timer_select ); /*set LEDC signal in gpio matrix*/ @@ -300,12 +214,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; @@ -315,12 +225,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1; LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0; @@ -331,18 +237,11 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } - if(fade_direction > LEDC_DUTY_DIR_INCREASE) { - LEDC_ERROR("Duty direction err\n"); - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error\n", ESP_ERR_INVALID_ARG); if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { - LEDC_ERROR("step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale); + ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale); return ESP_ERR_INVALID_ARG; } ledc_duty_config(speed_mode, @@ -359,12 +258,8 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } - if(!ledc_is_valid_channel(channel)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -379,18 +274,14 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - if(!ledc_is_valid_mode(speed_mode)) { - return -1; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (-1)); uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4); return duty; } esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz) { - if(!ledc_is_valid_mode(speed_mode)) { - return ESP_ERR_INVALID_ARG; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); esp_err_t ret = ESP_OK; uint32_t div_num = 0; @@ -403,7 +294,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; } if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { - LEDC_ERROR("div param err,div_param=%u\n", div_num); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", div_num); ret = ESP_FAIL; } LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num = div_num; @@ -413,9 +304,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num) { - if(!ledc_is_valid_mode(speed_mode)) { - return 0; - } + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (0)); portENTER_CRITICAL(&ledc_spinlock); uint32_t freq = 0; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; diff --git a/components/driver/uart.c b/components/driver/uart.c index f585c0965d..29e4522d6d 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -90,10 +90,11 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit return ESP_OK; } -int uart_get_word_length(uart_port_t uart_num) +esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - return UART[uart_num]->conf0.bit_num; + *(data_bit) = UART[uart_num]->conf0.bit_num; + return ESP_OK; } esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) @@ -106,10 +107,11 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) return ESP_OK; } -int uart_get_stop_bits(uart_port_t uart_num) +esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - return UART[uart_num]->conf0.stop_bit_num; + (*stop_bit) = UART[uart_num]->conf0.stop_bit_num; + return ESP_OK; } esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) @@ -122,19 +124,20 @@ esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) return ESP_OK; } -int uart_get_parity(uart_port_t uart_num) +esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); int val = UART[uart_num]->conf0.val; if(val & UART_PARITY_EN_M) { if(val & UART_PARITY_M) { - return UART_PARITY_ODD; + (*parity_mode) = UART_PARITY_ODD; } else { - return UART_PARITY_EVEN; + (*parity_mode) = UART_PARITY_EVEN; } } else { - return UART_PARITY_DISABLE; + (*parity_mode) = UART_PARITY_DISABLE; } + return ESP_OK; } esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) @@ -149,14 +152,14 @@ esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) return ESP_OK; } -int uart_get_baudrate(uart_port_t uart_num) +esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); uint32_t clk_div = (UART[uart_num]->clk_div.div_int << 4) | UART[uart_num]->clk_div.div_frag; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - uint32_t baudrate = ((UART_CLK_FREQ) << 4) / clk_div; - return baudrate; + (*baudrate) = ((UART_CLK_FREQ) << 4) / clk_div; + return ESP_OK; } esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) @@ -192,7 +195,7 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow return ESP_OK; } -int uart_get_hw_flow_ctrl(uart_port_t uart_num) +esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); uart_hw_flowcontrol_t val = UART_HW_FLOWCTRL_DISABLE; @@ -202,7 +205,8 @@ int uart_get_hw_flow_ctrl(uart_port_t uart_num) if(UART[uart_num]->conf0.tx_flow_en) { val |= UART_HW_FLOWCTRL_CTS; } - return val; + (*flow_ctrl) = val; + return ESP_OK; } static esp_err_t uart_reset_fifo(uart_port_t uart_num) @@ -311,11 +315,11 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* //only one GPIO pad can connect with input signal esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); - UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); - UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); - UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); +// UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); +// UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); +// UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); +// UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); +// UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { From d9005e739dbf1e32aab0b2cbce01eae100d43ef6 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 24 Oct 2016 21:18:02 +0800 Subject: [PATCH 054/285] Add bytebuffer support to ringbuf.c --- .../freertos/include/freertos/ringbuf.h | 50 ++- components/freertos/ringbuf.c | 344 +++++++++++++----- 2 files changed, 303 insertions(+), 91 deletions(-) diff --git a/components/freertos/include/freertos/ringbuf.h b/components/freertos/include/freertos/ringbuf.h index 7884e9856e..93ba30758e 100644 --- a/components/freertos/include/freertos/ringbuf.h +++ b/components/freertos/include/freertos/ringbuf.h @@ -18,22 +18,34 @@ to this bit of memory will block. The requirement for items to be contiguous is slightly problematic when the only way to place the next item would involve a wraparound from the end to the beginning of the ringbuffer. This can -be solved in two ways: -- allow_split_items = pdTRUE: The insertion code will split the item in two items; one which fits +be solved (or not) in a few ways: +- type = RINGBUF_TYPE_ALLOWSPLIT: The insertion code will split the item in two items; one which fits in the space left at the end of the ringbuffer, one that contains the remaining data which is placed in the beginning. Two xRingbufferReceive calls will be needed to retrieve the data. -- allow_split_items = pdFALSE: The insertion code will leave the room at the end of the ringbuffer +- type = RINGBUF_TYPE_NOSPLIT: The insertion code will leave the room at the end of the ringbuffer unused and instead will put the entire item at the start of the ringbuffer, as soon as there is enough free space. +- type = RINGBUF_TYPE_BYTEBUF: This is your conventional byte-based ringbuffer. It does have no +overhead, but it has no item contiguousness either: a read will just give you the entire written +buffer space, or the space up to the end of the buffer, and writes can be broken up in any way +possible. Note that this type cannot do a 2nd read before returning the memory of the 1st. The maximum size of an item will be affected by this decision. When split items are allowed, it's acceptable to push items of (buffer_size)-16 bytes into the buffer. When it's not allowed, the -maximum size is (buffer_size/2)-8 bytes. +maximum size is (buffer_size/2)-8 bytes. The bytebuf can fill the entire buffer with data, it has +no overhead. */ //An opaque handle for a ringbuff object. typedef void * RingbufHandle_t; +//The various types of buffer +typedef enum { + RINGBUF_TYPE_NOSPLIT = 0, + RINGBUF_TYPE_ALLOWSPLIT, + RINGBUF_TYPE_BYTEBUF +} ringbuf_type_t; + /** * @brief Create a ring buffer @@ -45,7 +57,7 @@ typedef void * RingbufHandle_t; * * @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error. */ -RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items); +RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type); /** @@ -120,6 +132,34 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size); +/** + * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes + * to return + * + * @param ringbuf - Ring buffer to retrieve the item from + * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. + * @param xTicksToWait - Ticks to wait for items in the ringbuffer. + * + * @return Pointer to the retrieved item on success; *item_size filled with the length of the + * item. NULL on timeout, *item_size is untouched in that case. + */ +void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size); + + +/** + * @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes + * to return. Call this from an ISR. + * + * @param ringbuf - Ring buffer to retrieve the item from + * @param item_size - Pointer to a variable to which the size of the retrieved item will be written. + * + * @return Pointer to the retrieved item on success; *item_size filled with the length of the + * item. NULL when the ringbuffer is empty, *item_size is untouched in that case. + */ +void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size); + + + /** * @brief Return a previously-retrieved item to the ringbuffer * diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 3651753147..77eb362843 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -18,6 +18,7 @@ #include "freertos/queue.h" #include "freertos/xtensa_api.h" #include "freertos/ringbuf.h" +#include "esp_attr.h" #include #include #include @@ -25,6 +26,7 @@ typedef enum { flag_allowsplit = 1, + flag_bytebuf = 2, } rbflag_t; typedef enum { @@ -33,8 +35,10 @@ typedef enum { } itemflag_t; +typedef struct ringbuf_t ringbuf_t; + //The ringbuffer structure -typedef struct { +struct ringbuf_t { SemaphoreHandle_t free_space_sem; //Binary semaphore, wakes up writing threads when there's more free space SemaphoreHandle_t items_buffered_sem; //Binary semaphore, indicates there are new packets in the circular buffer. See remark. size_t size; //Size of the data storage @@ -44,7 +48,12 @@ typedef struct { uint8_t *data; //Data storage portMUX_TYPE mux; //Spinlock for actual data/ptr/struct modification rbflag_t flags; -} ringbuf_t; + size_t maxItemSize; + //The following keep function pointers to hold different implementations for ringbuffer management. + BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size); + uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length); + void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item); +}; @@ -73,14 +82,16 @@ static int ringbufferFreeMem(ringbuf_t *rb) return free_size-1; } -//Copies a single item to the ring buffer. Assumes there is space in the ringbuffer and + +//Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on //success, pdFALSE if it can't make the item fit and the calling routine needs to retry //later or fail. //This function by itself is not threadsafe, always call from within a muxed section. -static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) +static BaseType_t copyItemToRingbufNoSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) { - size_t rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value + size_t rbuffer_size; + rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size //of a header to the end of the ringbuff @@ -88,65 +99,28 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe //See if we have enough contiguous space to write the buffer. if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) { - //The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do - //that depending on how the ringbuffer is configured. - //The code here is also expected to check if the buffer, mangled in whatever way is implemented, - //will still fit, and return pdFALSE if that is not the case. - if (rb->flags & flag_allowsplit) { - //Buffer plus header is not going to fit in the room from wr_pos to the end of the - //ringbuffer... we need to split the write in two. - //First, see if this will fit at all. - if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) { - //Will not fit. - return pdFALSE; - } - //Because the code at the end of the function makes sure we always have - //room for a header, this should never assert. - configASSERT(rem_len>=sizeof(buf_entry_hdr_t)); - //Okay, it should fit. Write everything. - //First, place bit of buffer that does fit. Write header first... - buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; - hdr->flags=0; - hdr->len=rem_len-sizeof(buf_entry_hdr_t); - rb->write_ptr+=sizeof(buf_entry_hdr_t); - rem_len-=sizeof(buf_entry_hdr_t); - if (rem_len!=0) { - //..then write the data bit that fits. - memcpy(rb->write_ptr, buffer, rem_len); - //Update vars so the code later on will write the rest of the data. - buffer+=rem_len; - rbuffer_size-=rem_len; - buffer_size-=rem_len; - } else { - //Huh, only the header fit. Mark as dummy so the receive function doesn't receive - //an useless zero-byte packet. - hdr->flags|=iflag_dummydata; - } - rb->write_ptr=rb->data; - } else { - //Buffer plus header is not going to fit in the room from wr_pos to the end of the - //ringbuffer... but we're not allowed to split the buffer. We need to fill the - //rest of the ringbuffer with a dummy item so we can place the data at the _start_ of - //the ringbuffer.. - //First, find out if we actually have enough space at the start of the ringbuffer to - //make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr) - if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) { - //Will not fit. - return pdFALSE; - } - //If the read buffer hasn't wrapped around yet, there's no way this will work either. - if (rb->free_ptr > rb->write_ptr) { - //No luck. - return pdFALSE; - } - - //Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet. - buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; - hdr->flags=iflag_dummydata; - //Reset the write pointer to the start of the ringbuffer so the code later on can - //happily write the data. - rb->write_ptr=rb->data; + //Buffer plus header is not going to fit in the room from wr_pos to the end of the + //ringbuffer... but we're not allowed to split the buffer. We need to fill the + //rest of the ringbuffer with a dummy item so we can place the data at the _start_ of + //the ringbuffer.. + //First, find out if we actually have enough space at the start of the ringbuffer to + //make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr) + if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) { + //Will not fit. + return pdFALSE; } + //If the read buffer hasn't wrapped around yet, there's no way this will work either. + if (rb->free_ptr > rb->write_ptr) { + //No luck. + return pdFALSE; + } + + //Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet. + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; + hdr->flags=iflag_dummydata; + //Reset the write pointer to the start of the ringbuffer so the code later on can + //happily write the data. + rb->write_ptr=rb->data; } else { //No special handling needed. Checking if it's gonna fit probably still is a good idea. if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) { @@ -174,9 +148,117 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe return pdTRUE; } +//Copies a single item to the ring buffer; allows split items. Assumes there is space in the ringbuffer and +//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on +//success, pdFALSE if it can't make the item fit and the calling routine needs to retry +//later or fail. +//This function by itself is not threadsafe, always call from within a muxed section. +static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) +{ + size_t rbuffer_size; + rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value + configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned + configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size + //of a header to the end of the ringbuff + size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer + + //See if we have enough contiguous space to write the buffer. + if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) { + //The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do + //that depending on how the ringbuffer is configured. + //The code here is also expected to check if the buffer, mangled in whatever way is implemented, + //will still fit, and return pdFALSE if that is not the case. + //Buffer plus header is not going to fit in the room from wr_pos to the end of the + //ringbuffer... we need to split the write in two. + //First, see if this will fit at all. + if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) { + //Will not fit. + return pdFALSE; + } + //Because the code at the end of the function makes sure we always have + //room for a header, this should never assert. + configASSERT(rem_len>=sizeof(buf_entry_hdr_t)); + //Okay, it should fit. Write everything. + //First, place bit of buffer that does fit. Write header first... + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; + hdr->flags=0; + hdr->len=rem_len-sizeof(buf_entry_hdr_t); + rb->write_ptr+=sizeof(buf_entry_hdr_t); + rem_len-=sizeof(buf_entry_hdr_t); + if (rem_len!=0) { + //..then write the data bit that fits. + memcpy(rb->write_ptr, buffer, rem_len); + //Update vars so the code later on will write the rest of the data. + buffer+=rem_len; + rbuffer_size-=rem_len; + buffer_size-=rem_len; + } else { + //Huh, only the header fit. Mark as dummy so the receive function doesn't receive + //an useless zero-byte packet. + hdr->flags|=iflag_dummydata; + } + rb->write_ptr=rb->data; + } else { + //No special handling needed. Checking if it's gonna fit probably still is a good idea. + if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) { + //Buffer is not going to fit, period. + return pdFALSE; + } + } + + //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer. + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr; + hdr->len=buffer_size; + hdr->flags=0; + rb->write_ptr+=sizeof(buf_entry_hdr_t); + memcpy(rb->write_ptr, buffer, buffer_size); + rb->write_ptr+=rbuffer_size; + + //The buffer will wrap around if we don't have room for a header anymore. + if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) { + //'Forward' the write buffer until we are at the start of the ringbuffer. + //The read pointer will always be at the start of a full header, which cannot + //exist at the point of the current write pointer, so there's no chance of overtaking + //that. + rb->write_ptr=rb->data; + } + return pdTRUE; +} + + +//Copies a bunch of daya to the ring bytebuffer. Assumes there is space in the ringbuffer and +//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on +//success, pdFALSE if it can't make the item fit and the calling routine needs to retry +//later or fail. +//This function by itself is not threadsafe, always call from within a muxed section. +static BaseType_t copyItemToRingbufByteBuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size) +{ + size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer + + //See if we have enough contiguous space to write the buffer. + if (rem_len < buffer_size) { + //...Nope. Write the data bit that fits. + memcpy(rb->write_ptr, buffer, rem_len); + //Update vars so the code later on will write the rest of the data. + buffer+=rem_len; + buffer_size-=rem_len; + rb->write_ptr=rb->data; + } + + //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer. + memcpy(rb->write_ptr, buffer, buffer_size); + rb->write_ptr+=buffer_size; + //The buffer will wrap around if we're at the end. + if ((rb->data+rb->size)==rb->write_ptr) { + rb->write_ptr=rb->data; + } + return pdTRUE; +} + //Retrieves a pointer to the data of the next item, or NULL if this is not possible. //This function by itself is not threadsafe, always call from within a muxed section. -static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length) +//Because we always return one item, this function ignores the wanted_length variable. +static uint8_t *getItemFromRingbufDefault(ringbuf_t *rb, size_t *length, int wanted_length) { uint8_t *ret; configASSERT(((int)rb->read_ptr&3)==0); @@ -210,10 +292,48 @@ static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length) return ret; } +//Retrieves a pointer to the data in the buffer, or NULL if this is not possible. +//This function by itself is not threadsafe, always call from within a muxed section. +//This function honours the wanted_length and will never return more data than this. +static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wanted_length) +{ + uint8_t *ret; + if (rb->read_ptr != rb->free_ptr) { + //This type of ringbuff does not support multiple outstanding buffers. + return NULL; + } + if (rb->read_ptr == rb->write_ptr) { + //No data available. + return NULL; + } + ret=rb->read_ptr; + if (rb->read_ptr > rb->write_ptr) { + //Available data wraps around. Give data until the end of the buffer. + *length=rb->size-(rb->read_ptr - rb->data); + if (wanted_length != 0 && *length > wanted_length) { + *length=wanted_length; + rb->read_ptr+=wanted_length; + } else { + rb->read_ptr=rb->data; + } + } else { + //Return data up to write pointer. + *length=rb->write_ptr -rb->read_ptr; + if (wanted_length != 0 && *length > wanted_length) { + *length=wanted_length; + rb->read_ptr+=wanted_length; + } else { + rb->read_ptr=rb->write_ptr; + } + } + return ret; +} + + //Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer //can be increase. //This function by itself is not threadsafe, always call from within a muxed section. -static void returnItemToRingbuf(ringbuf_t *rb, void *item) { +static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { uint8_t *data=(uint8_t*)item; configASSERT(((int)rb->free_ptr&3)==0); configASSERT(data >= rb->data); @@ -249,6 +369,17 @@ static void returnItemToRingbuf(ringbuf_t *rb, void *item) { } +//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer +//can be increase. +//This function by itself is not threadsafe, always call from within a muxed section. +static void returnItemToRingbufBytebuf(ringbuf_t *rb, void *item) { + uint8_t *data=(uint8_t*)item; + configASSERT(data >= rb->data); + configASSERT(data < rb->data+rb->size); + //Free the read memory. + rb->free_ptr=rb->read_ptr; +} + void xRingbufferPrintInfo(RingbufHandle_t ringbuf) { ringbuf_t *rb=(ringbuf_t *)ringbuf; @@ -259,7 +390,7 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf) -RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items) +RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type) { ringbuf_t *rb = malloc(sizeof(ringbuf_t)); if (rb==NULL) goto err; @@ -273,9 +404,35 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_item rb->free_space_sem = xSemaphoreCreateBinary(); rb->items_buffered_sem = xSemaphoreCreateBinary(); rb->flags=0; - if (allow_split_items) rb->flags|=flag_allowsplit; + if (type==RINGBUF_TYPE_ALLOWSPLIT) { + rb->flags|=flag_allowsplit; + rb->copyItemToRingbufImpl=copyItemToRingbufAllowSplit; + rb->getItemFromRingbufImpl=getItemFromRingbufDefault; + rb->returnItemToRingbufImpl=returnItemToRingbufDefault; + //Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead. + rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4; + } else if (type==RINGBUF_TYPE_BYTEBUF) { + rb->flags|=flag_bytebuf; + rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf; + rb->getItemFromRingbufImpl=getItemFromRingbufByteBuf; + rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf; + //Calculate max item size. We have no headers and can split anywhere -> size is total size minus one. + rb->maxItemSize=rb->size-1; + } else if (type==RINGBUF_TYPE_NOSPLIT) { + rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit; + rb->getItemFromRingbufImpl=getItemFromRingbufDefault; + rb->returnItemToRingbufImpl=returnItemToRingbufDefault; + //Calculate max item size. Worst case, we have the write ptr in such a position that we are lacking four bytes of free + //memory to put an item into the rest of the memory. If this happens, we have to dummy-fill + //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill + //with the real item. (item size being header+data) + rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4; + } else { + configASSERT(0); + } if (rb->free_space_sem == NULL || rb->items_buffered_sem == NULL) goto err; vPortCPUInitializeMutex(&rb->mux); + return (RingbufHandle_t)rb; err: @@ -303,18 +460,7 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf) { ringbuf_t *rb=(ringbuf_t *)ringbuf; configASSERT(rb); - //In both cases, we return 4 bytes less than what we actually can have. If the ringbuffer is - //indeed entirely filled, read_ptr==free_ptr, which throws off the free space calculation. - if (rb->flags & flag_allowsplit) { - //Worst case, we need to split an item into two, which means two headers of overhead. - return rb->size-(sizeof(buf_entry_hdr_t)*2)-4; - } else { - //Worst case, we have the write ptr in such a position that we are lacking four bytes of free - //memory to put an item into the rest of the memory. If this happens, we have to dummy-fill - //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill - //with the real item. (item size being header+data) - return (rb->size/2)-sizeof(buf_entry_hdr_t)-4; - } + return rb->maxItemSize; } BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait) @@ -352,7 +498,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry //everything if this is the case. Otherwise, we can write and are done. - done=copyItemToRingbuf(rb, data, dataSize); + done=rb->copyItemToRingbufImpl(rb, data, dataSize); portEXIT_CRITICAL(&rb->mux); } xSemaphoreGive(rb->items_buffered_sem); @@ -371,7 +517,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da //Does not fit in the remaining space in the ringbuffer. write_succeeded=pdFALSE; } else { - write_succeeded = copyItemToRingbuf(rb, data, dataSize); + write_succeeded = rb->copyItemToRingbufImpl(rb, data, dataSize); } portEXIT_CRITICAL_ISR(&rb->mux); if (write_succeeded) { @@ -381,7 +527,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da } -void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait) +static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) { ringbuf_t *rb=(ringbuf_t *)ringbuf; uint8_t *itemData; @@ -398,7 +544,7 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t } //Okay, we seem to have data in the buffer. Grab the mux and copy it out if it's still there. portENTER_CRITICAL(&rb->mux); - itemData=getItemFromRingbuf(rb, item_size); + itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size); portEXIT_CRITICAL(&rb->mux); if (itemData) { //We managed to get an item. @@ -408,6 +554,11 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t return (void*)itemData; } +void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait) +{ + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0); +} + void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size) { @@ -415,7 +566,28 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size) uint8_t *itemData; configASSERT(rb); portENTER_CRITICAL_ISR(&rb->mux); - itemData=getItemFromRingbuf(rb, item_size); + itemData=rb->getItemFromRingbufImpl(rb, item_size, 0); + portEXIT_CRITICAL_ISR(&rb->mux); + return (void*)itemData; +} + +void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) { + ringbuf_t *rb=(ringbuf_t *)ringbuf; + if (wanted_size == 0) return NULL; + configASSERT(rb); + configASSERT(rb->flags & flag_bytebuf); + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size); +} + +void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size) +{ + ringbuf_t *rb=(ringbuf_t *)ringbuf; + uint8_t *itemData; + if (wanted_size == 0) return NULL; + configASSERT(rb); + configASSERT(rb->flags & flag_bytebuf); + portENTER_CRITICAL_ISR(&rb->mux); + itemData=rb->getItemFromRingbufImpl(rb, item_size, 0); portEXIT_CRITICAL_ISR(&rb->mux); return (void*)itemData; } @@ -425,7 +597,7 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item) { ringbuf_t *rb=(ringbuf_t *)ringbuf; portENTER_CRITICAL_ISR(&rb->mux); - returnItemToRingbuf(rb, item); + rb->returnItemToRingbufImpl(rb, item); portEXIT_CRITICAL_ISR(&rb->mux); xSemaphoreGive(rb->free_space_sem); } @@ -435,7 +607,7 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_ { ringbuf_t *rb=(ringbuf_t *)ringbuf; portENTER_CRITICAL_ISR(&rb->mux); - returnItemToRingbuf(rb, item); + rb->returnItemToRingbufImpl(rb, item); portEXIT_CRITICAL_ISR(&rb->mux); xSemaphoreGiveFromISR(rb->free_space_sem, higher_prio_task_awoken); } From d7ea61734b2474460fde113833a382a81ba88fb6 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 24 Oct 2016 21:25:48 +0800 Subject: [PATCH 055/285] Tabs -> spaces --- components/freertos/ringbuf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 77eb362843..ce5504596a 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -556,7 +556,7 @@ static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_siz void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait) { - return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0); + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0); } @@ -573,17 +573,17 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size) void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) { ringbuf_t *rb=(ringbuf_t *)ringbuf; - if (wanted_size == 0) return NULL; + if (wanted_size == 0) return NULL; configASSERT(rb); configASSERT(rb->flags & flag_bytebuf); - return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size); + return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size); } void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size) { ringbuf_t *rb=(ringbuf_t *)ringbuf; uint8_t *itemData; - if (wanted_size == 0) return NULL; + if (wanted_size == 0) return NULL; configASSERT(rb); configASSERT(rb->flags & flag_bytebuf); portENTER_CRITICAL_ISR(&rb->mux); From 78392bf76b3d47ba290a0d97f443e695916416f5 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Fri, 28 Oct 2016 14:32:13 +0800 Subject: [PATCH 056/285] components/openssl: change the description 1. change the description of Makefile.projbuild 2. remove the license header in the API document 3. add private inlcuding header code in the components file --- components/openssl/Makefile.projbuild | 3 +-- components/openssl/OpenSSL-APIs.rst | 7 +------ components/openssl/component.mk | 3 ++- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/components/openssl/Makefile.projbuild b/components/openssl/Makefile.projbuild index b1d5641231..f8501f3187 100644 --- a/components/openssl/Makefile.projbuild +++ b/components/openssl/Makefile.projbuild @@ -1,3 +1,2 @@ -# Anyone compiling mbedTLS code needs the name of the -# alternative config file +# Anyone compiling openssl code needs the mbedtls library and header file diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index e7877b128c..2b606dbcf2 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -1,14 +1,9 @@ OpenSSL-APIs ------------ -All original source code in this repository is Copyright (C) 2015-2016 -Espressif Systems. This source code is licensed under the Apache -License 2.0 as described in the file LICENSE. - OpenSSL APIs not mentioned in this article are not open to public for the time, also do not have the corresponding function. -If user calls it directly, it will always return an error or may show cannot link -at compile time. +If user calls it directly, it will always return an error or may show cannot link at compile time. Chapter Introduction ==================== diff --git a/components/openssl/component.mk b/components/openssl/component.mk index 97de6975c9..2dfcc6b38d 100644 --- a/components/openssl/component.mk +++ b/components/openssl/component.mk @@ -2,7 +2,8 @@ # Component Makefile # -COMPONENT_ADD_INCLUDEDIRS := include include/internal include/platform include/oepnssl +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := include/internal include/platform include/openssl COMPONENT_SRCDIRS := library platform From 41a91d7cb9540f718811c62629aaca140d87ac08 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 31 Oct 2016 14:06:29 +0800 Subject: [PATCH 057/285] feature/openssl: change the description for docbook --- components/openssl/Makefile.projbuild | 2 -- components/openssl/OpenSSL-APIs.rst | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 components/openssl/Makefile.projbuild diff --git a/components/openssl/Makefile.projbuild b/components/openssl/Makefile.projbuild deleted file mode 100644 index f8501f3187..0000000000 --- a/components/openssl/Makefile.projbuild +++ /dev/null @@ -1,2 +0,0 @@ -# Anyone compiling openssl code needs the mbedtls library and header file - diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index 2b606dbcf2..39fa0ebb9b 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -1,9 +1,12 @@ OpenSSL-APIs ------------ +This directory does not contain OpenSSL itself, but the code here can be used as a wrapper for applications using the OpenSSL API. +It uses mbedSSL to do the actual work, so anyone compiling openssl code needs the mbedtls library and header file. + OpenSSL APIs not mentioned in this article are not open to public for the time, also do not have the corresponding function. -If user calls it directly, it will always return an error or may show cannot link at compile time. +If user calls it directly, it will always return an error or may show cannot link at compiling time. Chapter Introduction ==================== From 9555ce291e094b8e9e25c1b6e4a36ee8d340ca14 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 31 Oct 2016 14:13:00 +0800 Subject: [PATCH 058/285] feature/openssl: correct wrong description --- components/openssl/OpenSSL-APIs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/openssl/OpenSSL-APIs.rst b/components/openssl/OpenSSL-APIs.rst index 39fa0ebb9b..93e438dcf9 100644 --- a/components/openssl/OpenSSL-APIs.rst +++ b/components/openssl/OpenSSL-APIs.rst @@ -2,7 +2,7 @@ OpenSSL-APIs ------------ This directory does not contain OpenSSL itself, but the code here can be used as a wrapper for applications using the OpenSSL API. -It uses mbedSSL to do the actual work, so anyone compiling openssl code needs the mbedtls library and header file. +It uses mbedTLS to do the actual work, so anyone compiling openssl code needs the mbedtls library and header file. OpenSSL APIs not mentioned in this article are not open to public for the time, also do not have the corresponding function. From 2119b9846962c2b2d961fa13c26662a2be711e19 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 31 Oct 2016 19:08:56 +0800 Subject: [PATCH 059/285] spi_flash: remove unnecessary src pointer alignment check in spi_flash_write ROM SPIWrite routine can work with unaligned sources, so this check is unnecessary. Furthermore, it breaks nvs_set_str and nvs_get_blob when data pointer is unaligned. Also fix stray backslash in COUNTER_STOP macro --- components/spi_flash/flash_ops.c | 6 +----- components/spi_flash/include/esp_spi_flash.h | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index ae72568aa5..134e1fe65b 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -39,7 +39,7 @@ static spi_flash_counters_t s_flash_stats; #define COUNTER_STOP(counter) \ do{ \ s_flash_stats.counter.count++; \ - s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); \\ + s_flash_stats.counter.time += (xthal_get_ccount() - ts_begin) / (XT_CLOCK_FREQ / 1000000); \ } while(0) #define COUNTER_ADD_BYTES(counter, size) \ @@ -126,10 +126,6 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size) esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t size) { - // TODO: replace this check with code which deals with unaligned sources - if (((ptrdiff_t) src) % 4 != 0) { - return ESP_ERR_INVALID_ARG; - } // Destination alignment is also checked in ROM code, but we can give // better error code here // TODO: add handling of unaligned destinations diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index c65eaa5836..840bbc4971 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -74,7 +74,7 @@ esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); /** * @brief Write data to Flash. * - * @note Both des_addr and src_addr have to be 4-byte aligned. + * @note Address in flash, dest, has to be 4-byte aligned. * This is a temporary limitation which will be removed. * * @param dest destination address in Flash @@ -88,7 +88,7 @@ esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); /** * @brief Read data from Flash. * - * @note Both des_addr and src_addr have to be 4-byte aligned. + * @note Both src and dest have to be 4-byte aligned. * This is a temporary limitation which will be removed. * * @param src source address of the data in Flash. From 269332f473b6e3b2960f53605dbac840ecedd60d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 31 Oct 2016 19:11:40 +0800 Subject: [PATCH 060/285] nvs_flash: use CRC-32 routine compatible with ROM version Host tests used different flavour of CRC-32, which made it impossible to load NVS partition dumps created on the chip --- components/nvs_flash/test/crc.cpp | 64 ++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/components/nvs_flash/test/crc.cpp b/components/nvs_flash/test/crc.cpp index 288b58a323..4cbb9be9ec 100644 --- a/components/nvs_flash/test/crc.cpp +++ b/components/nvs_flash/test/crc.cpp @@ -14,25 +14,51 @@ #include #include -extern "C" unsigned long crc32_le(unsigned long crc_in, unsigned char const* data, unsigned int length) -{ - uint32_t i; - bool bit; - uint8_t c; - uint32_t crc = (uint32_t) crc_in; +static const unsigned int crc32_le_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; - while (length--) { - c = *data++; - for (i = 0x80; i > 0; i >>= 1) { - bit = crc & 0x80000000; - if (c & i) { - bit = !bit; - } - crc <<= 1; - if (bit) { - crc ^= 0x04c11db7; - } - } + + +extern "C" unsigned int crc32_le(unsigned int crc, unsigned char const * buf,unsigned int len) +{ + unsigned int i; + crc = ~crc; + for(i=0;i>8); } - return crc; + return ~crc; } + From 413f2c00f69ff6ce9777eab366280eb82cb3ac35 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 31 Oct 2016 19:17:25 +0800 Subject: [PATCH 061/285] nvs_flash: introduce write failures after each word written Previously the test bench would check failure recovery by introducing error after each write operation. This makes checks a bit more extensive (and much longer) by failing after every word written. Surprisingly, this change didn't expose any bugs. --- .../nvs_flash/test/spi_flash_emulation.h | 8 +++---- components/nvs_flash/test/test_nvs.cpp | 22 ++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/components/nvs_flash/test/spi_flash_emulation.h b/components/nvs_flash/test/spi_flash_emulation.h index ba50c4f9e4..4e544a39e2 100644 --- a/components/nvs_flash/test/spi_flash_emulation.h +++ b/components/nvs_flash/test/spi_flash_emulation.h @@ -74,11 +74,11 @@ public: return false; } - if (mFailCountdown != SIZE_MAX && mFailCountdown-- == 0) { - return false; - } - for (size_t i = 0; i < size / 4; ++i) { + if (mFailCountdown != SIZE_MAX && mFailCountdown-- == 0) { + return false; + } + uint32_t sv = src[i]; size_t pos = dstAddr / 4 + i; uint32_t& dv = mData[pos]; diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index ce552578db..223e5dea9a 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -894,7 +894,7 @@ TEST_CASE("test recovery from sudden poweroff", "[.][long][nvs][recovery][monkey size_t totalOps = 0; int lastPercent = -1; - for (uint32_t errDelay = 4; ; ++errDelay) { + for (uint32_t errDelay = 0; ; ++errDelay) { INFO(errDelay); emu.randomize(seed); emu.clearStats(); @@ -903,23 +903,25 @@ TEST_CASE("test recovery from sudden poweroff", "[.][long][nvs][recovery][monkey if (totalOps != 0) { int percent = errDelay * 100 / totalOps; - if (percent != lastPercent) { + if (percent > lastPercent) { printf("%d/%d (%d%%)\r\n", errDelay, static_cast(totalOps), percent); lastPercent = percent; } } - TEST_ESP_OK(nvs_flash_init_custom(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); nvs_handle handle; - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); - size_t count = iter_count; - if(test.doRandomThings(handle, gen, count) != ESP_ERR_FLASH_OP_FAIL) { - nvs_close(handle); - break; + + if (nvs_flash_init_custom(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK) { + if (nvs_open("namespace1", NVS_READWRITE, &handle) == ESP_OK) { + if(test.doRandomThings(handle, gen, count) != ESP_ERR_FLASH_OP_FAIL) { + nvs_close(handle); + break; + } + nvs_close(handle); + } } - nvs_close(handle); TEST_ESP_OK(nvs_flash_init_custom(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); @@ -929,7 +931,7 @@ TEST_CASE("test recovery from sudden poweroff", "[.][long][nvs][recovery][monkey CHECK(0); } nvs_close(handle); - totalOps = emu.getEraseOps() + emu.getWriteOps(); + totalOps = emu.getEraseOps() + emu.getWriteBytes() / 4; } } From d9cdc7de5811a450919518b5ea8d6a8edd0a960f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 31 Oct 2016 19:48:28 +0800 Subject: [PATCH 062/285] nvs_flash: don't allow more operation to be done on page in PageState::INVALID Currently a restart is required to recover a page from invalid state. The long-term solution is to detect such a condition and recover automatically (without a restart). This will be implemented in a separate change set. --- components/nvs_flash/src/nvs_page.cpp | 26 +++++--- .../nvs_flash/test/spi_flash_emulation.h | 12 ++++ components/nvs_flash/test/test_nvs.cpp | 62 +++++++++++++++++++ 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index fae1f6f1b7..a10b88c976 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software @@ -131,8 +131,12 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize) { Item item; - esp_err_t err; + + if (mState == PageState::INVALID) { + return ESP_ERR_NVS_INVALID_STATE; + } + if (mState == PageState::UNINITIALIZED) { err = initialize(); if (err != ESP_OK) { @@ -166,7 +170,6 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c } // write first item - size_t span = (totalSize + ENTRY_SIZE - 1) / ENTRY_SIZE; item = Item(nsIndex, datatype, span, key); mHashList.insert(item, mNextFreeEntry); @@ -215,6 +218,11 @@ esp_err_t Page::readItem(uint8_t nsIndex, ItemType datatype, const char* key, vo { size_t index = 0; Item item; + + if (mState == PageState::INVALID) { + return ESP_ERR_NVS_INVALID_STATE; + } + esp_err_t rc = findItem(nsIndex, datatype, key, index, item); if (rc != ESP_OK) { return rc; @@ -478,6 +486,8 @@ esp_err_t Page::mLoadEntryTable() mState = PageState::INVALID; return err; } + + mHashList.insert(item, i); if (item.crc32 != item.calculateCrc32()) { err = eraseEntryAndSpan(i); @@ -488,8 +498,6 @@ esp_err_t Page::mLoadEntryTable() continue; } - mHashList.insert(item, i); - if (item.datatype != ItemType::BLOB && item.datatype != ItemType::SZ) { continue; } @@ -785,8 +793,12 @@ void Page::debugDump() const Item item; readEntry(i, item); if (skip == 0) { - printf("W ns=%2u type=%2u span=%3u key=\"%s\"\n", item.nsIndex, static_cast(item.datatype), item.span, item.key); - skip = item.span - 1; + printf("W ns=%2u type=%2u span=%3u key=\"%s\" len=%d\n", item.nsIndex, static_cast(item.datatype), item.span, item.key, (item.span != 1)?((int)item.varLength.dataSize):-1); + if (item.span > 0 && item.span <= ENTRY_COUNT - i) { + skip = item.span - 1; + } else { + skip = 0; + } } else { printf("D\n"); skip--; diff --git a/components/nvs_flash/test/spi_flash_emulation.h b/components/nvs_flash/test/spi_flash_emulation.h index 4e544a39e2..14e56bab6e 100644 --- a/components/nvs_flash/test/spi_flash_emulation.h +++ b/components/nvs_flash/test/spi_flash_emulation.h @@ -141,6 +141,18 @@ public: { return reinterpret_cast(mData.data()); } + + void load(const char* filename) + { + FILE* f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + off_t size = ftell(f); + assert(size % SPI_FLASH_SEC_SIZE == 0); + mData.resize(size); + fseek(f, 0, SEEK_SET); + auto s = fread(mData.data(), SPI_FLASH_SEC_SIZE, size / SPI_FLASH_SEC_SIZE, f); + assert(s == static_cast(size / SPI_FLASH_SEC_SIZE)); + } void clearStats() { diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index 223e5dea9a..b70801f848 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -953,6 +953,68 @@ TEST_CASE("test for memory leaks in open/set", "[leaks]") } } +TEST_CASE("duplicate items are removed", "[nvs][dupes]") +{ + SpiFlashEmulator emu(3); + { + // create one item + nvs::Page p; + p.load(0); + p.writeItem(1, "opmode", 3); + } + { + // add another one without deleting the first one + nvs::Item item(1, ItemType::U8, 1, "opmode"); + item.data[0] = 2; + item.crc32 = item.calculateCrc32(); + emu.write(3 * 32, reinterpret_cast(&item), sizeof(item)); + uint32_t mask = 0xFFFFFFFA; + emu.write(32, &mask, 4); + } + { + // load page and check that second item persists + nvs::Page p; + p.load(0); + uint8_t val; + p.readItem(1, "opmode", val); + CHECK(val == 2); + CHECK(p.getErasedEntryCount() == 1); + CHECK(p.getUsedEntryCount() == 1); + } +} + +TEST_CASE("recovery after failure to write data", "[nvs]") +{ + SpiFlashEmulator emu(3); + // TODO: remove explicit alignment + const char str[] __attribute__((aligned(4))) = "value 0123456789abcdef012345678value 0123456789abcdef012345678"; + + // make flash write fail exactly in Page::writeEntryData + emu.failAfter(17); + { + Storage storage; + TEST_ESP_OK(storage.init(0, 3)); + + TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_FLASH_OP_FAIL); + + // check that repeated operations cause an error + TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_NVS_INVALID_STATE); + + uint8_t val; + TEST_ESP_ERR(storage.readItem(1, ItemType::U8, "key", &val, sizeof(val)), ESP_ERR_NVS_NOT_FOUND); + } + { + // load page and check that data was erased + Page p; + p.load(0); + CHECK(p.getErasedEntryCount() == 3); + CHECK(p.getUsedEntryCount() == 0); + + // try to write again + TEST_ESP_OK(p.writeItem(1, ItemType::SZ, "key", str, strlen(str))); + } +} + TEST_CASE("dump all performance data", "[nvs]") { std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl; From abea6c50f15f74c43f467a1179166ad031443b0a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 31 Oct 2016 21:10:47 +0800 Subject: [PATCH 063/285] nvs_flash: delete all duplicate entries in a page while loading Due to previous flash write bug it was possible to create multiple duplicate entries in a single page. Recovery logic detected that case and bailed out with an assert. This change adds graceful recovery from this condition. Tests included. --- components/nvs_flash/src/nvs_page.cpp | 40 +++++++++++------- components/nvs_flash/test/test_nvs.cpp | 56 ++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index a10b88c976..23cefd1aad 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -301,6 +301,8 @@ esp_err_t Page::eraseEntryAndSpan(size_t index) } if (item.calculateCrc32() != item.crc32) { rc = alterEntryState(index, EntryState::ERASED); + --mUsedEntryCount; + ++mErasedEntryCount; if (rc != ESP_OK) { return rc; } @@ -473,7 +475,9 @@ esp_err_t Page::mLoadEntryTable() if (end > ENTRY_COUNT) { end = ENTRY_COUNT; } - for (size_t i = 0; i < end; ++i) { + size_t span; + for (size_t i = 0; i < end; i += span) { + span = 1; if (mEntryTable.get(i) == EntryState::ERASED) { lastItemIndex = INVALID_ENTRY; continue; @@ -488,6 +492,9 @@ esp_err_t Page::mLoadEntryTable() } mHashList.insert(item, i); + + // search for potential duplicate item + size_t duplicateIndex = mHashList.find(0, item); if (item.crc32 != item.calculateCrc32()) { err = eraseEntryAndSpan(i); @@ -498,23 +505,26 @@ esp_err_t Page::mLoadEntryTable() continue; } - if (item.datatype != ItemType::BLOB && item.datatype != ItemType::SZ) { - continue; - } - - size_t span = item.span; - bool needErase = false; - for (size_t j = i; j < i + span; ++j) { - if (mEntryTable.get(j) != EntryState::WRITTEN) { - needErase = true; - lastItemIndex = INVALID_ENTRY; - break; + + if (item.datatype == ItemType::BLOB || item.datatype == ItemType::SZ) { + span = item.span; + bool needErase = false; + for (size_t j = i; j < i + span; ++j) { + if (mEntryTable.get(j) != EntryState::WRITTEN) { + needErase = true; + lastItemIndex = INVALID_ENTRY; + break; + } + } + if (needErase) { + eraseEntryAndSpan(i); + continue; } } - if (needErase) { - eraseEntryAndSpan(i); + + if (duplicateIndex < i) { + eraseEntryAndSpan(duplicateIndex); } - i += span - 1; } // check that last item is not duplicate diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index b70801f848..528c9df686 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -963,22 +963,27 @@ TEST_CASE("duplicate items are removed", "[nvs][dupes]") p.writeItem(1, "opmode", 3); } { - // add another one without deleting the first one + // add another two without deleting the first one nvs::Item item(1, ItemType::U8, 1, "opmode"); item.data[0] = 2; item.crc32 = item.calculateCrc32(); emu.write(3 * 32, reinterpret_cast(&item), sizeof(item)); - uint32_t mask = 0xFFFFFFFA; + emu.write(4 * 32, reinterpret_cast(&item), sizeof(item)); + uint32_t mask = 0xFFFFFFEA; emu.write(32, &mask, 4); } { // load page and check that second item persists - nvs::Page p; - p.load(0); + nvs::Storage s; + s.init(0, 3); uint8_t val; - p.readItem(1, "opmode", val); + ESP_ERROR_CHECK(s.readItem(1, "opmode", val)); CHECK(val == 2); - CHECK(p.getErasedEntryCount() == 1); + } + { + Page p; + p.load(0); + CHECK(p.getErasedEntryCount() == 2); CHECK(p.getUsedEntryCount() == 1); } } @@ -986,8 +991,7 @@ TEST_CASE("duplicate items are removed", "[nvs][dupes]") TEST_CASE("recovery after failure to write data", "[nvs]") { SpiFlashEmulator emu(3); - // TODO: remove explicit alignment - const char str[] __attribute__((aligned(4))) = "value 0123456789abcdef012345678value 0123456789abcdef012345678"; + const char str[] = "value 0123456789abcdef012345678value 0123456789abcdef012345678"; // make flash write fail exactly in Page::writeEntryData emu.failAfter(17); @@ -1015,6 +1019,42 @@ TEST_CASE("recovery after failure to write data", "[nvs]") } } +TEST_CASE("crc error in variable length item is handled", "[nvs]") +{ + SpiFlashEmulator emu(3); + const uint64_t before_val = 0xbef04e; + const uint64_t after_val = 0xaf7e4; + // write some data + { + Page p; + p.load(0); + TEST_ESP_OK(p.writeItem(0, "before", before_val)); + const char* str = "foobar"; + TEST_ESP_OK(p.writeItem(0, ItemType::SZ, "key", str, strlen(str))); + TEST_ESP_OK(p.writeItem(0, "after", after_val)); + } + // corrupt some data + uint32_t w; + CHECK(emu.read(&w, 32 * 3 + 8, sizeof(w))); + w &= 0xf000000f; + CHECK(emu.write(32 * 3 + 8, &w, sizeof(w))); + // load and check + { + Page p; + p.load(0); + CHECK(p.getUsedEntryCount() == 2); + CHECK(p.getErasedEntryCount() == 2); + + uint64_t val; + TEST_ESP_OK(p.readItem(0, "before", val)); + CHECK(val == before_val); + TEST_ESP_ERR(p.findItem(0, ItemType::SZ, "key"), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_OK(p.readItem(0, "after", val)); + CHECK(val == after_val); + } +} + + TEST_CASE("dump all performance data", "[nvs]") { std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl; From 92b663d9f214180486f46c5c6926f518b345083b Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 31 Oct 2016 21:26:33 +0800 Subject: [PATCH 064/285] lwip: optimize tx flow control 1. Remove tx flow control for TCP 2. Remove tx flow control for UDP temporary 3. Return the error code when call esp_wifi_internal_tx() --- components/lwip/api/sockets.c | 5 +++-- components/lwip/port/netif/wlanif.c | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 455d007ea7..df658578af 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -395,12 +395,15 @@ static void lwip_socket_drop_registered_memberships(int s); */ static inline void esp32_tx_flow_ctrl(void) { +//TODO we need to do flow control for UDP +#if 0 uint8_t _wait_delay = 1; while ((system_get_free_heap_size() < HEAP_HIGHWAT) || esp_wifi_internal_tx_is_stop()){ vTaskDelay(_wait_delay/portTICK_RATE_MS); if (_wait_delay < 64) _wait_delay *= 2; } +#endif } #else @@ -1208,8 +1211,6 @@ lwip_send(int s, const void *data, size_t size, int flags) #endif /* (LWIP_UDP || LWIP_RAW) */ } - esp32_tx_flow_ctrl(); - write_flags = NETCONN_COPY | ((flags & MSG_MORE) ? NETCONN_MORE : 0) | ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index 548bb7f970..ffad69cd46 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -142,16 +142,13 @@ low_level_output(struct netif *netif, struct pbuf *p) } } - esp_wifi_internal_tx(wifi_if, q->payload, pbuf_x_len); - return ERR_OK; - + return esp_wifi_internal_tx(wifi_if, q->payload, pbuf_x_len); #else for(q = p; q != NULL; q = q->next) { esp_wifi_internal_tx(wifi_if, q->payload, q->len); } -#endif - return ERR_OK; +#endif } /** From 41514f497aa5965a7b8b238392205d11c6c8805b Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 23 Oct 2016 22:12:34 +0200 Subject: [PATCH 065/285] Renamed .md to .rst --- CONTRIBUTING.md => CONTRIBUTING.rst | 15 ++++++++++----- README.md => README.rst | 29 ++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 14 deletions(-) rename CONTRIBUTING.md => CONTRIBUTING.rst (91%) rename README.md => README.rst (86%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.rst similarity index 91% rename from CONTRIBUTING.md rename to CONTRIBUTING.rst index b0af761d53..a6e759eb18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.rst @@ -1,12 +1,15 @@ -# Contributions Guide +Contributions Guide +=================== We welcome contributions to the esp-idf project! -## How to Contribute +How to Contribute +----------------- Contributions to esp-idf - fixing bugs, adding features, adding documentation - are welcome. We accept contributions via [Github Pull Requests](https://help.github.com/articles/about-pull-requests/). -## Before Contributing +Before Contributing +------------------- Before sending us a Pull Request, please consider this list of points: @@ -24,7 +27,8 @@ Before sending us a Pull Request, please consider this list of points: * If you're unsure about any of these points, please open the Pull Request anyhow and then ask us for feedback. -## Pull Request Process +Pull Request Process +-------------------- After you open the Pull Request, there will probably be some discussion in the comments field of the request itself. @@ -32,6 +36,7 @@ Once the Pull Request is ready to merge, it will first be merged into our intern If this process passes, it will be merged onto the public github repository. -## Legal Part +Legal Part +---------- Before a contribution can be accepted, you will need to sign our [Contributor Agreement](docs/contributor-agreement.rst). You will be prompted for this automatically as part of the Pull Request process. diff --git a/README.md b/README.rst similarity index 86% rename from README.md rename to README.rst index ff645c3392..65d6167673 100644 --- a/README.md +++ b/README.rst @@ -1,6 +1,10 @@ -# Using Espressif IoT Development Framework with the ESP32 +Using Espressif IoT Development Framework with the ESP32 +======================================================== -# Setting Up ESP-IDF +|docs| + +Setting Up ESP-IDF +------------------ In the [docs](docs) directory you will find per-platform setup guides: @@ -8,23 +12,27 @@ In the [docs](docs) directory you will find per-platform setup guides: * [Mac OS Setup Guide](docs/macos-setup.rst) * [Linux Setup Guide](docs/linux-setup.rst) -# Finding A Project +Finding A Project +----------------- As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in the setup guide, esp-idf comes with some example projects in the [examples](examples) directory. Once you've found the project you want to work with, change to its directory and you can configure and build it: -# Configuring your project +Configuring your project +------------------------ `make menuconfig` -# Compiling your project +Compiling your project +---------------------- `make all` ... will compile app, bootloader and generate a partition table based on the config. -# Flashing your project +Flashing your project +--------------------- When `make all` finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this from make by running: @@ -34,7 +42,8 @@ This will flash the entire project (app, bootloader and partition table) to a ne You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it. -# Compiling & Flashing Just the App +Compiling & Flashing Just the App +--------------------------------- After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table: @@ -45,7 +54,8 @@ After the initial flash, you may just want to build and flash just your app, not (There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.) -# The Partition Table +The Partition Table +------------------- Once you've compiled your project, the "build" directory will contain a binary file with a name like "my_app.bin". This is an ESP32 image binary that can be loaded by the bootloader. @@ -62,7 +72,8 @@ In both cases the factory app is flashed at offset 0x10000. If you `make partiti For more details about partition tables and how to create custom variations, view the `docs/partition_tables.rst` file. -# Resources +Resources +--------- * The [docs directory of the esp-idf repository](docs) contains esp-idf documentation. From ec99397c3ea057903fba8cf12e1360df0727baae Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 23 Oct 2016 22:14:24 +0200 Subject: [PATCH 066/285] Initial Sphinx / ReadTheDocs.org configuration --- docs/Doxyfile | 12 ++ docs/Makefile | 177 +++++++++++++++++++++++++++ docs/_static/1.png | Bin 0 -> 11412 bytes docs/conf.py | 270 ++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 88 ++++++++++++++ docs/requirements.txt | 1 + 6 files changed, 548 insertions(+) create mode 100644 docs/Doxyfile create mode 100644 docs/Makefile create mode 100644 docs/_static/1.png create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/requirements.txt diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 0000000000..795bfa5e89 --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,12 @@ +PROJECT_NAME = "ESP32 Programming Guide" +XML_OUTPUT = xml +GENERATE_LATEX = NO +GENERATE_MAN = NO +GENERATE_RTF = NO +CASE_SENSE_NAMES = NO +INPUT = ../components/driver/include +RECURSIVE = YES +QUIET = YES +JAVADOC_AUTOBRIEF = YES +GENERATE_HTML = NO +GENERATE_XML = YES \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000..c04268ff1d --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/ReadtheDocsTemplate" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ReadtheDocsTemplate" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/_static/1.png b/docs/_static/1.png new file mode 100644 index 0000000000000000000000000000000000000000..4920892781f90c3db323f82033866c3f7116c073 GIT binary patch literal 11412 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
I-V-NX(N}q}D@(Dr+V{Hb>y4lN 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 U;S}7@z`(%Z>FVdQ&MBb@0LNnn&j0`b literal 0 HcmV?d00001 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000000..7c4614d458 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,270 @@ +# -*- coding: utf-8 -*- +# +# Read the Docs Template documentation build configuration file, created by +# sphinx-quickstart on Tue Aug 26 14:19:49 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# run doxygen +# +# for implementation on readthedocs.org check +# https://github.com/rtfd/readthedocs.org/issues/388 +# +from subprocess import call +call('doxygen') + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['breathe'] + +# Breathe extension variables +breathe_projects = { "esp32-idf": "xml/" } +breathe_default_project = "esp32-idf" + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Read the Docs Template' +copyright = u'2014, Read the Docs' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0' +# The full version, including alpha/beta/rc tags. +release = '1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'ReadtheDocsTemplatedoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'ReadtheDocsTemplate.tex', u'Read the Docs Template Documentation', + u'Read the Docs', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'readthedocstemplate', u'Read the Docs Template Documentation', + [u'Read the Docs'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'ReadtheDocsTemplate', u'Read the Docs Template Documentation', + u'Read the Docs', 'ReadtheDocsTemplate', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000..f74a3acae1 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,88 @@ +.. Read the Docs Template documentation master file + +Welcome to ESP32 Programming Guide +================================== + + +Example C functions +------------------- + +.. c:function:: esp_err_t esp_wifi_get_ap_list (uint16_t *number, wifi_ap_list_t *ap_list) + +.. c:function:: esp_err_t esp_wifi_set_protocol (wifi_interface_t ifx, uint8_t protocol_bitmap) + + +Example C function integration +------------------------------ + +.. doxygenfunction:: esp_wifi_init +.. doxygenfunction:: esp_wifi_set_config + +.. doxygenfunction:: gpio_isr_register +.. doxygenfunction:: ledc_timer_set + + +Example C enum integration +-------------------------- + +.. doxygenenum:: wifi_auth_mode_t + + +Example C struct integration +---------------------------- + +.. doxygenstruct:: wifi_scan_config_t + :members: + + +Contents: + +.. About - TBA + +.. toctree:: + :caption: Toolchain Setup + :maxdepth: 1 + + windows-setup + linux-setup + macos-setup + eclipse-setup + +.. API Reference - TBA + +.. Technical Reference - TBA + +.. toctree:: + :caption: Debugging + :maxdepth: 1 + + openocd + +.. Resources - TBA + +.. toctree:: + :caption: Contribute + :maxdepth: 1 + + contributing + contributor-agreement + +.. toctree:: + :caption: Copyrights and Licenses + :maxdepth: 1 + + COPYRIGHT + +.. toctree:: + :caption: Flapping Documents + :maxdepth: 1 + + partition-tables + build_system + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000000..188f51e62d --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +breathe \ No newline at end of file From 99fc7600888ae76252772ac544aed3a8399c1de6 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 23 Oct 2016 22:15:13 +0200 Subject: [PATCH 067/285] Link to file a folder up --- docs/contributing.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/contributing.rst diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000000..3bdd7dc21a --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1 @@ +.. include:: ../CONTRIBUTING.rst \ No newline at end of file From cb70ac831f3430175cbb8f230e48ce2c8b164f28 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 23 Oct 2016 22:25:28 +0200 Subject: [PATCH 068/285] Increased buikd coverage of DoxyGen --- docs/Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index 795bfa5e89..23f6f1a2d7 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -4,7 +4,7 @@ GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO -INPUT = ../components/driver/include +INPUT = ../components RECURSIVE = YES QUIET = YES JAVADOC_AUTOBRIEF = YES From 6435a71de2b9b9e5a3c731f630cf0dc8a33953ac Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 23 Oct 2016 22:31:15 +0200 Subject: [PATCH 069/285] Reduced the build coverage by DoxyGen --- docs/Doxyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index 23f6f1a2d7..795bfa5e89 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -4,7 +4,7 @@ GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO -INPUT = ../components +INPUT = ../components/driver/include RECURSIVE = YES QUIET = YES JAVADOC_AUTOBRIEF = YES From 4900c63071a88883b327aedc754bcae0bcc6fd62 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 23 Oct 2016 22:59:53 +0200 Subject: [PATCH 070/285] Added ReadTheDocs.org badges --- README.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 65d6167673..9711e22946 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,9 @@ In the [docs](docs) directory you will find per-platform setup guides: Finding A Project ----------------- -As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in the setup guide, esp-idf comes with some example projects in the [examples](examples) directory. +As well as the esp-idf-template_ project mentioned in the setup guide, esp-idf comes with some example projects in the [examples](examples) directory. + +.. _esp-idf-template: https://github.com/espressif/esp-idf-template Once you've found the project you want to work with, change to its directory and you can configure and build it: @@ -82,3 +84,9 @@ Resources * [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one. * If you're interested in contributing to esp-idf, please check the [CONTRIBUTING.md](CONTRIBUTING.md) file. + + +.. |docs| image:: https://readthedocs.org/projects/docs/badge/?version=latest + :alt: Documentation Status + :scale: 100% + :target: http://esp-idf.readthedocs.io/en/latest/?badge=latest \ No newline at end of file From 6ce1af58984751a3dafc3d5111e8bbedffdf7aa3 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Mon, 24 Oct 2016 20:43:13 +0200 Subject: [PATCH 071/285] Set up of theme for local builds --- docs/conf.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 7c4614d458..5ba76d7f25 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,14 +20,16 @@ import os # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) -# run doxygen +# -- Run DoxyGen to prepare XML for Sphinx--------------------------------- +# ref. https://github.com/rtfd/readthedocs.org/issues/388 # -# for implementation on readthedocs.org check -# https://github.com/rtfd/readthedocs.org/issues/388 +# added by krzychb, 24-Oct-2016 # + from subprocess import call call('doxygen') + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -268,3 +270,18 @@ texinfo_documents = [ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False + +# -- Use sphinx_rtd_theme for local builds -------------------------------- +# ref. https://github.com/snide/sphinx_rtd_theme#using-this-theme-locally-then-building-on-read-the-docs +# +# added by krzychb, 24-Oct-2016 +# +# on_rtd is whether we are on readthedocs.org +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +# otherwise, readthedocs.org uses their theme by default, so no need to specify it From 8ae97a285551a07046e7b80a5fb4de157fb36faf Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Mon, 24 Oct 2016 20:44:57 +0200 Subject: [PATCH 072/285] Initial list of Wi-Fi API --- docs/COPYRIGHT.rst | 6 ++- docs/Doxyfile | 4 +- docs/api/esp_wifi.rst | 59 ++++++++++++++++++++++++ docs/api/example.rst | 41 +++++++++++++++++ docs/build_system.rst | 2 +- docs/contributor-agreement.rst | 2 +- docs/eclipse-setup.rst | 3 ++ docs/index.rst | 82 ++++++++++++++-------------------- docs/linux-setup.rst | 3 ++ docs/macos-setup.rst | 3 ++ docs/openocd.rst | 3 ++ docs/partition-tables.rst | 5 ++- docs/windows-setup.rst | 7 ++- 13 files changed, 162 insertions(+), 58 deletions(-) create mode 100644 docs/api/esp_wifi.rst create mode 100644 docs/api/example.rst diff --git a/docs/COPYRIGHT.rst b/docs/COPYRIGHT.rst index da5f5b204a..67b3d9bf1c 100644 --- a/docs/COPYRIGHT.rst +++ b/docs/COPYRIGHT.rst @@ -1,3 +1,6 @@ +Copyrights and Licenses +*********************** + Software Copyrights =================== @@ -87,8 +90,7 @@ developments under license policy of following terms. Copyright (C) 2011, ChaN, all right reserved. * The TJpgDec module is a free software and there is NO WARRANTY. -* No restriction on use. You can use, modify and redistribute it for -personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +* No restriction on use. You can use, modify and redistribute it for personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. * Redistributions of source code must retain the above copyright notice. diff --git a/docs/Doxyfile b/docs/Doxyfile index 795bfa5e89..9c8fb81df8 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -4,9 +4,9 @@ GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO -INPUT = ../components/driver/include +INPUT = ../components/esp32/include/esp_wifi.h RECURSIVE = YES QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO -GENERATE_XML = YES \ No newline at end of file +GENERATE_XML = YES diff --git a/docs/api/esp_wifi.rst b/docs/api/esp_wifi.rst new file mode 100644 index 0000000000..48b4db204a --- /dev/null +++ b/docs/api/esp_wifi.rst @@ -0,0 +1,59 @@ +Wi-Fi API +========= + +Macros +------ + +.. doxygendefine:: WIFI_INIT_CONFIG_DEFAULT + + +Typedefs +-------- + +.. doxygentypedef:: wifi_promiscuous_cb_t +.. doxygentypedef:: wifi_rxcb_t +.. doxygentypedef:: esp_vendor_ie_cb_t + + +Functions +--------- + +.. doxygenfunction:: esp_wifi_init +.. doxygenfunction:: esp_wifi_deinit +.. doxygenfunction:: esp_wifi_set_mode +.. doxygenfunction:: esp_wifi_get_mode +.. doxygenfunction:: esp_wifi_start +.. doxygenfunction:: esp_wifi_stop +.. doxygenfunction:: esp_wifi_connect +.. doxygenfunction:: esp_wifi_disconnect +.. doxygenfunction:: esp_wifi_clear_fast_connect +.. doxygenfunction:: esp_wifi_kick_station +.. doxygenfunction:: esp_wifi_scan_start +.. doxygenfunction:: esp_wifi_scan_stop +.. doxygenfunction:: esp_wifi_get_ap_num +.. doxygenfunction:: esp_wifi_get_ap_list +.. doxygenfunction:: esp_wifi_set_ps +.. doxygenfunction:: esp_wifi_get_ps +.. doxygenfunction:: esp_wifi_set_protocol +.. doxygenfunction:: esp_wifi_get_protocol +.. doxygenfunction:: esp_wifi_set_bandwidth +.. doxygenfunction:: esp_wifi_get_bandwidth +.. doxygenfunction:: esp_wifi_set_channel +.. doxygenfunction:: esp_wifi_get_channel +.. doxygenfunction:: esp_wifi_set_country +.. doxygenfunction:: esp_wifi_get_country +.. doxygenfunction:: esp_wifi_set_mac +.. doxygenfunction:: esp_wifi_get_mac +.. doxygenfunction:: esp_wifi_set_promiscuous_rx_cb +.. doxygenfunction:: esp_wifi_set_promiscuous +.. doxygenfunction:: esp_wifi_get_promiscuous +.. doxygenfunction:: esp_wifi_set_config +.. doxygenfunction:: esp_wifi_get_config +.. doxygenfunction:: esp_wifi_get_station_list +.. doxygenfunction:: esp_wifi_free_station_list +.. doxygenfunction:: esp_wifi_set_storage +.. doxygenfunction:: esp_wifi_reg_rxcb +.. doxygenfunction:: esp_wifi_set_auto_connect +.. doxygenfunction:: esp_wifi_get_auto_connect +.. doxygenfunction:: esp_wifi_set_vendor_ie +.. doxygenfunction:: esp_wifi_set_vendor_ie_cb diff --git a/docs/api/example.rst b/docs/api/example.rst new file mode 100644 index 0000000000..88ecb4601d --- /dev/null +++ b/docs/api/example.rst @@ -0,0 +1,41 @@ +Example Visualizations +====================== + +Function prototpe +----------------- + +.. c:function:: esp_err_t esp_wifi_get_ap_list (uint16_t *number, wifi_ap_list_t *ap_list) +.. c:function:: esp_err_t esp_wifi_set_protocol (wifi_interface_t ifx, uint8_t protocol_bitmap) + + +Function definition +------------------- + +Wi-Fi +^^^^^ +.. doxygenfunction:: esp_wifi_init +.. doxygenfunction:: esp_wifi_set_config + +GPIO +^^^^ +.. doxygenfunction:: gpio_isr_register + +Led Control +^^^^^^^^^^^ + +.. doxygenfunction:: ledc_timer_set + + +Enum definition +--------------- + +.. doxygenenum:: wifi_auth_mode_t + + +Struct definition +----------------- + +.. doxygenstruct:: wifi_scan_config_t + :members: + + diff --git a/docs/build_system.rst b/docs/build_system.rst index 4df65b1b5c..34db487e0a 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -1,5 +1,5 @@ Build System ------------- +************ This document explains the Espressif IoT Development Framework build system and the concept of "components" diff --git a/docs/contributor-agreement.rst b/docs/contributor-agreement.rst index 7c194e7728..de294740c4 100644 --- a/docs/contributor-agreement.rst +++ b/docs/contributor-agreement.rst @@ -1,5 +1,5 @@ Contributor Agreement ---------------------- +===================== Individual Contributor Non-Exclusive License Agreement ------------------------------------------------------ diff --git a/docs/eclipse-setup.rst b/docs/eclipse-setup.rst index 21d83a7f06..32d60a17a0 100644 --- a/docs/eclipse-setup.rst +++ b/docs/eclipse-setup.rst @@ -1,3 +1,6 @@ +Build and Falsh with Eclipse IDE +******************************** + Installing Eclipse IDE ====================== diff --git a/docs/index.rst b/docs/index.rst index f74a3acae1..30e981e8ef 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,54 +1,41 @@ .. Read the Docs Template documentation master file -Welcome to ESP32 Programming Guide -================================== - - -Example C functions -------------------- - -.. c:function:: esp_err_t esp_wifi_get_ap_list (uint16_t *number, wifi_ap_list_t *ap_list) - -.. c:function:: esp_err_t esp_wifi_set_protocol (wifi_interface_t ifx, uint8_t protocol_bitmap) - - -Example C function integration ------------------------------- - -.. doxygenfunction:: esp_wifi_init -.. doxygenfunction:: esp_wifi_set_config - -.. doxygenfunction:: gpio_isr_register -.. doxygenfunction:: ledc_timer_set - - -Example C enum integration --------------------------- - -.. doxygenenum:: wifi_auth_mode_t - - -Example C struct integration ----------------------------- - -.. doxygenstruct:: wifi_scan_config_t - :members: - +ESP32 Programming Guide +======================= Contents: -.. About - TBA - .. toctree:: - :caption: Toolchain Setup + :caption: Setup Toolchain :maxdepth: 1 - windows-setup - linux-setup - macos-setup - eclipse-setup + Windows + Linux + Mac OS -.. API Reference - TBA +.. Configure - TBA + +.. Connect - TBA + +.. toctree:: + :caption: Build and Flash + :maxdepth: 1 + + Eclipse IDE + +.. toctree:: + :caption: Tweak + :maxdepth: 1 + + partition-tables + build_system + +.. toctree:: + :caption: API Reference + :maxdepth: 1 + + Wi-Fi + api/example .. Technical Reference - TBA @@ -68,17 +55,14 @@ Contents: contributor-agreement .. toctree:: - :caption: Copyrights and Licenses + :caption: Legal :maxdepth: 1 COPYRIGHT -.. toctree:: - :caption: Flapping Documents - :maxdepth: 1 - - partition-tables - build_system +.. About - TBA + + Indices and tables diff --git a/docs/linux-setup.rst b/docs/linux-setup.rst index 13e1b3a9c0..cf5e78b63d 100644 --- a/docs/linux-setup.rst +++ b/docs/linux-setup.rst @@ -1,3 +1,6 @@ +Set up of Toolchain for Linux +***************************** + Step 0: Prerequisites ===================== diff --git a/docs/macos-setup.rst b/docs/macos-setup.rst index 8178a17ada..53c6fe54c8 100644 --- a/docs/macos-setup.rst +++ b/docs/macos-setup.rst @@ -1,3 +1,6 @@ +Set up of Toolchain for Mac OS +****************************** + Step 0: Prerequisites ===================== diff --git a/docs/openocd.rst b/docs/openocd.rst index 57ee93db4a..6a6f50e3f4 100644 --- a/docs/openocd.rst +++ b/docs/openocd.rst @@ -1,3 +1,6 @@ +Debugging +========= + OpenOCD setup for ESP32 ----------------------- diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index e0a39126b5..88597532d2 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -1,5 +1,8 @@ Partition Tables ----------------- +================ + +Overview +-------- A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash. diff --git a/docs/windows-setup.rst b/docs/windows-setup.rst index baea1ac40b..a425f5b3a0 100644 --- a/docs/windows-setup.rst +++ b/docs/windows-setup.rst @@ -1,5 +1,8 @@ -Step 1: Toolchain for Windows: Quick Steps -================================== +Set up of Toolchain for Windows +******************************* + +Step 1: Quick Steps +=================== Windows doesn't have a built-in "make" environment, so as well as installing the toolchain you will need a GNU-compatible environment. We use the MSYS2_ environment to provide. You don't need to use this environment all the time (you can use Eclipse_ or some other front-end), but it runs behind the scenes. From b6a463e94f5886bc953515e8b9a45b7c13a514a2 Mon Sep 17 00:00:00 2001 From: krzychb Date: Tue, 25 Oct 2016 12:09:05 +0200 Subject: [PATCH 073/285] Draft of GPIO API included --- docs/Doxyfile | 25 +++++++++--------- docs/api/gpio.rst | 67 +++++++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + 3 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 docs/api/gpio.rst diff --git a/docs/Doxyfile b/docs/Doxyfile index 9c8fb81df8..8328d44ca3 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,12 +1,13 @@ -PROJECT_NAME = "ESP32 Programming Guide" -XML_OUTPUT = xml -GENERATE_LATEX = NO -GENERATE_MAN = NO -GENERATE_RTF = NO -CASE_SENSE_NAMES = NO -INPUT = ../components/esp32/include/esp_wifi.h -RECURSIVE = YES -QUIET = YES -JAVADOC_AUTOBRIEF = YES -GENERATE_HTML = NO -GENERATE_XML = YES +PROJECT_NAME = "ESP32 Programming Guide" +XML_OUTPUT = xml +GENERATE_LATEX = NO +GENERATE_MAN = NO +GENERATE_RTF = NO +CASE_SENSE_NAMES = NO +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver/gpio.h ../components/esp32/include/rom/gpio.h +RECURSIVE = YES +QUIET = YES +JAVADOC_AUTOBRIEF = YES +GENERATE_HTML = NO +GENERATE_XML = YES +WARN_LOGFILE = "DoxyGenWarningLog.txt" \ No newline at end of file diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst new file mode 100644 index 0000000000..11b0e4a463 --- /dev/null +++ b/docs/api/gpio.rst @@ -0,0 +1,67 @@ +GPIO API +======== + +Functions +--------- + +.. doxygenfunction:: gpio_config +.. doxygenfunction:: gpio_set_intr_type +.. doxygenfunction:: gpio_intr_enable +.. doxygenfunction:: gpio_intr_disable +.. doxygenfunction:: gpio_set_level +.. doxygenfunction:: gpio_get_level +.. doxygenfunction:: gpio_set_direction +.. doxygenfunction:: gpio_set_pull_mode +.. doxygenfunction:: gpio_wakeup_enable +.. doxygenfunction:: gpio_wakeup_disable +.. doxygenfunction:: gpio_isr_register + +*Example code:* + +Configuration of GPIO as an output + +.. code-block:: c + + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt + io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode + io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19 + io_conf.pull_down_en = 0; //disable pull-down mode + io_conf.pull_up_en = 0; //disable pull-up mode + gpio_config(&io_conf); //configure GPIO with the given settings + +Configuration of GPIO as an input + +.. code-block:: c + + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt + io_conf.mode = GPIO_MODE_INPUT; //set as input + io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5 + io_conf.pull_down_en = 0; //disable pull-down mode + io_conf.pull_up_en = 1; //enable pull-up mode + gpio_config(&io_conf); //configure GPIO with the given settings + + +Low level ROM GPIO functions + +.. doxygenfunction:: gpio_init +.. doxygenfunction:: gpio_output_set +.. doxygenfunction:: gpio_output_set_high +.. doxygenfunction:: gpio_input_get +.. doxygenfunction:: gpio_input_get_high +.. doxygenfunction:: gpio_intr_handler_register +.. doxygenfunction:: gpio_intr_pending +.. doxygenfunction:: gpio_intr_pending_high +.. doxygenfunction:: gpio_intr_ack +.. doxygenfunction:: gpio_intr_ack_high +.. doxygenfunction:: gpio_pin_wakeup_enable +.. doxygenfunction:: gpio_pin_wakeup_disable +.. doxygenfunction:: gpio_matrix_in +.. doxygenfunction:: gpio_matrix_out +.. doxygenfunction:: gpio_pad_select_gpio +.. doxygenfunction:: gpio_pad_set_drv +.. doxygenfunction:: gpio_pad_pullup +.. doxygenfunction:: gpio_pad_pulldown +.. doxygenfunction:: gpio_pad_unhold +.. doxygenfunction:: gpio_pad_hold diff --git a/docs/index.rst b/docs/index.rst index 30e981e8ef..26b906c366 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,7 @@ Contents: :maxdepth: 1 Wi-Fi + GPIO api/example .. Technical Reference - TBA From 3584fcfc7cf3a3211c476cb2d02b66360d649bce Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Tue, 25 Oct 2016 21:28:21 +0200 Subject: [PATCH 074/285] Make section --- docs/index.rst | 19 ++++++------- docs/make-project.rst | 63 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 docs/make-project.rst diff --git a/docs/index.rst b/docs/index.rst index 26b906c366..84f37b5600 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,14 +21,16 @@ Contents: :caption: Build and Flash :maxdepth: 1 + Make Eclipse IDE .. toctree:: - :caption: Tweak + :caption: Want More? :maxdepth: 1 partition-tables build_system + openocd .. toctree:: :caption: API Reference @@ -38,13 +40,10 @@ Contents: GPIO api/example -.. Technical Reference - TBA - .. toctree:: - :caption: Debugging - :maxdepth: 1 + :caption: Technical Reference - openocd + Technical Reference .. Resources - TBA @@ -63,11 +62,9 @@ Contents: .. About - TBA - - - -Indices and tables -================== + +Indices +======= * :ref:`genindex` * :ref:`search` diff --git a/docs/make-project.rst b/docs/make-project.rst new file mode 100644 index 0000000000..e72bb81dd0 --- /dev/null +++ b/docs/make-project.rst @@ -0,0 +1,63 @@ +Build and Flash with Make +========================= + +Finding a project +----------------- + +As well as the `esp-idf-template `_ project mentioned in the setup guide, esp-idf comes with some example projects on github in the `examples `_ directory. + +Once you've found the project you want to work with, change to its directory and you can configure and build it: + +Configuring your project +------------------------ + +`make menuconfig` + +Compiling your project +---------------------- + +`make all` + +... will compile app, bootloader and generate a partition table based on the config. + +Flashing your project +--------------------- + +When `make all` finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this from make by running: + +`make flash` + +This will flash the entire project (app, bootloader and partition table) to a new chip. The settings for serial port flashing can be configured with `make menuconfig`. + +You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it. + +Compiling & Flashing Just the App +--------------------------------- + +After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table: + +* `make app` - build just the app. +* `make app-flash` - flash just the app. + +`make app-flash` will automatically rebuild the app if it needs it. + +(There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.) + +The Partition Table +------------------- + +Once you've compiled your project, the "build" directory will contain a binary file with a name like "my_app.bin". This is an ESP32 image binary that can be loaded by the bootloader. + +A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash. + +Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded. + +The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables: + +* "Single factory app, no OTA" +* "Factory app, two OTA definitions" + +In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table. + +For more details about :doc:`partition tables ` and how to create custom variations, view the :doc:`documentation `. + From 591b7caa9514ab7d2a2c03bf8b42bec490eea86f Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Tue, 25 Oct 2016 21:28:42 +0200 Subject: [PATCH 075/285] Back to .md format --- CONTRIBUTING.rst | 6 +++--- README.rst => README.md | 44 +++++++++++++---------------------------- 2 files changed, 17 insertions(+), 33 deletions(-) rename README.rst => README.md (72%) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index a6e759eb18..ed1c0b92d8 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -6,7 +6,7 @@ We welcome contributions to the esp-idf project! How to Contribute ----------------- -Contributions to esp-idf - fixing bugs, adding features, adding documentation - are welcome. We accept contributions via [Github Pull Requests](https://help.github.com/articles/about-pull-requests/). +Contributions to esp-idf - fixing bugs, adding features, adding documentation - are welcome. We accept contributions via `Github Pull Requests `_. Before Contributing ------------------- @@ -23,7 +23,7 @@ Before sending us a Pull Request, please consider this list of points: * Are comments and documentation written in clear English, with no spelling or grammar errors? -* If the contribution contains multiple commits, are they grouped together into logical changes (one major change per pull request)? Are any commits with names like "fixed typo" [squashed into previous commits](http://eli.thegreenplace.net/2014/02/19/squashing-github-pull-requests-into-a-single-commit/)? +* If the contribution contains multiple commits, are they grouped together into logical changes (one major change per pull request)? Are any commits with names like "fixed typo" `squashed into previous commits `_? * If you're unsure about any of these points, please open the Pull Request anyhow and then ask us for feedback. @@ -39,4 +39,4 @@ If this process passes, it will be merged onto the public github repository. Legal Part ---------- -Before a contribution can be accepted, you will need to sign our [Contributor Agreement](docs/contributor-agreement.rst). You will be prompted for this automatically as part of the Pull Request process. +Before a contribution can be accepted, you will need to sign our :doc:`Contributor Agreement `. You will be prompted for this automatically as part of the Pull Request process. diff --git a/README.rst b/README.md similarity index 72% rename from README.rst rename to README.md index 9711e22946..c01e314f11 100644 --- a/README.rst +++ b/README.md @@ -1,10 +1,8 @@ -Using Espressif IoT Development Framework with the ESP32 -======================================================== +# Using Espressif IoT Development Framework with the ESP32 -|docs| +[![alt text](https://readthedocs.org/projects/docs/badge/?version=latest "Documentation Status")](http://esp-idf.readthedocs.io/en/latest/?badge=latest) -Setting Up ESP-IDF ------------------- +# Setting Up ESP-IDF In the [docs](docs) directory you will find per-platform setup guides: @@ -12,29 +10,23 @@ In the [docs](docs) directory you will find per-platform setup guides: * [Mac OS Setup Guide](docs/macos-setup.rst) * [Linux Setup Guide](docs/linux-setup.rst) -Finding A Project ------------------ +# Finding A Project -As well as the esp-idf-template_ project mentioned in the setup guide, esp-idf comes with some example projects in the [examples](examples) directory. - -.. _esp-idf-template: https://github.com/espressif/esp-idf-template +As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in the setup guide, esp-idf comes with some example projects in the [examples](examples) directory. Once you've found the project you want to work with, change to its directory and you can configure and build it: -Configuring your project ------------------------- +# Configuring your project `make menuconfig` -Compiling your project ----------------------- +# Compiling your project `make all` ... will compile app, bootloader and generate a partition table based on the config. -Flashing your project ---------------------- +# Flashing your project When `make all` finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this from make by running: @@ -44,8 +36,7 @@ This will flash the entire project (app, bootloader and partition table) to a ne You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it. -Compiling & Flashing Just the App ---------------------------------- +# Compiling & Flashing Just the App After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table: @@ -56,8 +47,7 @@ After the initial flash, you may just want to build and flash just your app, not (There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.) -The Partition Table -------------------- +# The Partition Table Once you've compiled your project, the "build" directory will contain a binary file with a name like "my_app.bin". This is an ESP32 image binary that can be loaded by the bootloader. @@ -72,21 +62,15 @@ The simplest way to use the partition table is to `make menuconfig` and choose o In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table. -For more details about partition tables and how to create custom variations, view the `docs/partition_tables.rst` file. +For more details about partition tables and how to create custom variations, view the `docs/partition-tables.rst` file. -Resources ---------- +# Resources -* The [docs directory of the esp-idf repository](docs) contains esp-idf documentation. +* The [docs directory of the esp-idf repository](docs) contains source of [esp-idf](http://esp-idf.readthedocs.io/) documentation. * The [esp32.com forum](http://esp32.com/) is a place to ask questions and find community resources. * [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one. -* If you're interested in contributing to esp-idf, please check the [CONTRIBUTING.md](CONTRIBUTING.md) file. +* If you're interested in contributing to esp-idf, please check the [Contributions Guide](http://esp-idf.readthedocs.io/en/latest/contributing.html>). - -.. |docs| image:: https://readthedocs.org/projects/docs/badge/?version=latest - :alt: Documentation Status - :scale: 100% - :target: http://esp-idf.readthedocs.io/en/latest/?badge=latest \ No newline at end of file From adae42fd85381071a2564940f398e2fdf22fdc97 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 26 Oct 2016 21:08:36 +0200 Subject: [PATCH 076/285] API template - reflected template in other DRAFT API dcouments - DRAFT of Bluetooth API - link fixes in CONTRIBUTING.rst --- CONTRIBUTING.rst | 6 +++-- docs/Doxyfile | 26 +++++++++--------- docs/api/bt.rst | 31 ++++++++++++++++++++++ docs/api/esp_wifi.rst | 15 +++++++++++ docs/api/example.rst | 41 ----------------------------- docs/api/gpio.rst | 28 +++++++++++++++----- docs/api/template.rst | 61 +++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 7 +++-- 8 files changed, 149 insertions(+), 66 deletions(-) create mode 100644 docs/api/bt.rst delete mode 100644 docs/api/example.rst create mode 100644 docs/api/template.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ed1c0b92d8..3bf43f6dbe 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -19,7 +19,7 @@ Before sending us a Pull Request, please consider this list of points: * Is the code adequately commented for people to understand how it is structured? -* Is there documentation or examples that go with code contributions? [There are additional suggestions for writing good examples in the examples README](examples/README.md). +* Is there documentation or examples that go with code contributions? `There are additional suggestions for writing good examples in the examples README `_. * Are comments and documentation written in clear English, with no spelling or grammar errors? @@ -39,4 +39,6 @@ If this process passes, it will be merged onto the public github repository. Legal Part ---------- -Before a contribution can be accepted, you will need to sign our :doc:`Contributor Agreement `. You will be prompted for this automatically as part of the Pull Request process. +Before a contribution can be accepted, you will need to sign our `Contributor Agreement `_. You will be prompted for this automatically as part of the Pull Request process. + + diff --git a/docs/Doxyfile b/docs/Doxyfile index 8328d44ca3..f905de74ce 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,13 +1,13 @@ -PROJECT_NAME = "ESP32 Programming Guide" -XML_OUTPUT = xml -GENERATE_LATEX = NO -GENERATE_MAN = NO -GENERATE_RTF = NO -CASE_SENSE_NAMES = NO -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver/gpio.h ../components/esp32/include/rom/gpio.h -RECURSIVE = YES -QUIET = YES -JAVADOC_AUTOBRIEF = YES -GENERATE_HTML = NO -GENERATE_XML = YES -WARN_LOGFILE = "DoxyGenWarningLog.txt" \ No newline at end of file +PROJECT_NAME = "ESP32 Programming Guide" +XML_OUTPUT = xml +GENERATE_LATEX = NO +GENERATE_MAN = NO +GENERATE_RTF = NO +CASE_SENSE_NAMES = NO +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver/gpio.h ../components/esp32/include/rom/gpio.h ../components/bt/include/bt.h +RECURSIVE = YES +QUIET = YES +JAVADOC_AUTOBRIEF = YES +GENERATE_HTML = NO +GENERATE_XML = YES +WARN_LOGFILE = "DoxyGenWarningLog.txt" diff --git a/docs/api/bt.rst b/docs/api/bt.rst new file mode 100644 index 0000000000..16f30dc4e6 --- /dev/null +++ b/docs/api/bt.rst @@ -0,0 +1,31 @@ +Bluetooth API +============= + +Overview +-------- + +`Instructions `_ + +Application Example +------------------- + +`Instructions `_ + +Reference +--------- + +`Instructions `_ + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: vhci_host_callback + +Functions +^^^^^^^^^ + +.. doxygenfunction:: API_vhci_host_check_send_available +.. doxygenfunction:: API_vhci_host_register_callback +.. doxygenfunction:: API_vhci_host_send_packet +.. doxygenfunction:: bt_controller_init + diff --git a/docs/api/esp_wifi.rst b/docs/api/esp_wifi.rst index 48b4db204a..e4ec59fc82 100644 --- a/docs/api/esp_wifi.rst +++ b/docs/api/esp_wifi.rst @@ -1,6 +1,21 @@ Wi-Fi API ========= +Overview +-------- + +`Instructions `_ + +Application Example +------------------- + +`Instructions `_ + +Reference +--------- + +`Instructions `_ + Macros ------ diff --git a/docs/api/example.rst b/docs/api/example.rst deleted file mode 100644 index 88ecb4601d..0000000000 --- a/docs/api/example.rst +++ /dev/null @@ -1,41 +0,0 @@ -Example Visualizations -====================== - -Function prototpe ------------------ - -.. c:function:: esp_err_t esp_wifi_get_ap_list (uint16_t *number, wifi_ap_list_t *ap_list) -.. c:function:: esp_err_t esp_wifi_set_protocol (wifi_interface_t ifx, uint8_t protocol_bitmap) - - -Function definition -------------------- - -Wi-Fi -^^^^^ -.. doxygenfunction:: esp_wifi_init -.. doxygenfunction:: esp_wifi_set_config - -GPIO -^^^^ -.. doxygenfunction:: gpio_isr_register - -Led Control -^^^^^^^^^^^ - -.. doxygenfunction:: ledc_timer_set - - -Enum definition ---------------- - -.. doxygenenum:: wifi_auth_mode_t - - -Struct definition ------------------ - -.. doxygenstruct:: wifi_scan_config_t - :members: - - diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 11b0e4a463..c12c991ce4 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -1,9 +1,24 @@ GPIO API ======== -Functions +Overview +-------- + +`Instructions `_ + +Application Example +------------------- + +`Instructions `_ + +Reference --------- +`Instructions `_ + +Functions +^^^^^^^^^ + .. doxygenfunction:: gpio_config .. doxygenfunction:: gpio_set_intr_type .. doxygenfunction:: gpio_intr_enable @@ -16,9 +31,7 @@ Functions .. doxygenfunction:: gpio_wakeup_disable .. doxygenfunction:: gpio_isr_register -*Example code:* - -Configuration of GPIO as an output +*Example code:* Configuration of GPIO as an output .. code-block:: c @@ -30,7 +43,7 @@ Configuration of GPIO as an output io_conf.pull_up_en = 0; //disable pull-up mode gpio_config(&io_conf); //configure GPIO with the given settings -Configuration of GPIO as an input +*Example code:* Configuration of GPIO as an input .. code-block:: c @@ -43,7 +56,8 @@ Configuration of GPIO as an input gpio_config(&io_conf); //configure GPIO with the given settings -Low level ROM GPIO functions +ROM GPIO functions +^^^^^^^^^^^^^^^^^^ .. doxygenfunction:: gpio_init .. doxygenfunction:: gpio_output_set @@ -65,3 +79,5 @@ Low level ROM GPIO functions .. doxygenfunction:: gpio_pad_pulldown .. doxygenfunction:: gpio_pad_unhold .. doxygenfunction:: gpio_pad_hold + + diff --git a/docs/api/template.rst b/docs/api/template.rst new file mode 100644 index 0000000000..8b1dfd4c50 --- /dev/null +++ b/docs/api/template.rst @@ -0,0 +1,61 @@ +Template API +============= + +Overview +-------- + +INSTRUCTIONS: Provide overview where and how this API may be used. For large number of functions, break down description into groups. + + +Application Example +------------------- + +INSTRUCTIONS: Provide one or more pratical examples to demonstrate functionality of this API. + + +Reference +--------- + +INSTRUCTIONS: Provide list of API memebers divided into sections. Use coresponding **.. doxygen** directices, so member documentation is auto updated. + +* Data Structures **.. doxygenstruct** +* Macros **.. doxygendefine** +* Type Definitions **.. doxygentypedef** +* Enumerations **.. doxygenenum** +* Functions **.. doxygenfunction** +* Variables **.. doxygenvariable** + +Include code snippotes to ilustrate functionality of particular functions where applicable. Skip section hearder if empty. + + +Data Structures +^^^^^^^^^^^^^^^ + +.. Data Structures .. doxygenstruct + +Macros +^^^^^^ + +.. Macros .. doxygendefine + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. Type Definitions .. doxygentypedef + +Enumerations +^^^^^^^^^^^^ + +.. Enumerations .. doxygenenum + +Functions +^^^^^^^^^ + +.. Functions .. doxygenfunction + +Variables +^^^^^^^^^ + +.. Variables .. doxygenvariable + + diff --git a/docs/index.rst b/docs/index.rst index 84f37b5600..5c4d7025c6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,3 @@ -.. Read the Docs Template documentation master file - ESP32 Programming Guide ======================= @@ -25,7 +23,7 @@ Contents: Eclipse IDE .. toctree:: - :caption: Want More? + :caption: What Else? :maxdepth: 1 partition-tables @@ -37,8 +35,9 @@ Contents: :maxdepth: 1 Wi-Fi + Bluetooth GPIO - api/example + Template .. toctree:: :caption: Technical Reference From f05cd619f4bdbdfc2019b4e9f800b82ae6a13f3b Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 26 Oct 2016 22:17:58 +0200 Subject: [PATCH 077/285] Sample cleaning of markup --- components/driver/include/driver/gpio.h | 148 ++++++++++++------------ docs/api/gpio.rst | 7 ++ 2 files changed, 81 insertions(+), 74 deletions(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 9b47c88e69..001be2a39d 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -157,39 +157,39 @@ typedef enum { } gpio_num_t; typedef enum { - GPIO_INTR_DISABLE = 0, /* disable GPIO interrupt */ - GPIO_INTR_POSEDGE = 1, /* GPIO interrupt type : rising edge */ - GPIO_INTR_NEGEDGE = 2, /* GPIO interrupt type : falling edge */ - GPIO_INTR_ANYEDGE = 3, /* GPIO interrupt type : both rising and falling edge */ - GPIO_INTR_LOW_LEVEL = 4, /* GPIO interrupt type : input low level trigger */ - GPIO_INTR_HIGH_LEVEL = 5, /* GPIO interrupt type : input high level trigger */ + GPIO_INTR_DISABLE = 0, /*!< disable GPIO interrupt */ + GPIO_INTR_POSEDGE = 1, /*!< GPIO interrupt type : rising edge */ + GPIO_INTR_NEGEDGE = 2, /*!< GPIO interrupt type : falling edge */ + GPIO_INTR_ANYEDGE = 3, /*!< GPIO interrupt type : both rising and falling edge */ + GPIO_INTR_LOW_LEVEL = 4, /*!< GPIO interrupt type : input low level trigger */ + GPIO_INTR_HIGH_LEVEL = 5, /*!< GPIO interrupt type : input high level trigger */ GPIO_INTR_MAX, } gpio_int_type_t; typedef enum { - GPIO_MODE_INPUT = GPIO_MODE_DEF_INPUT, /* GPIO mode : input only */ - GPIO_MODE_OUTPUT = GPIO_MODE_DEF_OUTPUT, /* GPIO mode : output only mode */ - GPIO_MODE_OUTPUT_OD = ((GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)), /* GPIO mode : output only with open-drain mode */ - GPIO_MODE_INPUT_OUTPUT_OD = ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)), /* GPIO mode : output and input with open-drain mode*/ - GPIO_MODE_INPUT_OUTPUT = ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_OUTPUT)), /* GPIO mode : output and input mode */ + GPIO_MODE_INPUT = GPIO_MODE_DEF_INPUT, /*!< GPIO mode : input only */ + GPIO_MODE_OUTPUT = GPIO_MODE_DEF_OUTPUT, /*!< GPIO mode : output only mode */ + GPIO_MODE_OUTPUT_OD = ((GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)), /*!< GPIO mode : output only with open-drain mode */ + GPIO_MODE_INPUT_OUTPUT_OD = ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_OUTPUT)|(GPIO_MODE_DEF_OD)), /*!< GPIO mode : output and input with open-drain mode*/ + GPIO_MODE_INPUT_OUTPUT = ((GPIO_MODE_DEF_INPUT)|(GPIO_MODE_DEF_OUTPUT)), /*!< GPIO mode : output and input mode */ } gpio_mode_t; typedef enum { - GPIO_PULLUP_DISABLE = 0x0, /* disable GPIO pull-up resistor */ - GPIO_PULLUP_ENABLE = 0x1, /* enable GPIO pull-up resistor */ + GPIO_PULLUP_DISABLE = 0x0, /*!< disable GPIO pull-up resistor */ + GPIO_PULLUP_ENABLE = 0x1, /*!< enable GPIO pull-up resistor */ } gpio_pullup_t; typedef enum { - GPIO_PULLDOWN_DISABLE = 0x0, /* disable GPIO pull-down resistor */ - GPIO_PULLDOWN_ENABLE = 0x1, /* enable GPIO pull-down resistor */ + GPIO_PULLDOWN_DISABLE = 0x0, /*!< disable GPIO pull-down resistor */ + GPIO_PULLDOWN_ENABLE = 0x1, /*!< enable GPIO pull-down resistor */ } gpio_pulldown_t; typedef struct { - uint64_t pin_bit_mask; /* GPIO pin: set with bit mask, each bit maps to a GPIO */ - gpio_mode_t mode; /* GPIO mode: set input/output mode */ - gpio_pullup_t pull_up_en; /* GPIO pull-up */ - gpio_pulldown_t pull_down_en; /* GPIO pull-down */ - gpio_int_type_t intr_type; /* GPIO interrupt type */ + uint64_t pin_bit_mask; /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */ + gpio_mode_t mode; /*!< GPIO mode: set input/output mode */ + gpio_pullup_t pull_up_en; /*!< GPIO pull-up */ + gpio_pulldown_t pull_down_en; /*!< GPIO pull-down */ + gpio_int_type_t intr_type; /*!< GPIO interrupt type */ } gpio_config_t; typedef enum { @@ -199,10 +199,10 @@ typedef enum { } gpio_level_t; typedef enum { - GPIO_PULLUP_ONLY, /* Pad pull up */ - GPIO_PULLDOWN_ONLY, /* Pad pull down */ - GPIO_PULLUP_PULLDOWN, /* Pad pull up + pull down*/ - GPIO_FLOATING, /* Pad floating */ + GPIO_PULLUP_ONLY, /*!< Pad pull up */ + GPIO_PULLDOWN_ONLY, /*!< Pad pull down */ + GPIO_PULLUP_PULLDOWN, /*!< Pad pull up + pull down*/ + GPIO_FLOATING, /*!< Pad floating */ } gpio_pull_mode_t; typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num); @@ -224,20 +224,20 @@ typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num); */ /** - * @brief GPIO common configuration + * @brief GPIO common configuration * * Use this Function ,Configure GPIO's Mode,pull-up,PullDown,IntrType * - * @parameter[in] pGPIOConfig - * pGPIOConfig.pin_bit_mask : Configure GPIO pins bits,set this parameter with bit mask. + * @param[in] pGPIOConfig + * pGPIOConfig.pin_bit_mask : Configure GPIO pins bits,set this parameter with bit mask. * If you want to configure GPIO34 and GPIO16, pin_bit_mask=GPIO_Pin_16|GPIO_Pin_34; - * pGPIOConfig.mode : Configure GPIO mode,such as output ,input... - * pGPIOConfig.pull_up_en : Enable or Disable pull-up - * pGPIOConfig.pull_down_en : Enable or Disable pull-down - * pGPIOConfig.intr_type : Configure GPIO interrupt trigger type - * @return ESP_OK: success ; - * ESP_ERR_INVALID_ARG: parameter error - * ESP_FAIL : GPIO error + * pGPIOConfig.mode : Configure GPIO mode,such as output ,input... + * pGPIOConfig.pull_up_en : Enable or Disable pull-up + * pGPIOConfig.pull_down_en : Enable or Disable pull-down + * pGPIOConfig.intr_type : Configure GPIO interrupt trigger type + * @return ESP_OK: success ; + * ESP_ERR_INVALID_ARG: parameter error + * ESP_FAIL : GPIO error * */ esp_err_t gpio_config(gpio_config_t *pGPIOConfig); @@ -246,12 +246,12 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig); /** * @brief GPIO set interrupt trigger type * - * @parameter[in] gpio_num : GPIO number. + * @param[in] gpio_num : GPIO number. * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); - * @parameter[in] intr_type: interrupt type, select from gpio_int_type_t + * @param[in] intr_type: interrupt type, select from gpio_int_type_t * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG: parameter error + * @return ESP_OK : success + * ESP_ERR_INVALID_ARG: parameter error * */ esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type); @@ -259,11 +259,11 @@ esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type); /** * @brief enable GPIO module interrupt signal * - * @parameter[in] gpio_num : GPIO number. + * @param[in] gpio_num : GPIO number. * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG: parameter error + * @return ESP_OK : success + * ESP_ERR_INVALID_ARG: parameter error * */ esp_err_t gpio_intr_enable(gpio_num_t gpio_num); @@ -271,78 +271,78 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num); /** * @brief disable GPIO module interrupt signal * - * @parameter[in] gpio_num : GPIO number. + * @param[in] gpio_num : GPIO number. * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG: parameter error + * @return ESP_OK : success + * ESP_ERR_INVALID_ARG: parameter error * */ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); /** - * @brief GPIO set output level + * @brief GPIO set output level * - * @parameter[in] gpio_num : GPIO number. + * @param[in] gpio_num : GPIO number. * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); - * @parameter[in] level : Output level. 0: low ; 1: high + * @param[in] level : Output level. 0: low ; 1: high * - * @return ESP_OK : success - * ESP_FAIL : GPIO error + * @return ESP_OK : success + * ESP_FAIL : GPIO error * */ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); /** - * @brief GPIO get input level + * @brief GPIO get input level * - * @parameter[in] gpio_num : GPIO number. + * @param[in] gpio_num : GPIO number. * If you want to get level of pin GPIO16, gpio_num should be GPIO_NUM_16 (16); * - * @return 0 : the GPIO input level is 0 + * @return 0 : the GPIO input level is 0 * 1 : the GPIO input level is 1 * */ int gpio_get_level(gpio_num_t gpio_num); /** - * @brief GPIO set direction + * @brief GPIO set direction * * Configure GPIO direction,such as output_only,input_only,output_and_input * - * @parameter[in] gpio_num : Configure GPIO pins number,it should be GPIO number. + * @param[in] gpio_num : Configure GPIO pins number,it should be GPIO number. * If you want to set direction of GPIO16, gpio_num should be GPIO_NUM_16 (16); - * @parameter[in] mode : Configure GPIO direction,such as output_only,input_only,... + * @param[in] mode : Configure GPIO direction,such as output_only,input_only,... * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG : fail - * ESP_FAIL : GPIO error + * @return ESP_OK : success + * ESP_ERR_INVALID_ARG : fail + * ESP_FAIL : GPIO error * */ esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode); /** - * @brief GPIO set pull + * @brief GPIO set pull * * User this Function,configure GPIO pull mode,such as pull-up,pull-down * - * @parameter[in] gpio_num : Configure GPIO pins number,it should be GPIO number. + * @param[in] gpio_num : Configure GPIO pins number,it should be GPIO number. * If you want to set pull up or down mode for GPIO16,gpio_num should be GPIO_NUM_16 (16); - * @parameter[in] pull : Configure GPIO pull up/down mode,such as pullup_only,pulldown_only,pullup_and_pulldown,... + * @param[in] pull : Configure GPIO pull up/down mode,such as pullup_only,pulldown_only,pullup_and_pulldown,... * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG : fail - * ESP_FAIL : GPIO error + * @return ESP_OK : success + * ESP_ERR_INVALID_ARG : fail + * ESP_FAIL : GPIO error * */ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull); /** - * @brief enable GPIO wake-up function. + * @brief enable GPIO wake-up function. * - * @param gpio_num_t gpio_num : GPIO number. + * @param gpio_num : GPIO number. * - * @param gpio_int_type_t intr_type : only GPIO_INTR_LOLEVEL\GPIO_INTR_HILEVEL can be used + * @param intr_type : only GPIO_INTR_LOLEVEL\GPIO_INTR_HILEVEL can be used * * @return ESP_OK: success * ESP_ERR_INVALID_ARG: parameter error @@ -350,9 +350,9 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull); esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type); /** - * @brief disable GPIO wake-up function. + * @brief disable GPIO wake-up function. * - * @param gpio_num_t gpio_num: GPIO number + * @param gpio_num: GPIO number * * @return ESP_OK: success * ESP_ERR_INVALID_ARG: parameter error @@ -365,13 +365,13 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * TODO: to move INUM options to menu_config - * @parameter uint32_t gpio_intr_num : GPIO interrupt number,check the info in soc.h, and please see the core-isa.h for more details - * @parameter void (* fn)(void* ) : interrupt handler function. + * @param gpio_intr_num : GPIO interrupt number,check the info in soc.h, and please see the core-isa.h for more details + * @param (*fn)(void* ) : interrupt handler function. * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * @parameter void * arg : parameter for handler function + * @param arg : parameter for handler function * - * @return ESP_OK : success ; - * ESP_FAIL: gpio error + * @return ESP_OK : success ; + * ESP_FAIL: gpio error */ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg); diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index c12c991ce4..3c5c122921 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -16,6 +16,13 @@ Reference `Instructions `_ +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: gpio_int_type_t +.. doxygenenum:: gpio_mode_t +.. doxygenenum:: gpio_pull_mode_t + Functions ^^^^^^^^^ From 1263cd340eb492581f4f179e28eb7c6df3fec644 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 27 Oct 2016 21:20:30 +0200 Subject: [PATCH 078/285] Add missing annotations --- components/nvs_flash/include/nvs.h | 42 +++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index 912ea22103..d0c9908afb 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -28,23 +28,27 @@ extern "C" { */ typedef uint32_t nvs_handle; -#define ESP_ERR_NVS_BASE 0x1100 -#define ESP_ERR_NVS_NOT_INITIALIZED (ESP_ERR_NVS_BASE + 0x01) -#define ESP_ERR_NVS_NOT_FOUND (ESP_ERR_NVS_BASE + 0x02) -#define ESP_ERR_NVS_TYPE_MISMATCH (ESP_ERR_NVS_BASE + 0x03) -#define ESP_ERR_NVS_READ_ONLY (ESP_ERR_NVS_BASE + 0x04) -#define ESP_ERR_NVS_NOT_ENOUGH_SPACE (ESP_ERR_NVS_BASE + 0x05) -#define ESP_ERR_NVS_INVALID_NAME (ESP_ERR_NVS_BASE + 0x06) -#define ESP_ERR_NVS_INVALID_HANDLE (ESP_ERR_NVS_BASE + 0x07) -#define ESP_ERR_NVS_REMOVE_FAILED (ESP_ERR_NVS_BASE + 0x08) -#define ESP_ERR_NVS_KEY_TOO_LONG (ESP_ERR_NVS_BASE + 0x09) -#define ESP_ERR_NVS_PAGE_FULL (ESP_ERR_NVS_BASE + 0x0a) -#define ESP_ERR_NVS_INVALID_STATE (ESP_ERR_NVS_BASE + 0x0b) -#define ESP_ERR_NVS_INVALID_LENGTH (ESP_ERR_NVS_BASE + 0x0c) +#define ESP_ERR_NVS_BASE 0x1100 /*!< Starting number of error codes */ +#define ESP_ERR_NVS_NOT_INITIALIZED (ESP_ERR_NVS_BASE + 0x01) /*!< The storage driver is not initialized */ +#define ESP_ERR_NVS_NOT_FOUND (ESP_ERR_NVS_BASE + 0x02) /*!< Id namespace doesn’t exist yet and mode is NVS_READONLY */ +#define ESP_ERR_NVS_TYPE_MISMATCH (ESP_ERR_NVS_BASE + 0x03) /*!< TBA */ +#define ESP_ERR_NVS_READ_ONLY (ESP_ERR_NVS_BASE + 0x04) /*!< Storage handle was opened as read only */ +#define ESP_ERR_NVS_NOT_ENOUGH_SPACE (ESP_ERR_NVS_BASE + 0x05) /*!< There is not enough space in the underlying storage to save the value */ +#define ESP_ERR_NVS_INVALID_NAME (ESP_ERR_NVS_BASE + 0x06) /*!< Namespace name doesn’t satisfy constraints */ +#define ESP_ERR_NVS_INVALID_HANDLE (ESP_ERR_NVS_BASE + 0x07) /*!< Handle has been closed or is NULL */ +#define ESP_ERR_NVS_REMOVE_FAILED (ESP_ERR_NVS_BASE + 0x08) /*!< The value wasn’t updated because flash write operation has failed. The value was written however, and update will be finished after re-initialization of nvs, provided that flash operation doesn’t fail again. */ +#define ESP_ERR_NVS_KEY_TOO_LONG (ESP_ERR_NVS_BASE + 0x09) /*!< TBA */ +#define ESP_ERR_NVS_PAGE_FULL (ESP_ERR_NVS_BASE + 0x0a) /*!< TBA */ +#define ESP_ERR_NVS_INVALID_STATE (ESP_ERR_NVS_BASE + 0x0b) /*!< TBA */ +#define ESP_ERR_NVS_INVALID_LENGTH (ESP_ERR_NVS_BASE + 0x0c) /*!< TBA */ +/** + * @brief Mode of opening the non-volatile storage + * + */ typedef enum { - NVS_READONLY, - NVS_READWRITE + NVS_READONLY, /*!< Read only */ + NVS_READWRITE /*!< Read and write */ } nvs_open_mode; /** @@ -129,24 +133,26 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * It is suggested that nvs_get/set_str is used for zero-terminated C strings, and * nvs_get/set_blob used for arbitrary data structures. * - * Example of using nvs_get_i32: + * \code{c} + * // Example of using nvs_get_i32: * int32_t max_buffer_size = 4096; // default value * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still * // have its default value. * - * Example (without error checking) of using nvs_get_str to get a string into dynamic array: + * // Example (without error checking) of using nvs_get_str to get a string into dynamic array: * size_t required_size; * nvs_get_str(my_handle, "server_name", NULL, &required_size); * char* server_name = malloc(required_size); * nvs_get_str(my_handle, "server_name", server_name, &required_size); * - * Example (without error checking) of using nvs_get_blob to get a binary data + * // Example (without error checking) of using nvs_get_blob to get a binary data * into a static array: * uint8_t mac_addr[6]; * size_t size = sizeof(mac_addr); * nvs_get_blob(my_handle, "dst_mac_addr", mac_addr, &size); + * \endcode * * @param[in] handle Handle obtained from nvs_open function. * @param[in] key Key name. Maximal length is determined by the underlying From 234739f51a917ae3b27c551471d906d548678b09 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 27 Oct 2016 21:22:35 +0200 Subject: [PATCH 079/285] Draft of non-volatile storage component documentation --- docs/Doxyfile | 2 +- docs/api/nvs.rst | 68 +++++++++++++++++++++++++++++++++++++++++++ docs/api/template.rst | 9 ++++++ docs/index.rst | 1 + 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 docs/api/nvs.rst diff --git a/docs/Doxyfile b/docs/Doxyfile index f905de74ce..bb55b8ba39 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -4,7 +4,7 @@ GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver/gpio.h ../components/esp32/include/rom/gpio.h ../components/bt/include/bt.h +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver/gpio.h ../components/esp32/include/rom/gpio.h ../components/bt/include ../components/nvs_flash/include RECURSIVE = YES QUIET = YES JAVADOC_AUTOBRIEF = YES diff --git a/docs/api/nvs.rst b/docs/api/nvs.rst new file mode 100644 index 0000000000..227a1c1f7f --- /dev/null +++ b/docs/api/nvs.rst @@ -0,0 +1,68 @@ +.. include:: ../../components/nvs_flash/README.rst + +Reference +--------- + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: nvs_open_mode + +Functions +^^^^^^^^^ + +.. doxygenfunction:: nvs_flash_init +.. doxygenfunction:: nvs_flash_init_custom + +.. doxygenfunction:: nvs_open + +*Note: the following nvs_set_X function are "the same" except the data type accepted* + +.. doxygenfunction:: nvs_set_i8 +.. doxygenfunction:: nvs_set_u8 +.. doxygenfunction:: nvs_set_i16 +.. doxygenfunction:: nvs_set_u16 +.. doxygenfunction:: nvs_set_i32 +.. doxygenfunction:: nvs_set_u32 +.. doxygenfunction:: nvs_set_i64 +.. doxygenfunction:: nvs_set_u64 +.. doxygenfunction:: nvs_set_str +.. doxygenfunction:: nvs_set_blob + +*Note: the following nvs_get_X functions are "the same" except the data type returned* + +.. doxygenfunction:: nvs_get_i8 +.. doxygenfunction:: nvs_get_u8 +.. doxygenfunction:: nvs_get_i16 +.. doxygenfunction:: nvs_get_u16 +.. doxygenfunction:: nvs_get_i32 +.. doxygenfunction:: nvs_get_u32 +.. doxygenfunction:: nvs_get_i64 +.. doxygenfunction:: nvs_get_u64 +.. doxygenfunction:: nvs_get_str +.. doxygenfunction:: nvs_get_blob + +.. doxygenfunction:: nvs_erase_key +.. doxygenfunction:: nvs_erase_all +.. doxygenfunction:: nvs_commit +.. doxygenfunction:: nvs_close + +Error codes +^^^^^^^^^^^ + +.. doxygendefine:: ESP_ERR_NVS_BASE +.. doxygendefine:: ESP_ERR_NVS_NOT_INITIALIZED +.. doxygendefine:: ESP_ERR_NVS_NOT_FOUND +.. doxygendefine:: ESP_ERR_NVS_TYPE_MISMATCH +.. doxygendefine:: ESP_ERR_NVS_READ_ONLY +.. doxygendefine:: ESP_ERR_NVS_NOT_ENOUGH_SPACE +.. doxygendefine:: ESP_ERR_NVS_INVALID_NAME +.. doxygendefine:: ESP_ERR_NVS_INVALID_HANDLE +.. doxygendefine:: ESP_ERR_NVS_REMOVE_FAILED +.. doxygendefine:: ESP_ERR_NVS_KEY_TOO_LONG +.. doxygendefine:: ESP_ERR_NVS_PAGE_FULL +.. doxygendefine:: ESP_ERR_NVS_INVALID_STATE +.. doxygendefine:: ESP_ERR_NVS_INVALID_LENGTH + + + diff --git a/docs/api/template.rst b/docs/api/template.rst index 8b1dfd4c50..0f2623c47f 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -6,6 +6,15 @@ Overview INSTRUCTIONS: Provide overview where and how this API may be used. For large number of functions, break down description into groups. +Use the folowing heading levels: + +* # with overline, for parts +* \* with overline, for chapters +* =, for sections +* -, for subsections +* ^, for subsubsections +* ", for paragraphs + Application Example ------------------- diff --git a/docs/index.rst b/docs/index.rst index 5c4d7025c6..9b62885bb1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,6 +37,7 @@ Contents: Wi-Fi Bluetooth GPIO + NVS Template .. toctree:: From fa002b490966ba3e4112a0d6f459ced22c7aa0ca Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Fri, 28 Oct 2016 14:43:48 +0200 Subject: [PATCH 080/285] Fixed headers to match python doc standard --- components/nvs_flash/README.rst | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/components/nvs_flash/README.rst b/components/nvs_flash/README.rst index 2f1c469135..ade5518aa5 100644 --- a/components/nvs_flash/README.rst +++ b/components/nvs_flash/README.rst @@ -7,14 +7,14 @@ Introduction Non-volatile storage (NVS) library is designed to store key-value pairs in flash. This sections introduces some concepts used by NVS. Underlying storage -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ Currently NVS uses a portion of main flash memory through ``spi_flash_{read|write|erase}`` APIs. The range of flash sectors to be used by the library is provided to ``nvs_flash_init`` function. Future versions of this library may add other storage backends to keep data in another flash chip (SPI or I2C), RTC, FRAM, etc. Keys and values -~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^ NVS operates on key-value pairs. Keys are ASCII strings, maximum key length is currently 15 characters. Values can have one of the following types: @@ -32,12 +32,12 @@ Keys are required to be unique. Writing a value for a key which already exists b Data type check is also performed when reading a value. An error is returned if data type of read operation doesn’t match the data type of the value. Namespaces -~~~~~~~~~~ +^^^^^^^^^^ To mitigate potential conflicts in key names between different components, NVS assigns each key-value pair to one of namespaces. Namespace names follow the same rules as key names, i.e. 15 character maximum length. Namespace name is specified in the ``nvs_open`` call. This call returns an opaque handle, which is used in subsequent calls to ``nvs_read_*``, ``nvs_write_*``, and ``nvs_commit`` functions. This way, handle is associated with a namespace, and key names will not collide with same names in other namespaces. Security, tampering, and robustness -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ NVS library doesn't implement tamper prevention measures. It is possible for anyone with physical access to the flash chip to alter, erase, or add key-value pairs. @@ -59,12 +59,12 @@ Internals --------- Log of key-value pairs -~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^ NVS stores key-value pairs sequentially, with new key-value pairs being added at the end. When a value of any given key has to be updated, new key-value pair is added at the end of the log and old key-value pair is marked as erased. Pages and entries -~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^ NVS library uses two main entities in its operation: pages and entries. Page is a logical structure which stores a portion of the overall log. Logical page corresponds to one physical sector of flash memory. Pages which are in use have a *sequence number* associated with them. Sequence numbers impose an ordering on pages. Higher sequence numbers correspond to pages which were created later. Each page can be in one of the following states: @@ -101,7 +101,7 @@ Mapping from flash sectors to logical pages doesn't have any particular order. L +----------+ +----------+ +----------+ +----------+ Structure of a page -~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^ For now we assume that flash sector size is 4096 bytes and that ESP32 flash encryption hardware operates on 32-byte blocks. It is possible to introduce some settings configurable at compile-time (e.g. via menuconfig) to accommodate flash chips with different sector sizes (although it is not clear if other components in the system, e.g. SPI flash driver and SPI flash cache can support these other sizes). @@ -133,7 +133,7 @@ CRC32 value in header is calculated over the part which doesn't include state va The following sections describe structure of entry state bitmap and entry itself. Entry and entry state bitmap -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Each entry can be in one of the following three states. Each state is represented with two bits in the entry state bitmap. Final four bits in the bitmap (256 - 2 * 126) are unused. @@ -148,7 +148,7 @@ Erased (2'b00) Structure of entry -~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^ For values of primitive types (currently integers from 1 to 8 bytes long), entry holds one key-value pair. For string and blob types, entry holds part of the whole key-value pair. In case when a key-value pair spans multiple entries, all entries are stored in the same page. @@ -200,7 +200,7 @@ Variable length values (strings and blobs) are written into subsequent entries, Namespaces -~~~~~~~~~~ +^^^^^^^^^^ As mentioned above, each key-value pair belongs to one of the namespaces. Namespaces identifiers (strings) are stored as keys of key-value pairs in namespace with index 0. Values corresponding to these keys are indexes of these namespaces. @@ -218,10 +218,9 @@ As mentioned above, each key-value pair belongs to one of the namespaces. Namesp Item hash list -~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^ To reduce the number of reads performed from flash memory, each member of Page class maintains a list of pairs: (item index; item hash). This list makes searches much quicker. Instead of iterating over all entries, reading them from flash one at a time, ``Page::findItem`` first performs search for item hash in the hash list. This gives the item index within the page, if such an item exists. Due to a hash collision it is possible that a different item will be found. This is handled by falling back to iteration over items in flash. Each node in hash list contains a 24-bit hash and 8-bit item index. Hash is calculated based on item namespace and key name. CRC32 is used for calculation, result is truncated to 24 bits. To reduce overhead of storing 32-bit entries in a linked list, list is implemented as a doubly-linked list of arrays. Each array holds 29 entries, for the total size of 128 bytes, together with linked list pointers and 32-bit count field. Minimal amount of extra RAM useage per page is therefore 128 bytes, maximum is 640 bytes. - From 2d56953ee458266e00bba2788ec6821823eddd8d Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 29 Oct 2016 20:54:58 +0200 Subject: [PATCH 081/285] docu makup update --- components/bt/include/bt.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/components/bt/include/bt.h b/components/bt/include/bt.h index 1e89f96aa1..f476334b12 100644 --- a/components/bt/include/bt.h +++ b/components/bt/include/bt.h @@ -28,37 +28,36 @@ extern "C" { * * This function should be called only once, before any other BT functions are called. */ -void bt_controller_init(); +void bt_controller_init(void); -/** @brief: vhci_host_callback +/** @brief vhci_host_callback * used for vhci call host function to notify what host need to do * * notify_host_send_available: notify host can send packet to controller * notify_host_recv: notify host that controller has packet send to host */ typedef struct vhci_host_callback { - void (*notify_host_send_available)(void); int (*notify_host_recv)(uint8_t *data, uint16_t len); } vhci_host_callback_t; -/** @brief: API_vhci_host_check_send_available +/** @brief API_vhci_host_check_send_available * used for check actively if the host can send packet to controller or not. - * return true for ready to send, false means cannot send packet + * @return true for ready to send, false means cannot send packet */ bool API_vhci_host_check_send_available(void); -/** @brief: API_vhci_host_send_packet +/** @brief API_vhci_host_send_packet * host send packet to controller - * param data is the packet point, the param len is the packet length - * return void + * @param data the packet point + *,@param len the packet length */ void API_vhci_host_send_packet(uint8_t *data, uint16_t len); -/** @brief: API_vhci_host_register_callback +/** @brief API_vhci_host_register_callback * register the vhci referece callback, the call back * struct defined by vhci_host_callback structure. - * param is the vhci_host_callback type variable + * @param callback vhci_host_callback type variable */ void API_vhci_host_register_callback(const vhci_host_callback_t *callback); From 905d27815c4889be2250e408e9194fd3ae70d058 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 29 Oct 2016 23:00:30 +0200 Subject: [PATCH 082/285] API samples - Logging library - Virtual file system component --- docs/Doxyfile | 31 ++++++++++++++++++++----------- docs/api/bt.rst | 2 +- docs/api/log.rst | 20 ++++++++++++++++++++ docs/api/nvs.rst | 4 ++-- docs/api/vfs.rst | 27 +++++++++++++++++++++++++++ docs/index.rst | 4 +++- 6 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 docs/api/log.rst create mode 100644 docs/api/vfs.rst diff --git a/docs/Doxyfile b/docs/Doxyfile index bb55b8ba39..9c53aff1f3 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,13 +1,22 @@ PROJECT_NAME = "ESP32 Programming Guide" -XML_OUTPUT = xml -GENERATE_LATEX = NO -GENERATE_MAN = NO -GENERATE_RTF = NO -CASE_SENSE_NAMES = NO -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver/gpio.h ../components/esp32/include/rom/gpio.h ../components/bt/include ../components/nvs_flash/include -RECURSIVE = YES + +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/esp32/include/rom/gpio.h ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include + +WARN_NO_PARAMDOC = YES + +RECURSIVE = NO +CASE_SENSE_NAMES = NO +EXTRACT_ALL = NO + +GENERATE_XML = YES +XML_OUTPUT = xml + +GENERATE_HTML = NO +HAVE_DOT = NO +GENERATE_LATEX = NO +GENERATE_MAN = NO +GENERATE_RTF = NO + QUIET = YES -JAVADOC_AUTOBRIEF = YES -GENERATE_HTML = NO -GENERATE_XML = YES -WARN_LOGFILE = "DoxyGenWarningLog.txt" +WARN_LOGFILE = "doxygen-warning-log.txt" + diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 16f30dc4e6..72ab9fbd12 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -19,7 +19,7 @@ Reference Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygentypedef:: vhci_host_callback +.. doxygenstruct:: vhci_host_callback Functions ^^^^^^^^^ diff --git a/docs/api/log.rst b/docs/api/log.rst new file mode 100644 index 0000000000..8e0f2d85da --- /dev/null +++ b/docs/api/log.rst @@ -0,0 +1,20 @@ +.. include:: ../../components/log/README.rst + +API Reference +------------- + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: esp_log_level_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_log_level_set +.. doxygenfunction:: esp_log_set_vprintf +.. doxygenfunction:: esp_log_write + +.. FIXME esp_log_timestamp + + diff --git a/docs/api/nvs.rst b/docs/api/nvs.rst index 227a1c1f7f..fc2bba5a16 100644 --- a/docs/api/nvs.rst +++ b/docs/api/nvs.rst @@ -1,7 +1,7 @@ .. include:: ../../components/nvs_flash/README.rst -Reference ---------- +API Reference +------------- Enumerations ^^^^^^^^^^^^ diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst new file mode 100644 index 0000000000..97ea1a5848 --- /dev/null +++ b/docs/api/vfs.rst @@ -0,0 +1,27 @@ +.. include:: ../../components/vfs/README.rst + +API Reference +------------- + +Defines +^^^^^^^ + +.. doxygendefine:: ESP_VFS_PATH_MAX +.. doxygendefine:: ESP_VFS_FLAG_DEFAULT +.. doxygendefine:: ESP_VFS_FLAG_CONTEXT_PTR + + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: esp_vfs_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_vfs_dev_uart_register +.. doxygenfunction:: esp_vfs_register + + + + diff --git a/docs/index.rst b/docs/index.rst index 9b62885bb1..72c5df13f4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,7 +37,9 @@ Contents: Wi-Fi Bluetooth GPIO - NVS + Logging + Non-volatile storage + Virtual filesystem Template .. toctree:: From f6118c67b09f479297a959ce6b0714d717e2ebbc Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 29 Oct 2016 23:00:46 +0200 Subject: [PATCH 083/285] Fixed desription of logging library --- components/log/README.rst | 61 +++++++++++++++++++++++++ components/log/include/esp_log.h | 76 ++++---------------------------- 2 files changed, 69 insertions(+), 68 deletions(-) create mode 100644 components/log/README.rst diff --git a/components/log/README.rst b/components/log/README.rst new file mode 100644 index 0000000000..d378179c8f --- /dev/null +++ b/components/log/README.rst @@ -0,0 +1,61 @@ +Logging library +=============== + +Overview +-------- + +Log library has two ways of managing log verbosity: compile time, set via menuconfig; and runtime, using ``esp_log_set_level`` function. + +At compile time, filtering is done using ``CONFIG_LOG_DEFAULT_LEVEL`` macro, set via menuconfig. All logging statments for levels higher than ``CONFIG_LOG_DEFAULT_LEVEL`` will be removed by the preprocessor. + +At run time, all logs below ``CONFIG_LOG_DEFAULT_LEVEL`` are enabled by default. ``esp_log_set_level`` function may be used to set logging level per module. Modules are identified by their tags, which are human-readable ASCII zero-terminated strings. + +How to use this library +----------------------- + +In each C file which uses logging functionality, define TAG variable like this: + +.. code-block:: c + + static const char* TAG = "MyModule"; + +then use one of logging macros to produce output, e.g: + +.. code-block:: c + + ESP_LOGW(TAG, "Baud rate error %.1f%%. Requested: %d baud, actual: %d baud", error * 100, baud_req, baud_real); + +Several macros are available for different verbosity levels: + +* ``ESP_LOGE`` - error +* ``ESP_LOGW`` - warning +* ``ESP_LOGI`` - info +* ``ESP_LOGD`` - debug +* ``ESP_LOGV`` - verbose + +Additionally there is an _EARLY_ variant for each of these macros (e.g. ``ESP_EARLY_LOGE`` ).These variants can run in startup code, before heap allocator and syscalls have been initialized. When compiling bootloader, normal ``ESP_LOGx`` macros fall back to the same implementation as ``ESP_EARLY_LOGx`` macros. So the only place where ``ESP_EARLY_LOGx`` have to be used explicitly is the early startup code, such as heap allocator initialization code. + +(Note that such distinction would not have been necessary if we would have an ``ets_vprintf`` function in the ROM. Then it would be possible to switch implementation from _EARLY_ version to normal version on the fly. Unfortunately, ``ets_vprintf`` in ROM has been inlined by the compiler into ``ets_printf``, so it is not accessible outside.) + +To override default verbosity level at file or component scope, define ``LOG_LOCAL_LEVEL`` macro. At file scope, define it before including ``esp_log.h``, e.g.: + +.. code-block:: c + + #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE + #include "esp_log.h" + + +At component scope, define it in component makefile: + +.. code-block:: make + + CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG + +To configure logging output per module at runtime, add calls to ``esp_log_set_level`` function: + +.. code-block:: c + + esp_log_set_level("*", ESP_LOG_ERROR); // set all components to ERROR level + esp_log_set_level("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack + esp_log_set_level("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client + diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 8ca6e241d4..2878dc5015 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -24,76 +24,16 @@ extern "C" { #endif /** - * @brief Logging library - * - * Log library has two ways of managing log verbosity: compile time, set via - * menuconfig, and runtime, using esp_log_set_level function. - * - * At compile time, filtering is done using CONFIG_LOG_DEFAULT_LEVEL macro, set via - * menuconfig. All logging statments for levels higher than CONFIG_LOG_DEFAULT_LEVEL - * will be removed by the preprocessor. - * - * At run time, all logs below CONFIG_LOG_DEFAULT_LEVEL are enabled by default. - * esp_log_set_level function may be used to set logging level per module. - * Modules are identified by their tags, which are human-readable ASCII - * zero-terminated strings. - * - * How to use this library: - * - * In each C file which uses logging functionality, define TAG variable like this: - * - * static const char* TAG = "MyModule"; - * - * then use one of logging macros to produce output, e.g: - * - * ESP_LOGW(TAG, "Baud rate error %.1f%%. Requested: %d baud, actual: %d baud", error * 100, baud_req, baud_real); - * - * Several macros are available for different verbosity levels: - * - * ESP_LOGE — error - * ESP_LOGW — warning - * ESP_LOGI — info - * ESP_LOGD - debug - * ESP_LOGV - verbose - * - * Additionally there is an _EARLY_ variant for each of these macros (e.g. ESP_EARLY_LOGE). - * These variants can run in startup code, before heap allocator and syscalls - * have been initialized. - * When compiling bootloader, normal ESP_LOGx macros fall back to the same implementation - * as ESP_EARLY_LOGx macros. So the only place where ESP_EARLY_LOGx have to be used explicitly - * is the early startup code, such as heap allocator initialization code. - * - * (Note that such distinction would not have been necessary if we would have an - * ets_vprintf function in the ROM. Then it would be possible to switch implementation - * from _EARLY version to normal version on the fly. Unfortunately, ets_vprintf in ROM - * has been inlined by the compiler into ets_printf, so it is not accessible outside.) - * - * To override default verbosity level at file or component scope, define LOG_LOCAL_LEVEL macro. - * At file scope, define it before including esp_log.h, e.g.: - * - * #define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE - * #include "esp_log.h" - * - * At component scope, define it in component makefile: - * - * CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG - * - * To configure logging output per module at runtime, add calls to esp_log_set_level function: - * - * esp_log_set_level("*", ESP_LOG_ERROR); // set all components to ERROR level - * esp_log_set_level("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack - * esp_log_set_level("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client + * @brief Log level * */ - - typedef enum { - ESP_LOG_NONE, // No log output - ESP_LOG_ERROR, // Critical errors, software module can not recover on its own - ESP_LOG_WARN, // Error conditions from which recovery measures have been taken - ESP_LOG_INFO, // Information messages which describe normal flow of events - ESP_LOG_DEBUG, // Extra information which is not necessary for normal use (values, pointers, sizes, etc). - ESP_LOG_VERBOSE // Bigger chunks of debugging information, or frequent messages which can potentially flood the output. + ESP_LOG_NONE, /*!< No log output */ + ESP_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */ + ESP_LOG_WARN, /*!< Error conditions from which recovery measures have been taken */ + ESP_LOG_INFO, /*!< Information messages which describe normal flow of events */ + ESP_LOG_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */ + ESP_LOG_VERBOSE /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */ } esp_log_level_t; typedef int (*vprintf_like_t)(const char *, va_list); @@ -143,7 +83,7 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, . * * @return timestamp, in milliseconds */ -uint32_t esp_log_timestamp(); +uint32_t esp_log_timestamp(void); #if CONFIG_LOG_COLORS From 079f5faad88353ec64d44bbe08d612e514fc0819 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 29 Oct 2016 23:15:27 +0200 Subject: [PATCH 084/285] Fixed confused Sphinx Sphinx failed to phrase esp_log_timestamp reorderdering esp_log_write and esp_log_timestamp fixed this issue --- components/log/include/esp_log.h | 23 +++++++++++------------ docs/api/log.rst | 3 +-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 2878dc5015..6716f6e10f 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -29,7 +29,7 @@ extern "C" { */ typedef enum { ESP_LOG_NONE, /*!< No log output */ - ESP_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */ + ESP_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */ ESP_LOG_WARN, /*!< Error conditions from which recovery measures have been taken */ ESP_LOG_INFO, /*!< Information messages which describe normal flow of events */ ESP_LOG_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */ @@ -60,17 +60,6 @@ void esp_log_level_set(const char* tag, esp_log_level_t level); */ void esp_log_set_vprintf(vprintf_like_t func); -/** - * @brief Write message into the log - * - * This function is not intended to be used directly. Instead, use one of - * ESP_LOGE, ESP_LOGW, ESP_LOGI, ESP_LOGD, ESP_LOGV macros. - * - * This function or these macros should not be used from an interrupt. - */ -void esp_log_write(esp_log_level_t level, const char* tag, const char* format, ...) __attribute__ ((format (printf, 3, 4))); - - /** * @brief Function which returns timestamp to be used in log output * @@ -85,6 +74,16 @@ void esp_log_write(esp_log_level_t level, const char* tag, const char* format, . */ uint32_t esp_log_timestamp(void); +/** + * @brief Write message into the log + * + * This function is not intended to be used directly. Instead, use one of + * ESP_LOGE, ESP_LOGW, ESP_LOGI, ESP_LOGD, ESP_LOGV macros. + * + * This function or these macros should not be used from an interrupt. + */ +void esp_log_write(esp_log_level_t level, const char* tag, const char* format, ...) __attribute__ ((format (printf, 3, 4))); + #if CONFIG_LOG_COLORS #define LOG_COLOR_BLACK "30" diff --git a/docs/api/log.rst b/docs/api/log.rst index 8e0f2d85da..dc76a30470 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -13,8 +13,7 @@ Functions .. doxygenfunction:: esp_log_level_set .. doxygenfunction:: esp_log_set_vprintf +.. doxygenfunction:: esp_log_timestamp .. doxygenfunction:: esp_log_write -.. FIXME esp_log_timestamp - From 19af5b7a764ae33a29001cb392f5a20ce4400432 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 29 Oct 2016 23:35:12 +0200 Subject: [PATCH 085/285] Changed title of docs --- docs/conf.py | 4 ++-- docs/index.rst | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5ba76d7f25..8a8821fb0d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -57,8 +57,8 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'Read the Docs Template' -copyright = u'2014, Read the Docs' +project = u'ESP32 Programming Guide' +copyright = u'2016, Espressif' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/index.rst b/docs/index.rst index 72c5df13f4..e194992089 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,10 @@ ESP32 Programming Guide ======================= +.. caution:: + + This is DRAFT - mind your step + Contents: .. toctree:: From 12efd96fb489ea3e697c32f24406aff2f96d7ff0 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 30 Oct 2016 19:37:45 +0100 Subject: [PATCH 086/285] Corrected documentation style This is for better visuaization with Sphinx --- components/nvs_flash/include/nvs.h | 23 +++++++++++++++-------- components/nvs_flash/include/nvs_flash.h | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index d0c9908afb..8418959793 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -62,12 +62,13 @@ typedef enum { * underlying implementation, but is guaranteed to be * at least 16 characters. Shouldn't be empty. * @param[in] open_mode NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will - * open a handle for reading only. All write requests will - * be rejected for this handle. + * open a handle for reading only. All write requests will + * be rejected for this handle. * @param[out] out_handle If successful (return code is zero), handle will be * returned in this argument. * - * @return - ESP_OK if storage handle was opened successfully + * @return + * - ESP_OK if storage handle was opened successfully * - ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized * - ESP_ERR_NVS_NOT_FOUND id namespace doesn't exist yet and * mode is NVS_READONLY @@ -90,7 +91,8 @@ esp_err_t nvs_open(const char* name, nvs_open_mode open_mode, nvs_handle *out_ha * @param[in] value The value to set. * @param[in] length For nvs_set_blob: length of binary value to set, in bytes. * - * @return - ESP_OK if value was set successfully + * @return + * - ESP_OK if value was set successfully * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL * - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints @@ -168,7 +170,8 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * zero, will be set to the actual length of the value * written. For nvs_get_str this includes zero terminator. * - * @return - ESP_OK if the value was retrieved successfully + * @return + * - ESP_OK if the value was retrieved successfully * - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints @@ -197,7 +200,8 @@ esp_err_t nvs_get_blob(nvs_handle handle, const char* key, void* out_value, size * implementation, but is guaranteed to be at least * 16 characters. Shouldn't be empty. * - * @return - ESP_OK if erase operation was successful + * @return + * - ESP_OK if erase operation was successful * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL * - ESP_ERR_NVS_READ_ONLY if handle was opened as read only * - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist @@ -213,7 +217,8 @@ esp_err_t nvs_erase_key(nvs_handle handle, const char* key); * @param[in] handle Storage handle obtained with nvs_open. * Handles that were opened read only cannot be used. * - * @return - ESP_OK if erase operation was successful + * @return + * - ESP_OK if erase operation was successful * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL * - ESP_ERR_NVS_READ_ONLY if handle was opened as read only * - other error codes from the underlying storage driver @@ -230,7 +235,8 @@ esp_err_t nvs_erase_all(nvs_handle handle); * @param[in] handle Storage handle obtained with nvs_open. * Handles that were opened read only cannot be used. * - * @return - ESP_OK if the changes have been written successfully + * @return + * - ESP_OK if the changes have been written successfully * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL * - other error codes from the underlying storage driver */ @@ -255,3 +261,4 @@ void nvs_close(nvs_handle handle); #endif #endif //ESP_NVS_H + diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index ce98f39407..1cade0e956 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -22,6 +22,8 @@ extern "C" { Temporarily, this region is hardcoded as a 12KB (0x3000 byte) region starting at 24KB (0x6000 byte) offset in flash. + + @return ESP_OK if flash was successfully initialised. */ esp_err_t nvs_flash_init(void); From d66f05c61b561c04d46cbe2aeb1cdb54fd0c2fb9 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 30 Oct 2016 19:38:44 +0100 Subject: [PATCH 087/285] Guide on documenting code --- docs/_static/1.png | Bin 11412 -> 0 bytes .../_static/doc-code-documentation-inline.png | Bin 0 -> 52513 bytes .../doc-code-documentation-rendered.png | Bin 0 -> 93186 bytes docs/_static/doc-code-function.png | Bin 0 -> 194398 bytes docs/_static/doc-code-member.png | Bin 0 -> 75946 bytes docs/_static/doc-code-void-function.png | Bin 0 -> 68465 bytes docs/documenting-code.rst | 126 ++++++++++++++++++ docs/index.rst | 7 +- 8 files changed, 132 insertions(+), 1 deletion(-) delete mode 100644 docs/_static/1.png create mode 100644 docs/_static/doc-code-documentation-inline.png create mode 100644 docs/_static/doc-code-documentation-rendered.png create mode 100644 docs/_static/doc-code-function.png create mode 100644 docs/_static/doc-code-member.png create mode 100644 docs/_static/doc-code-void-function.png create mode 100644 docs/documenting-code.rst diff --git a/docs/_static/1.png b/docs/_static/1.png deleted file mode 100644 index 4920892781f90c3db323f82033866c3f7116c073..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11412 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
I-V-NX(N}q}D@(Dr+V{Hb>y4lN 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 U;S}7@z`(%Z>FVdQ&MBb@0LNnn&j0`b diff --git a/docs/_static/doc-code-documentation-inline.png b/docs/_static/doc-code-documentation-inline.png new file mode 100644 index 0000000000000000000000000000000000000000..72bf22f156224e4e32e7934828ccc73f15cea67e GIT binary patch literal 52513 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 Tw6A4gU|{fc^>bP0l+XkKb7s{- literal 0 HcmV?d00001 diff --git a/docs/_static/doc-code-documentation-rendered.png b/docs/_static/doc-code-documentation-rendered.png new file mode 100644 index 0000000000000000000000000000000000000000..d287d3c2e133a45c7bf11fd130d274a52202e246 GIT binary patch literal 93186 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

y~8{bZ}19aYtNhJoDYr@JJ@(7 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৞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 z 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 literal 0 HcmV?d00001 diff --git a/docs/_static/doc-code-function.png b/docs/_static/doc-code-function.png new file mode 100644 index 0000000000000000000000000000000000000000..758971ce7271bb175d27d063643f21c03e00b8df GIT binary patch literal 194398 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
$*5C2*T@t5w)7_oH_GgxzT(B&3dvZ$n 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%0A?u9=SpB8M@^|-vuH{9*h;kfX_ zTW_aW%h-hdTkXI7+t%$$x{FpX547NHj9a)TTck5LZ1%S~=D*_FAX|s-fp(%i^xi;aC z+f27wuyCvQ_E?VHyFO{}m}ipqa@AIy$jY_4ar-8oRh#D$@+wdFz74PQdiQiApWmui zzGeOKng01ru>R*eS5nv9+0Z++`}wD9Grs>UXjf0n6r5wft1I>JvPAZ1pCr#!?=Quj zFUY*W+4S)8g3Slt8m(`emuygTpe;vw%j{g?N*z`YSE=PaWt;x&`jWjq@ut_dxqPZ| zb-|7ut*_4)=1Q&ey}y9>uKNvx-x;hcZ-j-19O}GK!_*bE^?m1cuKC&D_Wp};-hO(2 z`)xCcvcGlB$M3zVxVvrj!}&+@O8Occ9Q_t-Wf^+wZOaXO{PTzARbY|MA`4<8|lk{P!e3&nxzOGN<^Q zrIsZJhmea=l0%4!#tIHCEst}SA)>CKjQ5MrhO&x9M18w^t9*N{?{W8pvpdsvr2RU# z^Vuq|Rkf|J4vP9r=(zSQ@<(pMkE<-3R?k1w_32T_Kb~_>Z8zw=cFlUuqt=pf)!+H} z?w8r(3dP;tm(*ok!)=?Dk3NsO+1>LkbI-zmhF8tIj%0m&X{*GWYh|XYU-0rKliSfp zCD!L}zw{`~U%vUc|M7P=FBJdXEB*h@;L`g=dcTfM48PAi+i0(isJ(mDaUM30E#8ZM zGr!#Ld-%fE`IYng%HQ4PQ~tXy*V&l6$foKK+s^OqAER^xzU$R}N%(l);7nYPrP%*? zt}b1lJ^PwlHl!Shd%2J^`SIJEX&-k~uHl{@ytm-h98JG@ch7cS4%_fq_vqY@rk)k= zZ*~5$JkhwbHPGa0%Hi|(q?Y^5K72$`rZ6(;D{C))=a^0Tqsd%$5 zeft~p#v8fI=L`2weD|_xv(owM(*>VQ<`}PEoa z``=Hnsrve5$Bo*$^KML-FHwCY@9&(v<##Xq{m=2}WlKfT<`u%9f7K-wDwJ*CUOsE< z&s$T@E&TtcZ1MKT`(&qA98!5ZBY5Xgzklyaj;#B;yYko83H$cO+SuNhto||O+xC0u zX|8j%UM^>}uZ{R)%(GcMx$o$I@#d~03QO|0h{pfg8h*fkQu~iPe@iz_RlL^mGhS!c zbxm0zE2)M}O0tgwg+z2anMHk%sEBCPw_OtNu&McS+~D8l3655FsT%*83T-@OVrLkX zynpWSMowzixj&z8+`Brp?_}V9k=;9+Z2#?k_p>$Q@o$GcO1`mmWlv8BL?q`d-)cV3 z_Imk`^uFy%{zt4UjYPhgw}niXPqY^M&vW?A(HY4f|FyEtF zAMPnyye&zz3D(hT4GpeJWxmfnFL48cmPWw4OrVH#=;W=!;^SS!ig+Ip(&dAFp zzw6foTMDw5`^b#skLL|QDh?aObHLmOa-x&LGf+$%21%X~Z!$doXgf4{X&zbgTZKKlALG*tTyQG8OKy-(2#WC-%Oh z`1ka!2h0AM*Vp~4XT1IGcFp_noB6LkE#*!(IFs&jrB^Sv`QLt}*&*g_>x@Az_ z?0?CdDv4SytKHVNMJ>2^q4d=A>ee^a3XPgBYt!C;%{6)A-f{oF^fPhAPrmj`|LpgD zeckc%L!-dG`9U*sfB(BxDQs_gNN>@+`m0;+yS`%n|HKC5X`avL!I3=k#-e)ZPl+~b zJlguVYWkd?dEm-9S;M2f6_)GUZaA8k+|ScE^Goa5?^z#GQUq78Dlgx5G;e*@BF;`m z#SY#2p2$_7*UsO)ay!6&7!3=)ZZ;MImbI~x&Nb-5>>gLHJ)!B?bn?@`fQq1*Yu|xp9@@aLdDar z%)hVqJ~`O7`<}bLp~*InvY+ZUjNoW`X5MIc{L%knheM^_-!$a656X0UzY&~1MdTX$`UC$y za7L(@Cpul(Iq%2)zVFV*)9$rhT=-tUZ)-r_ROax9-yVm5(5UXqoX9V+BmTeLqrFUD z=9=6)wz1ZCN_KK8({<hok>R97p$w&~w&FVm8J&-qLT*X@nnh4+r7-l(5Fw}9=> zkwP7=#KiN;agV;T`_kkkKMO?foS${jaIxg%Md!9}dH%rM{7Qi5$92w2h1`RW-29Sj&DrRl zs9~2Y;T65Mt?ggf>d-0nT>56dqKIO?6*K3hl6w)DnSwpQ_{6I+&- zu(&VTxje4r$CB5V1T`k^<5)kPeQVnr_v9{MTj64P^rPx0VfPs>cH3Nb?fSD) zL+|9d>Xw~fmNZ|QbZOb2OrDmBZ&pXP?=qL~d*^u~bLN8mn)y@wAFdbuG{rym-}RNU zIbGk)OFnj(Dclpid9K^TUG-J_tp<(P>$XUMOQo~PXEy%$e1?dtUJ1eEp$) zi_;U*a@l{+3FFbp*isl!rF5F79TC$(b^HWZyY>#k+bjHwt}y`hp&dej##{jgYj425Al=1zg{i1Gnsni(Cw34 zV$-&L*jd2auND$yss8Sn^0OMdRn^x$V)8r}g~v?i-SQyFOXQN2`^#Ax_54ynE^<06 z|HyHi-PzhGs>zrmCG_9QaQB}j41x31KVF@`@9@FLY8y6eYxsUN`o-MkA!bu&R9fZV)S8dvTByxUP>RR1Nd@b5P z=~i^cnv?EZ&YJ(I|MyiOX43iv#eTj?Q;sa1a{cb3`o2pyBB~p;C&m@y z9VqmkbNh_bJFFKq{&8iDIUjLY^S7XwDx;3qp04z#4>#C6ZCI18wJA`0U5mwG%Pj#~ z^_SPKKC$FeHEa0epNFi>L@sAkojK+yQWkXmy8Ys(8j0?d{~vdY z3uZiJxPHQW#qO&eucu0Kl$kaMUrF6^Hu%}~szBlV-uj=jC7UjN4k+Hs*n5`$qVd9p zZ%bo0zPQ;^^Nmx>>CFSF34M=lx!qZF&hzVIYxBRdFBYHe1c|xFfW%jPVOD6F%=5BE)ah4YgReJUI+paSknQe>GyqeeY7WSktbL+pJm{BNo z^80a*U5u+wwJH_dbB>_YWA7T zYlc<29M5%{11{LH^FDR2xyAP^U-8;KcF)xF9%1JeC>{~J=65n<-sN{sl2@|2CYo*F z$UJoAn2nV_+krbKGFyX}q%Hk4`9p1Bv&Cs+kJmGrib}=4KjM=V_@DHBc3z;J%ch@o zJ5qyp9%bG5CaR_(X8LA>kLR3UGyT=QBldK=ors^huRw^U+d;Mh+mk&1H{KU~$*;EL zyZ#ox?ThtfF9#)k3RIl4-?Dp(wep2og`(cu7ZUxt2vloV#J_9~S~{`MGIyQr;j<~` zbH3#tIkAh)oB1lo&u{88T;iOk2mE?sviW?#n{T{)ZdVuA2i^$2esAgB>L{MCOAg-C zm0N0i>5^Yjv{aM?&)0?T&$5W57$=(dTzjzRaAbsv{lY5w!*4&|vnWzyjrEF^)wOpo zoxAq&_7LF{@7KZpp*B;&oq- z)=P_dF4%S?^6()`=Icw#?OS72QhOfG=G?H@)zRm+RLJ_(6Q33F=(+waX)oa7x#QLF zld*itcio9Umd{vPv`712`c>{19k#81?PU%~T9;Q;?l^q*EN@)gHH$Ct?GI;HiVK%q z|FqYl;>sp+>^d0MBhO4=yOx=+kP_ELl9 zQ-O6yqm%cS8r@1=G&!gHUvA6ZYlgqq2s!@tQ{42g*YAXW(w{`Wv^@Zyz=s z+xgdhV?U16y`)n|5ip#AJE2^s!N zTEkR6-D+98{q2&q<-Iu!x>Ij<@P2Due_2!J$8W|A?4Vt*1S(w{YhdKC8RyL!{t-ac^2!FJNxzK0#jf^z*|7ihhc zsWakts=DX;R?YisGq$eTM#w#uz_*1ojv-Feaf-YSmyZV`uW?q*oK#DBX7Q-1e> znbUGCX3jXYxQI#WDO22)Ik~(*_mbeUZ!2F+n4I`b zZn4TIow`jNZfzR%=gMSmYOKjQaM;lD%Ie^ZF4K}X&ut&gciFJ$vCSPFn+bOeR9dHt z89WkCHDBj;>)`}{!?^W4ixuxNwy)7&kQ^HI?9Gj0>5oC{+zi|1Jt~Npb>raO%j|aZ z-fh@k=5}eT+?h33j)Q`f{MKB8)dN?t4U1cB)MOrM?FdC0E_M>Nh5@ zKk(qLl1kO?L)towGk>ybSiR@$p5J%S>VuVw$IaPlB5ctQmuwZU`BU@Y%#%kZDbPu&vS=oV+#mwbazZLII7k&9|NutA_^DC!)wD~G>?uY0i-`^)Ey|+5a zCbegW5F`71i5QK2JrUv2J37P{xbBX5>*%`0)~_P=XiLShsp)r zs4aU=G@p;;W|>yz?eF)3jmtalli4O2|Hm;mbfa8&A4Scv<%^no{IKZZNAYu(MaI8z zcHhR{^>;r9ImJGB37V%yO~Zsl4%l5tnf71Azj;x0kXq zglkI;3rs#cr1h`xbk1Y`yq>qae&gr=iXZcz%5*oeU&`J$>9UWx^3`)P>9Ro=ex&(T z@5y{=*R|(t!n5lPfu+U%(gi2)%X6x3U9on%c>T&`C)bz+wCcQYdVt6cu#JyPHDOz zC3M%TaJ|&0ljapguf^t_JF#T3x%T2-j~m~aV~pjJQc@+oPjjvDOx&7rw4U|OxfzEH z|Jp6n{Mvd>X2*;<&80=cZciRPO;EhI{$Z0XXW8`b7b5qYw|uaa(`x8<@nD$WV@X=nJclH*JQPG#{SD~ z%l~c;y4MvF1mZxkXD zws?-)aaZ*j^EbG}2uEg2K6K<((*9HYpf4JGKfZ6k$ zdcl?NMsnTLXSr6r;{0Ntd+_TwO%0C|+#lsP{g<%O^8AypZ{qQ%e2L7liz~dEAJy+v z4Xc`H_3#%%O19UDBgYT*g&+REh4aj#cgdZHa@XiD*lx#jt=^vFbAz6V$(l8(sP zvOT{MvSjJ92TT)Z1@^F=>Yj7t->nkySNd@-M<;XYbnDN2%*p^t7k`!+K8yHMU({kS zHF?KYnZmxl4O@!Z;~f;5nD;z*^n~yKl&q4z%+@tknG*coj~`^e=Hl14XJKVMaNV4% zaAxR>S+SGL+OBH<-65Oac9`Ao(4~{i7njbJm>75DN|(K&#&veDgyIJVXN05;_3oPV zzI&ze!z%K?MQZ`YM!SN|+nc-V)z;b0opf%!w%e_M`7ij^uh2Kq3 z9}T~Hef0>-YQ=Z2oOwDkMDzpXip1(IC40k~AH}D{^iG|~7xU$dQ-%`f|1&x%OP4E} z>alElzUD#MMp@w^q5sXT20vd)$Q0c&R2I5_=~?2wpEhs){i%QWzg~~=`cjvbwO#H8 z(%KTW5p72p)Gc=XR8TMwaAcgv(RJ1-^S92Fr04s$^TgHvc5emTY=hK-?na?ZFf@D?~X`M~*8CtTd3l$>mH6BA_{Kh2XoW4Dvzwf$f9zI6M8 zEh&%hd)X(0>fvJo;C6V^g!)Ip(N=-+6Z$@X+}P1=-`{P@e__8;iZrKr@4BLE;ur3R zG@V&uapb6~;@S^ZQSIRig{EvZCn33c9cBzPpTKa-7|FX|$dTe-AmT=T>P1Qzm z^VL}wE?BmR@^U6?{$%B`UE@~gWpi|P!)Mm(9|{88F7@7dreoDT`Qh_Pjd@$+lrQ=$ zQoqj2&UfU|zg0oMW_Z1Qx1UYR>v5T@=B&6g4$Bt4R^p9%(qps2@m_1ikt0VQA39oe z=l$$Mvm#_Xel9keAp30IUFWY$qwZ91b@{$$t@_Ee=NG>(GrS~x(rwWnSMv`EA#P8X zPk60eak+?f>GfTP`xf1bJ^p34n_cx^yPx@7KsZIeXhK{bIlLoA;7;l@3iz?dxbOcVAwT!_ttpEw zAFBL&RVB#puHnC0|6)L3_wBonCrk|7@@>)Y>G6-_-l|TyU)v^Wx^$)Z_J}1q{97Jn zr3nc=dL%GEs^Qk}+K5F3i|(FyURAh$i%hS+xI%FD?SpMaoXPyB1MBvSZ#IgW@%EJQ z)t}3RlKa;$w$Nq#sOh@n@03r?jsj+mVPFy@!k=g0a5yFY2Y+MT}PkoAA?@ayd3pmFe<0=qAN@xNtz zU8y$l+B|=MLF?B}6Y|<8?)w`$<+y{*rSx~)|I3f8dbi6X`}wR5T^y_DZ}V`EkvsZV z#(s{SMANRstzTLkExsNSj_KTV#PGZpkLH!2ALW-XaK_v<-Sr_)>h?dO{Po@SyZ_z` zuluF`XZ!zYf6DKxJEm6V&-(H{caKufkKpt1<@ftEuD9oGRF!tGo~8LOX>w=u;VSw0 zJAZJn%C#q!{S%AR$rAls7PI@&giDu~e)OyD{KHxQYh}2b){)!!4AT_s|5k5XFD>#Q zsOOUH?eAXKUzYz$KE7?z<-Y-Y);Lf4To;jEmM~AOBuf4Gg}FQx^NpOdZmV2d`fZVK zdH!8PaA4d%3<`{PXAXbSXTS0mDjZ<$ySLH9$J@OuO-e{v>7nGLhSUAamgXy%Y4rT( z;8?hK;gLg|qf~xa9W+&u@yv`o;pgv|w21$Yv)+P-KW&~YnO?u$`eBme5(($@P@X+= zVg#pE$i2K7dBUgHsUm?v$>{xqv#;hY*9F;q_|G#?LH=J+aQDI46MRb++v!Vw41CzZ z(eL`{Xm*CIn7$NuzVr9LjRO9n3)*tluwG4ByyFB%i~EsBHHo&nmZeOPi}-UcS7oZB zZTrD%+9y0ZmYE4Op7>!h<<6^vJr?X+wtkQ0fVAdbgIaU{H*_Q`SUrAQRI}%??jy}k z#^v=g&&&G{3VFFt;AscVVMsjN-a~^)1MvI_edZ^qVAcQUDaG8;(X(&dDXuN{j=8>Z z=k5IDxBaQp z-F95qxlX=u-TEBKPrsQLZhhE(>uOo-|4-Z0d;RAp{ki`1`E~i^{|p}6M8BpScwc@* z?dkmsHX2{9SYKDQb^ffjeffP>a9Q2y#=F&P^KYrA#o5lhS9b}WDiysl%l4<3thev? z%rEQyN^$&NscE0ZTsL1RGoUiM?&`fy^<^5iy&Hbdt4geU%psVZ;B>1wYH6F>m5q%1 z<7Ho+mNI1fbE`H|-f&((^#9|zixwI>uI)X(;J-XG?^f-tjj!`>wz&1&p1vw3(hCpi3O#@Cdof{= z?*4<-c8V!!2|LBjiuq_c?&mP#jk8?EWSyWBSM2;ZDX zsg^>_lL9N!Ik;B6s(+Pd`c+=1B1-_dQX%q-XzKAT0-e2-qw3{9(zBst=4+06k^|#F+W<{KTzgv zH@B(3;Wodrf9%IZ6F+@j`FzE0iJleN%mD#W|7YxVm3vwdJh^4T5$_!r^{Wm)myiF? z;o+ZZQslEORq2AhK&pS5;_vh?Z{vBka{RPlySL}qXU@H@JX8HQTeV)?$7eJD?gTTv z6+43u&pAH(w)lgm(yg<^y8|w|)-ip!eGF83MG7?kmwDGV`|*{!a*=PVme}Ma@*Z9r z`N6ta%1_XBMNU+Je1##)gT>m+x+vwyg{_+y6Z z$Nj9k@)UGFM>}4b#nydzt;gnbuEqCtIs;a1w|)46DTP0&@SS!si&%E6UgaCE&#xV8 z!?tW&nayEn+jaS&sf57KpKod-lteOS*TW1cqWm#n>m!@$LH`3Ctn`Av}?z*d}krYLs8tm9Jb%gBUH^7`fE!1 z>z_HU=;rbCLhX`@x_)Iz+X*YbE&IBW!*o~Ay3fmg+*4t&?0>4{%y!7oUR?3t!nkF1 z54-aJSMi)YD|~C?v+`^DOK$#fHRbwzUU|QJ&bQ8=ajRLwe!pL6b(VkW-{^?tADvM6B`t1w8nXtcNtKYEk!S{C1%-v=O+2$kl-c7=5{MZ+~*?Qxrcpe`=w^L%R znppXBjh>R;C(BFv{r>ddbML!bC*!@V|EQ^;;8gF|TaUk;*|9YD?RSgB*>C-FF626{ zopJoe>*p_I;~JcnE{xXrCwu>H-u0F*3rosdg~ii8D(JJAE$iH&&zY||;fX%)s`op# zb5D@1|C?~BVQF_gd-Jhdg|kn&`ta>J(*4hKO~+i*yI=P*-+cEv|M1J*+rRI*zfJxY zqe;biKWj6Y=h3&{f2;q!TY#tf{gv&PZ&%eGfB8Ig{lBks9~!;q^nCgLh_gi3^W3_7 zQQz+7Ot!!2-0}Zq`qK^fr?*J!Ki?Wx?J`m2pZwhRyNc>A;d@$^?Ef;S&f4(GwJ)## z-&OPdcKfp3Hp39J%=CyYGiFA26dJUa)-boJ^z+}o5+JSYzxT_DXYy;JvzG7P^Rj=b znu<5?;BO}(zxu8AW`DW;X`66v#En#DZ|Ca*KQBA(5Vbx2>Yioj zl)#AWjglMF*dI)~*<=3tP_qB8s=VumO8pypJNcfp$+UakD?VZG^hbKj${ky_HqFyv zm)YF!a?x1Rxhk*xzez;-ZL=xA?r*y-8&IEfTQsNt^0b2iu@f(A&sVH>{`)RB=>50l zbN8j}Fn+tm-8u5&uiWc5@9lK`zl-bBO~)^G?~h;EBqZe({DQyv>EaK!3v+(IUw3(X zZc6#Ds&}@z5AG_x>JM(aeQ(?T0`IpsJGwu)Ju@zR9=-j;7N$%+GYy%Bb?1X`nq)`J ze0Th_IP;9Dnf?EAZvVJjQr7RuHfzPTiO1#t#x6SF2~LO#x4v%s!GC0`BkzVkOunr1 zEH>8L*Zp`Xk~2s4p_Qv3s?&&EDFFuOBnI@8_Aa{rm+5z2gbLMOA+8Jb3mf@4I<% zAHIHVS?w>Pcjb$tnzVbkv)ZPtdC7hoJHJk4+#Jo4EboRxoZf{LQod)7_GGWVo8muAJuVR|`18iP>WMduensgi+a2INr(_>@@59H>ZQaQ{MXTZiYK$wcRj|$s zzh$U6`)IfHPY;i^N3*pymt5)0+2wkv@MWp+rpKEL^CLRsc&9j*2NWrC#I4gS_}1a@ z>}>c9=Z=nOK22asBR#y|^}3Z@Oy7db6Quk@WS= zN)KPYVo|@&;PTvLL3Na(uTb>tDecpFPxyamU(UVsrDV&F)438cLEg>(+KS}PYfTC) z`>rT=Z%5M<2Jst~#}|D%X>#qF+r=O$_I!7Ciyuc8uGq=%JtlD@R^yWP5z3?WxVxP<3v|PFM+RYD=D<$gxpH7^!aYN(kc6BcK zp2W^in{*wv9o_#oQQReVU*oaYGdW7Pwl?S6aJ~DIG*7H*(Vr`qHvHJd8ZN~A@aZEC z_VXSo*KCi@IT!Dt zs}uTjch18X&-!0@mm4~Ws^3}~#&E*mVx_2Lpk2X2tHSTTOa58%?c8>dW2a};UTO2< z#SfhS9=>SLxpnGFjt$@2U#oj{pI?8I#}SrJFilS{zJbHM+9VFUiyDwr0lW3r$kyVSv+|1rtiTS z=?bgedRtQ!SzP{V9Fuz(c_6R$Og?=>bS)u#?9@panA(bVx^2V zi_0V@1-9pPxu4h?o8*(VJS1G0KX9%npZuo%lZyp{R_)gCn>f#-oYDW(r+~OZep}nz z6+WL9e!MLB&r@~B|FqVkwcgdaV*2l&1xJ5x+1#vMv-nfNNGp3rWs$JB^54uqg$=^Jw^MbSCj@R5 z+a~U3`k$*;T={`t?pb9OVauJTz9Q{Z-eTpJn;g zMadCvcB?D=9{q1gW+{?>x3Ke4!RD>a;U$cD-_9h?)k^c;u=L=k0QObq+Yiisb@ZUJ zjJTGS;aZWOx0`ATih%QR%=%dziSkFFYW$wSUT&*A;fZc*?yVuY7aPq~BHf`#*tauB8D} zR(tr~U7q>ssJ4{l^?AuNPc_cW zGnr@`oE4Yk>gB)0`})-4iei0f!<)J<;=>#aYuLp%dtJ2I;<35)#DB4CYVwQwe$AV& zeKv8O9EaChx91$a&y~DWmOP!O^+Zhc!LQSyE6-eFp0(BSRrpbh`S%+oO|Py@V@>Aw zp76r|^YqEwe->{FxNXb3Q)OGG&V*0S*KP$lTUPO_ax#7>5PRD1@U1spVsfy*VDGG` z_lG`5YxI3?-);PrSBjlGQ!Zl4t$RGXq(40TB#v$`G&JbAl-J8yF2EOnME^>KF`19{H!pVKQ?yz%z!>;AJoCvmM`-2UqE-rPAqT|vE8d+tS! zuiyUpez{|dU9&mAp7NcK9Cf$q*&J_9U6H%1f8I&cw+GaE4R#;>Kgq&+msziRCF6$e zUtDw2G_$f!2Tpj$Uu~U!#B|RA9oM}J5CWAM^E3j7Ip||`Gj~`a!IJMU4?Od}xGn55w?)KjDNS|@r zRrzxfYu2i+0>wju*Ul}}cr&lP%DMIBHI|zHXFcbrZ43(t?0a=f>Q0qa7pvl}QsKkT zj&W7378ZE&rm>@E^@{bH?#rHY*8E=ozwf<{2S@3-3lUeEPZbo`B+qNp`)PIWip$}L zGgS4yoKQIHyeOi}up}k=;ewy6HZvwA7p`FB%+urWI(KO5E~}8ZeH||Ll0trbJUx0 zx>g19=9yf1locj&=lr~u+#KCD6A>@R$FZr9RtXJ}z4`0o{ zsc3Zv#D@3#%_@01<$A88#vASWDNN6k?>~;-t&tMDcCjm;?x&{Q#Hw4umcGpbyBsX< zG`2OlXoYiXy*e_( zBsJ)#=KXM^=7fCdjyucDBLuhZ3IDTxo09znp8x+Q+fFs*7Yz-` z-|i}ZV~O2`RibJX!mlh?_sU4m<<~*|MQr*<%Ra05?lZX==X1)TOz!K%UD_Jn zA6t8!d9-#dTk87hna)x9v|i(Hbr$tIxlFIGdzdvz``n-2R~q}fT~6`5Q@iot1!uX4 zow&PWON!ukTi3!5kurt*diIIlRdRna{mAD4XO4aUz6qRTyP&l6AKSXX`Cj}R4;2cV zKe3aTe$G1oRNO+fd+h5P4L!71J3Hq}ep+PnaAFDTcJ?jj6Q<-#O_YAfDgNJQO8C`9 z%Z&I98yh=3ced3Pwl#>wqzbZs%y*xg(X*>-msQqh@y8ERi`c$By#BFjrWnJN^|>E6 zyKdcSS1B&ZDEFb?*U|H?>6Oq49gFrmcfWhIRX}uha0bVgLmTU@`POr{uGr<6xNgaz z`~PItIO#q7*K^{g5u5d@iE{H>{%n%Ea&3x&_8soNd7e8AmBeIcM@a7O?ATx5+pNBK zVpweVY;)K7oULaygDswxCArKf`ugwOx&I9%dbz@n%Qp$WeAoNn$>}Th*#VJLIvCa$ zq=eZ|H*+-+e!D{|jgLFKK1O4m$|2Y5u2ix(x+!yTj^nCrtmeN{zP+5UH zeD&*<|B40Q%6C{qsqjtQYnDEL;kPDbo!G+@!~OogE8;8q)OFEhMN^bciOWvc|F4d1nawxho<-uOP(GU%4)jNsgyb7soc2e`!cZJDrOvYZpv`R`-IsQ3+1^y{HPOUw%9AIG^Tj-k zU+;3AU{arv(NWh?ldo;TuJ$O%Rac8!U&(a8plxu{W7de8kQR^pe3nZ|>)W2So!V5= zQo*LU`jgpgm)rVXJ!->$6MjVkN($Iv~Gz$ zcG+9b!1VKR-{j+UlWuR^w6E`{x@&=Tfrwn?+Zh6@%Zg^NGGD=O+r=#Qys)}JcGlDA z4|ZA1+k1XrYS8WTT(f2AfxG8qH}YOCan|SBD_d}Br2unQu9d{TUY`8yhNMvBP;gRLDk>#AA3`ovf_2TnYpGUXLwsQzJIf6%j8{am`|qrA6EZr z=eEQ{^Tg`Y1=i2`<}Z2|`+VKjm3j{g8>M~LI$hkcLff9R=Xv;{gsKxofWJ$0y3y9lH60?MruW(A?XVXS^q7ikzRAozCTcUue%X6ZTk{ zwT^3=m3>dWy_L+LZM&j(^YVjc%>5ekdOLS(d^9|G^M;vE*WLJ2FJC?IonwBN{re_6 zowYoV<0h&0{hs>ZBafultaM4a>=)N#%x@R8^Q!JD&M7!_ zN!GG|>v{VnRbrkQ%Ps^;v`lutTk!Fs(h;@J6~;#|iL|9p4LDc2Sm}ewzTyR}S5{0| z^2BVqVVODKv-|Vh{=7|Gvts#0xl%5B+3&87&zn@PnQW@=*!-I@er|{3b?BD(hbnIG~ zx^k{hY09G$idpZOZ;9!>^87z>qd>Hg(0@MnzDGF~yj4p-OO`w@VeZ^iyrDnufJAKI zS-uBPyPr$kd&eHFUtoMr%HDF?g8sb77g~-V_Pu`?VyUjnwnE8sMt@ROsqE{J%G>>C zTRCE9zdk&9UErDA>A9QvS3Sl&d*E&-p`d zrPgABIU#NjU2BUs*m2yis~3uEHTid9E9dEz=jWLJ<#DcKGrSEb{)QF%2>Ksi7oeM+PftRH!kvN#-<#YD!zitvTZ#- z`+^@Q*O>jE;aa{={o8+mhF90QC+&5a>s=db*X5ph&EoaEw%v~x>ACLPzTuAo=e27S zPh=~n?%&yQTX37jukvh_dr}vAdk;AHYMh&OTk*@hIltz~7(4H+xMb}-?Sj?K1k)xz znWpHc0@qd)uj>hn-1m=9Jw|U)rIXD`F2!2hMK$!Dnxgx?p3x zwvM9o!LuEQf1KOM;GdqcXV%XC6VAsU&cEAexw13I>FQkGCAozbPpmueGo$tWjaId}7=OwxX3*S0|kBOUMcQt?7Mfk^h%&U(09F zj|wX8b6Q-UW3fzHZtCXT6DI%J+?}*OPHB@ndbz|x=AYd1X04Ao&-X~D_C4!(72qCG z{d`eqmcSz2tPA0y?F`lVF|#9&-&t(*W9BTSKj&nYw7-vDEs^Kbc9l_G9@ULaz8i4bz|zb)H}Uu7mo9ZH&)WGM+_-<9 zW6bFpWimHI63cS9x{Fqs9MzukGwOidBduL0#cple-nY+5f5)$*kG$q@TdIG@+i^#} zf}Ur((EVMl>%2QG9`_W@GT0W)>hY&PhX44#!}Bz%_Bn_JC;fX^Df8>4hK~M)A8Mge zhT+{8l-VE5{omlV*rk3N>zUX?ftCuUJf}_@WI3;W@L*DJ*;{W9>twCZncxW|F&Aa? zl4tc?YsK#-IyEO9-7w>y*%y``lQr);{y)}~jfjkOir>l3%eHZc-HN%ZmTvwLEp@^= zD535MU+4Dw4w~7{Co;1umTXIie3dMC)BUzU?-GyuTrUHKvbmWHCVg3! z#^)@0&!R#}=6c(6!`m~v9{)Z&@s82DzbeyhncH@xJ9|DYS>||tdxZMGlf3bcTe@ZU zaLvnNPqqDcU-6Xdq1CHxB%b%NE-I^-`)Ka*)4L4jSP3-zVwB|k#FrxToA3VoEzP^@ z1(=wb1qEJoJ6sR`aeDtQrRAOPh(e#wk~=r6t%tc=XFsI`x=pZrN-9BOOO26yVbAT;<&Sg?CqB8pM_s^ z*!Iq^;l60K?(GZdSAro`bK0T{IIEU;PRxtSb=}s_dX6pLasCdIyB#^=!4WKG8RE>7 zel5?c2KQUsWs(_XY4x zf2VNqs(9R%36r1Q>a8pVsAgrxhw5(&AJ<%auj z^?IjV-XDCskekb~;R+vohFYZhcOuzrn3) zyG3C^_M?bdA42qOTo1mK|MAJga{CsmR)Ta-U(3xUh76 z$-E2D6=rG&)$Gat(HR~@&Y`oz5cgn4chUP6x zLm!^gj*{^%d?`C~$)Eg-i_+fJ+4RX59ylsj_{8mb$mxoSrF_S8k_y-QyGq$JZ?nIt zuyQ(YvC3-`VczKN;s(xF+8gj#9cjr^NJ#OnYsX;hF{i z6snuE6VnS%EEj6#3fyTF)u8g=NTT}_o0)A%Uq!hxA12A(cX{ZV6e7OJ%KZ5Ssr0U5 zzmq%6{9>nz>An~J|L(eh^G80@)I)z~yf}XH#M=6vhoNg7?{8xc)p z`r=RC7cy6M&$z7Z3p;fC3&TG9Jnr`o5_0Rg7kxbPCi~BEOL-OZ?5#%vYIT@wYbIP> zY`f1jcK#i!wpqr4C8y-3$K?mjzr0m*ldVEbkd*F|Nm{$p?VKGyo2X#iR&DdwVF-~Z$M zUyqOTwd5T29vw{;moYf)JUg%3xud@D{xJv5H~sTl>r2h2Z43LjONnjXx3U7>4mY0? zi9K&+1!qLI&#M$%FLe3sQ4a;f#FwItKW=!iR9GEUwqU;AzSTN1%lXm$nQDzUswHAB zJ@e(+AZ*TDcuST0dK}l&1qVK?<*B&2^TGnL&K)}>epuZ&XuXwd?q!pQFZ^SIL{$E5 zjxF#0zw`JGsjR-u|F#<_x2#yHi|1?5t~vXT}X@AGBq-imz&+CG;b z%;V9@A)bEM_K={kFl)WKb%o2bNB1_AgwwCx6nco0)OMRp`mHgS+njm|N6gA2Oq^QnHcVUvXt&eE8Xtc<=;7z< zE^QSEkqX{U~=z zsa*ffvt`5FXR5wW>^|_WpCV>|>LB|HOL?CE4F*T%`n!cS+0W^Fe_V03iplxl$n8@? z&Z$jFJ+7#7LOyQZy$&D!qlY;~Ll(&``SL&M8P{YF2NriFEltsr&zTc;zh7@5 z6wos*((BBEAKMRR{uiVA>!zost; z=`451njk6L@!*5Rk)=x%G%s?i+1E4&cW(b!^-$8wb4jJ@UAOg1I*w>Kel;uSF6_If z^6&Q6NArVZ_D|=U9xTS8baCsQdeO*tVjuQd>Ah8WcO!)5Zr#K0E7aG0$+Um#UeqP| z<%!JE!rdx&CuuZKbIme!MyK26Z10Mc5P?fXZ^bGMGv>f zrLD~sTIorN%a1+8OITo0;_u!qlkEB3$3GHQzaP4(WT ziXX)b>;k3JW0;;QF6E1L_7*AQ>s@%wU~A9ut%)nIc6e;OQz^J_pUNp|v5Ym5hZ3qa zg_vp{$O|@qUMbn}b^Gy!f87hUc0XVHLtOul*tdkpnp^!H>E6CortHi4XDL5(+rPzg zgYLhh+HtdEH48S{wNLg-yrNffA$}jOZVcS zqxws-Pi<7s@Zes`8m*C}IX|-V;0KFh0sFbVZkKg*H@Ur2I{iaqJ8pP47+MIW!Jm-f0WBp~&IoxfbaW!WXAsu0Cmku4#5r~dc-uz%BV$9?mz z<;71zROXyMQ*cX3LiD_7)W`kj1viG~AKkC9|JPMfm)&y@B+hnFS*rH_%hr}1EGcn& z`_5@zh0&0tv=ZCgMsU(N!9vTC#$cY8RGdXeyS+! ze`lF?#652kuSyHs1r3ACf&G&{3v%#&YntvZkYtqi_^Qp-;~9oAzCHzKPRbW%p4|0M zti5H4%AEbT+SU~Yy3N{JxAI56_@n#tj{S0cJ6Sz2Wslp+yP7O%CX46)YD{}&r^jk} z;N(5oM3Z?(e{_Fe^JwCAE48b0x7qBzZ4N8&R|x+J`lw&_FZJ|`^&bxPw<`tMub8`Q zseie^eA~OzST@V+PAK@zd(AvNbK-=S9UCN;h)#RxUz`j;72a|uB}`?PeS0o`<6%XRl;3!1pb@1{5Y@jQUBB80~gM!9C1@u zwrqc-tLt{N_qfEwdl|ybo%4K`Xz+CWUz)x{EVE7dTk8wq#v=|2>sCKE2zb@S+};#u zZ`FU(J=0>}d9jt>?N$DHT5L~IGPRQ7oT<8_Xeozk>DvlR<+ziVc9tKpw>-*_m%iih zC0$ux-<>8uEfv$pNuA^hzAe3wIe71vy9sW*@5 z_+PW7_f_YA^_Ni}>#O@*a#at>$~*|aqs;m3?Kg$3iF0o+Iyz0|U#|Cs{qBFP_bUIJ z9PmFY?zY{xFW+My{a>~3$9vbx73MX{uMHQ?bS++GeQu6WdUJeYoTiZU;m0=qe^TZ( z?A|PuduP&OJF~A|RSPeC{lOR^{xzvdtT`!Zy4vbqPZQVK4_AbWe0tBH>}O?W*gJdQ zvJDEe?+Ei(^K{i~&-tW%tk_7>Q!2MgJ9fvsHyw-PrY=ZKT%P;-c4qB9>8bPD3-4%( zminu2^kTca=vr`2vCy=@>mQ3=nrg}PAF!Fz^8VXI#>6vIckg)ci1+;4x%Gu}TZ*@B zS3PrAIBGzv=iPmzUaqEgMXZ^gqiL1!%X#Vr zSAGjwv56_H3N*dk#(gd6`G3tL7o`>dDSwk~O26s-GBwuo&*a(ritfF)LN*619neipl{?TH) z@;PyLJ66wHs%Y}#?cv1rLVIFFwyORqOff|2_^>xEa-gXZ7A{cTI0v(SIXd;3mQI)3#0DSG|7p~oZpA6snoyyt&goc;E_ z;D2|IKlArnZ&Ci|_-Ox;cPdQA@16I&(&(F+rs}S2rO2K7G*v@uc%*?r>X3JAN~cU|Q} z>yP)k-VPT$jagE~9~C9qeLAs!#TzTDI6copJ{|vq>Sy`KiD*v({y(u{0=l|!jiNUR?rU`%VA3SnTiapl* zgWJNnDdwWW8K*ZKv#Cd?g0Nk`3VaR zdGzk-d0Z9$uM~7u#Y&&`!4*dKC1(B!z7k2^Z5zY+3zz3gtp2QDpnHwSK4RjH+O=!t zgH&Fu=5Idv{h7xebFbXyTR~4e|EQGx`+9W$@yH7H_^Y~{NbYCi82Le z4l&P?JaS=OWL?`V4R0B1xhCnyTsr$!xIVclpD?+E+xgLMwG%5f{ihyG|M+!h$Q{XP zllMNqC{ZPKf9LbJg8wrM3V7Wg`7UH}zaWs>Z~RPf$EToWy6r!{%)cJ{;#EbH;m1vf zYs9@g4CJ?0hX1ja`{dXv@c&AkyVT;3kNe|Jypw5n+Q)Qr);pek>;4^eQF`su_SVte z<;|_1>L2AF@ys+4iHpbw9@K{#TV5@&@i9e;G6~i>|PzyMI`;m3Mvk zm57-Y9aECy4^MHq(Y3?NQ-Ftzt>D3?)Ik(MtO6~ zXs&-fqb`Kuy)*lRIrlqPnrb~e{CR^-UR%SW<1dbMoY+UzE&qiLvDP1)7r`-mt z*DXubUhP-nSi05Djr**7ZiOb-mJ7W`_Q5h+Z|MGWkM?kp|8{KZ++=&F6@g|`Hdn+x z>PdcXGx6@4>gO%D8YbP!^uEo#XQr6@kF-MLBV1}?h3`A+<@2q7++Sw+%9GK_=lkK0 zr*=GHzbY~>zVpDzzwCud!Mi`}pZn8Z``Oly{Q^@|RB!jkkCtA4`UCB?98O!D^l;UF zq#^hGt?~aZ@f~k;`!-*cuw8Ls>6&9T?}cq%zB@dnt;PH-Yx=inrM0C|=3?g_x#rrO z@t2)=?vM5gzbW4N7wR?(${s#_Y5T(NZ+cVX_qH@f)z7g>>%Nh_exru#f@7Cfr`>s3 z)6BV(M}M>IinZ>0l>aUMd*?y8*113Yi@JXOJy`tl#e{nk1#36+z3glM$lUpVnWf^t z_X?NaDfGqMQ2wXAx8P9gk^f673=`yHPcDD(;92($AH)A57j{&09u3v4FxCBbSN!74 zmptCW=1cC1s~%LLzVUIk6pK#JX~#WcZR%*SD$n%Bhk0@ zw(7s+!XF1Te_a2P=obENr@HC~>xvGs=5iHAy?g&R{yQmmvFcI$tY(wL89pTkB_}J0 ze0^beF*CUN>B01hTlX3lzOD05KKl5S!<*mlcye^V9bUO&{-=e)hbH}4&U!+zI;G3_ zzqI!Y?V_U_H|KfmH(n%sNa~E;$>!t#lnZ=!_9_~!|CMRB=iI!8;4<}$6^hFbL}cr= zT6S)>W}WL0l1+9%5$ZXBg> zH@+ptx4VPYFl$Zn0!!X?_7`m~@ABck5UO)oDR1(vWdi?y^HnY=cz(m*i0}Po=TCYc z|8pHJQx}*}D4*|gjqUTprw>_nFEEMG+uQTUyH!JDSJP~NHbp*>oU?mfylYusMX_&K zB9?l&*Zzz?WcR`Xx4ov_)`==vbwvl4iboZ)^gPbv>OB3oOhc}s|A%~*)2W&Ek8gKa zM=S^o@_F$0>d6lqOIyPoR31HAEb!<#gLFT~ldZiTP6^K6gr0IHe)#yoy(Eb#X>pQ( z``;Wlv6C)+-7ynGh_aDP+Y!``yAg*y`kA8%dvsPK_Nix3TIt3p(gip0;+<&5ejQeYl{Ve0Ag#4GC2R>vR zIofvc_FXo zLhr*h)S?1K-txKg+qbxMx*vJi(Z}JK^HKSW_k7*!?Lyu23yw7AXyz`TyqGg}-9dJ- zZ(<$07vFt(b6xtxMNMMSQw7&;*jC^eP@?ll^W1?I981E!TuNEA=;RBrpuIW?Zv`7( z{IGCotvr178mppYxuM2#&a7OK?KO1==6jqlO9jQ?k zdfWG?A>5Gn^pj=pmG1vG*k}2!spAcg%5it)8+ZApO^e$R@ZIV6$BnPAJX@!J`1++; zVnT);z5OmT`nhxN9-3HKE}`wTBhPQ)y>$71XVsFQ-t&~&+WG01 zUe}?$58engoR|@@>b67+%Q5BGuWWfdAMF?Z>DT`8?@OCKLe}w0LPGzYdo>My?^f=b z-#xdEcUp#_L{*kN-%P7R>IQPpq5tBftO9%>6SDy)QENoqecZ>9URU z#O1a!HAk)awR%jJ9=iXEWlgQ}#3>Vd|4foOvLoW)s}z=ZIzj(uzjBP7qs??AaNZ7$ zmP+eG_nzCVyZB_aNUqNoYegqBk&0Mxe(BUwIRYM87ufwp9GE;j@!R1ka<#$%2AH?LSOj zxvN-&s+7$aWj}D|3g11&j~4Ue8V>!*X}R^MmnFz{UU2li8T;C8?1kqTZEOuMVKOS} z4wxd$*qy&tpyHw3k}!=+i{)QkwBC{O-Tcq(ME@tY(d}+qN>|pt5=dNW|26#AgY)&) z?EmM)EJCJZRII*)`?qf)CG23+;|3?=- z2848Owz%c7@y;|p_x8;Nt74Bmd!&8Gr}WUyVm^)zf2G;6hn8;1xN&RFk$>F&is89Q znJdH`|Nl&%^U=pO%uDK$u(H6LZ2sc*fG2h{CfErD**xX6`;)K z4COm^vrV*J(SQ7if{6y_Pu_mTd)1-6TNd0n&!p~uQ_(_e=B0f-d)5dD0nmOjSo|1nn-ta^#YQ?&f7iK3l ztv~-m@%O|nCX?RXtqE;W5YM)_{KzSXtFt`YX>I@EOWHh~nKMsl@9c1qm*VGReRPaR zcXy_Ym)&Rc8sXb*g?E1zPuNito7Vqc_fkQ5Ps;onmg~vP54=A+xU88`zeqs(A^+~p zVg)|yrpN8LJL83m?-*PA<>-w#Y;bu3QHf6|L-2SaN=h>kOsmV^vk0(Sqm8vfn(|t8rOk}hF`UB76 z|KV5w)XjenJ?I+)5}?c?ss?dwoPBm z9OK-j>Ho-?TchU<@4ID{hvz(V|NX&2s$NgY+?dbr_u0hM_|}!YS?^_4Im*8+|K?z} z$nw_di<}27zZ9Cpuc|#PRnEcl9Oe>K=A1{Hb{3EL)$W>kj)}T2tfil}rCj z)GH|c?mWXH&+Tc?2X5D@Sm_Ln$#*VwOS#;ZIr{zh5%&XW3tv2&P;w@H=YO?HYxy4? zpX~Rc)5opuvr2DmvBcW1hv)Cx#Zt{l6Ti>CRJcDM zeEbLVsngHec$j^|1EVT<`;S^W8Sc!}OJ1;-YkGIqhmv`pe)l!pJ@V_2^p>g575?Yh z``VaX32471G~et(Q0tl<0W54RiaKWU*Q-jktA5ol(&TzKv2w-R$yM{ut$kRy&iKro zq{4?@td~3j)bGaL`Z^H_tbS^;F#MU~?v3)r zMY;FPu5t+_e$`l$b+|jzGHlnT2XChKwM`GYetlPODbLiMk1aZH^@!PC^!}co$eDj@ z#h;_4FVyt^_FLSY5cunAr3Z(&K6jg<-flVmWvu~%yJc0{?%#2ZJ11(gKXO5%hq>en z#V*zANFMq9Do^grz1-()Wqx@^PqEmx539xH7hCqG`#wFf^Hx*h(QlJExz+rv8 z#sZgrCKLAVYdm#J>yEs_Z;#2}%IdbJUOThjt+>!gN#opt4;4=?9kAKrbSUoAvWKNx zM6X&FK9O%Z(BYDDy*#XyU&+TuFVuX z@OPF_%)Wc9g*>v8w@-MT*|>R|ctF(j))xu0G8SD9tlA&1JwvnplJW;#OSh;$kfg2Lg_yggq*|-}FUycF^Jpyo)w| zusPK1EI6}DbggockNw(XTiee&Sj~BKL#g&+Qnh}};CxvObPZhIJ{W%_>1soJvd zqOIbRfB$FPa*WGlU$&&*bjhx}K}m@Z6t(oPEy}K8)w*c5?X!B_at(7)i1ngutIC}zUmung z@@Yl?GVo^Jd}4ormSWaQesN#-FHlDFkSPhYJ*be|GQ7`2km0tVV#<3Y|mYaRfaJtQR9wPF4qbj=kDwouRNNky4&ULpY-iUX&&=>y`2x-S38KX*vo3s5eX@o#v^;AD-2Yozmi`S+GCe_;Al$$3pRijB$~D{#eF$?LPnOOeHRYH+G4fK)Qzrecuv>h4!s0VIxn~xy|H`EoQ0FYn6?ol}@uQvb z74J(*DyBv(3q6-;?@C*6XTs$>^};tRuco9=JZBZg_Wt6Wn7-YAi>;)Eqc2y4#C2rn zHqVHx?MyP)SD9vWrBFwbN;8lnZ7dG^+BqaP3o5>%dC!Yj>r3wqG#-Cd-Q0rXlBF2 zIcYrRkLP9t26SwC)y+|8)>!|QW1IaO_640A)#T)s@tt_R?6-zp?yN`4J&MoY5Bm2a zKla~I!PzUV{AIR%aX;5A`PRiL&)c-MedV+bCH+oMvp=2)-#y5G<3>oXU62URZl zl=(t^scPGmw~o=;T$~?+-|x$9iQXsA9h_axaxKIDa_sA{4~x#oo9Qv0s91D&@5N>M zrxu8}ubXqb?pGnVOWu-_rGGNZ<(JMiIXS;>`n${5PM)*9z3tY_4du$G)IV*AP0Mtj z`R?K`?&b~?&9!e$mj1Z^>-`q}y+5{p{=e;Q#`}qfIE#HQF}zD-``j>llEkz}V&7lh zx?EY2w*f8{7XU+%Wq% zFT~#0oc&|=pQBAh*8011);d?u*UP`1`Fg7Bl?C6*W2=<*grsfSs<-d0+0NSi5i4>R z?+s50YTo(H$Hn5d(w|=~SM+$M`d9Doi2og&E!-6ybN^&FU!uDv|E)dJIl+(eKL&@} zy^MBRTG6`SOt$mwhfAhj?&Y=Wa<(rvO=%OEKgB$e*>?8JcXwy=zuYy&?61iGva8#- z`FfO1eRq>@eQ&sQp8rpcH|xsOK3?;?_2*)3P~5#~mAB)wC*Ljg^3Gi2XLt1MqMDd` zSEb-5$?AE!BBCF+{Rx`0t0;H-%k5@rOLlx+ckz8tMdAK0+dZcL>wLZIcloKU$6t247!dLIb<2kN zPqyc}znpc=ZTG9De{XmBKMA}MxW4E1x2cCZOqOmq6n|UpWbyNV+c~N)`o~5E?Y^%4 zzwFkf`$04I=K3rA>Wllg{j z5i802TXM%XUS2cl&;9`u->_Hv*Q-1cTeM||gzL|Z9GqWVE1&W~yQcl>)3ysvd@FBG%Loz*Zcvhd0&haKM& zOy0iTQ-44%>VNzVzHguIS@hm~rR#QD%`tJw7M>q__22K>vv+%r)&INKTRSEt*R5ga zHPzpsAnt$a%ir$_27Ud{n}1fzm-GwdWjFht;3#Sh4p6!DsVlH*C&$m7i66dmI-KjB zE)d_pN&6lDv(VS+2Kp*fj!v9C(^~My$LQiErDtCC&EBKFu3z&!`)4PPdgtr*EgLO3 zSN5>2%f5K;|3d>SJF~U?Hy-VMDY4_e;=^(s8((5B4+bX$pTLvv-;=9vmEbSEcj^R z@(p~tf?@0*cbN6E?C0OJXU{|FSv`-I#p!hETi=Si`QN8yMu@K6;kAogT7Fd=zI|Dm zu`qJQYq|CqsTI8Uzweyz%_{8o#jvSe8ueQ0Pmh~_vN;v?dxK7#%O6Rl=iV-Fj2d6P zjyvfZv*txx1@F&v{c|(!n_poudp7^ETOni7>uN<|;jIe`&g8Qw_SGF`@;qa2@@ds` zxy6e1e2OOg6_q~~3>3x9=lLea<}lW;DxCi4)~$%cr(VDI@YZ$u{GjXG3EuAG_f@W# ztjG(ycs-_XNvM@~S$%M;I{1fw)y+CpP)!ogk1wnRHq@JT-nQES`0 zmQE7bpKT)b%s6?49O%Thw#0uKprm4Ns3f19oLuqg3Fod|y9|tsm}dI4O`R(G%y{FT zJvzEuerR%ojs-NRF9C(>{26Q4>hkdMEn2#ib?w@I}<TO5=ujhx_DWMDU ztv^rq|9E*Z@L^6m|MJG1t6h4`y=m7>WqXr}aw{WoG2J6?4$u2Alz=gE%#ZdacYo+RO*f{nlQ=Jo44 zx_w{!Ztwf;T$~?5TZOW@EK|OGzZCcXJO`=+&m8aif7O&%Ebjhc)$RX}Txa(?sA|Pu zxpQR)Z&B;dOyeI%#U4I+HyeNM;%WTfI z`11Zbo81-C^UdTwetv74e_5#h_0P-qwKs|KY@4?DS(WmudhCv5cfr(4mt2^_WTG>Ubvq1Xk7hq!|Z$!`*4S~feEQAn70~+-6;Pc^Ji6+ z;ZBWqr-cV*1$CBA<D9`!T082Z_B@3(YrAF3Sw*r%+Umk52*k(5wbtbNmh*31 zc0o?{Qs?W?UH&U~=llNXzW;P}Np0_?sCKWdeP2y2Q!f2xk9xRwlUL=$KN>bayKB~5 z__{Qd_1e|VJLV_wKW{KG>0Q;(Yq~33`(A3%M7E>qmjvBsx%?~pv~k(l_832V&7Ke9 znd>_KbXRYw3=E2Mnfd)z-~rUs^0~jWo?paPwNO``adpfAA8)0;xi-DYAJ0n6Rat_W-7Rl=JzhAV`Ou8_THKW3!=jhYKlKZ;u zab8T19-UX-y-L#mc(_9N3C|e+hhGm%uhZ=f7K^qb$jJ z@}|bB_XmDF;^O=gy5>TXquFPvwO6N4nCJ1+qF&IHpWV>?Q2f>}QfEyiKNwqfPJaI8 zp|DhI&+6U^qg`FTSB=!FW(O92w0V>)G$F2pc`kEzgtFoKh`9$3ERGJcn`LbsI8(5l zTatB$xr~vf3<)Vbx^=pP# z=P(>MUS4=kmto>Z!48i7EyvWEUu0`Lcg?qr`RgR`>M4shcQlLmy@Rh^c}+GcZV_)i zQ1U->$I&ze%R91q)6_P6^mmAg5omJy^B}igs@-?b#FEdVVjd4pKaYDDmB;eNXU)TB zZ`%X%*>6-HU$~}LfZLlnL)*LI^UK2)CD$159TfD~UnPBa^642b_cfN}SI>yHI;4Mu z``x*7A1vcL+mpF+*7ZIvbTv7%&z`6He?*IWi-O{EiLMdG?|Ea!^nS0>G zV(HF(okQ=-Il7gLaaJKEQN9R4Wp7Ctp#!Kj=08i&-n4| z;lBwov$MF2f3t{g{AQ6X*>&igrG|N^MD97ahRMuMr+#l(Hp|JsR_IKf74PvMS~imL z4L+M2uWCkRw8$Lrci%hVX9mmZPmw-f_8oa}#769h`{H*SRndy9^J4%1-B131F?hqb zE(aGE7A57!6DCDBTJH_MwEVBe5%m{q{2o>QE)AI`_An?+CFY!5d+se?o7r*4Z941q zi;G>tZ4dtVC3fwI%PXOpwne%%)*8uU(T&nve}os_j*T2ymNSaG~&mvii4;1IVLLR zZLlk3DVw?dV~tzKe-okqVjViw9f>>HioCq->|2&^*HhHHBCQwqkAt7PW%^aaIYoU3 zp1!-f{n7n9vYNWa5tnt6p9D%W`9~dm_DJ~5i764YDq56;{y+btb-~i0>6}g!hm1Av z^9345jviM~(_$-petAMr1$(UX=ZN?hM_h&eKWk*okPKvyGEF(JJ>gNU2>Y2B0g357 zJ6?FW99LJe`zNIJYF<)et&o3q%a3xmLvIyN`aNPz{@B-;Q^~geh&8vklE53cZm#7T zF$r#AE`Or2P9y;bn}o9Sr=CB{O_`Q)2& zPvzQF8h1T>rt@Q#TzhGj^1Sm>0{@Ftr{u3wJor>#-{vm2mxl|_e82Bp*df_>yX|ky z$M?me{qE`?pH;Es_}^04v`6?a_oKC-aqZ-oN?W{4Oq zWqQ4tKR_zAcXOYi#_L(r;-uncy*d7Qp57N8U8lWfed}J?KAPY1|I;56x45!LClBbP zbgA-~A3ONxMcs`jcE@J#G}TDmoqH0k3i-#}7 zL?!>)^nxXsXov-(!`W=e}-r3JP z^0LRzhx?=bKY^Wm?u7>ya>xpIPdIPq8rh_u^JcH0!1T7BEi6aae<`l~r1&dsYQX9E zg^D+M-Ro_5jGuFe+^Onul;r$=O}nFTWBb}_jY*e`CJKo+O=t;;c{!sg%Chfx_~F7= z6D*bTc)ZFcX zNA~|n&6*$a6SX&5Txwp{{jbCCq-(N#Z1l~)Myn%ubaeY3T)ZlBg!QMQh7#}7wR1C8 zo;dPUdTxd7;scMmqhfsQ`Y%=Qoe>f0b${LNU1~>aWVw?6K6owNw`KMAi#5BYOjTWX z{LEt2`l%9kPuKC(&zLJ;&N!`$IrilAZjGMZ4O?EXH{7mu{L&ZiJNstzwCvgaVs2cM zkKXY|FM{vvnA5W&q5cPZo08sUneWx=2K$xTWA-)8eD_+zle2Zbzr?xe_YQ96lr$45 zmZ&L_t?YdG;rmC|fD|RYxa{MvSB9QC&hsm3y^^XSw`l+IrJuT&%WHRUmZ%D8_tu=q z@#(XBLWPqC+u_ZuGu;5 zLwJ||6``c4nr=PLidK=Z8pi2AqH9iyE!Er?ll|L!Us#IB@mXg2O7BdM-v0bOXx1%# z#^~!yHES7X{3wz8$mw#)HmYTQM{(zcXU(7X7x%?4as7UQ$z_W7kF~)&MK^`+a{sZP z8?9(R-d!)NvMKzi`Fp8Pmp3{k9tma;?Eg~Y;PhNw;QH4?@9!+%onHD}F6nmspM9_G z-rnDB=RE&VM|Jr>HJ7+07DxB~`pEg>p6vhF>#nzceSi7WbNA0F$JS-vdTsqv{QH|v zw|UX5J1_SCjD8zAt#}Es|NHH9pf}Y)+cP@<>cQ6og1RWsQmEyJyo`*Cw>NiGbo9mh z_w{SF`UF;-d7fBUI1y?qPa^34sUsX8?aj>Ej`zzyJ~>(aQZU2JACI(NKGvIgzVXK2 z@RcD@n|>Zn2C3%ZP}C}0#G18LN~*W*=NXAr?BGt;40|b1r{jFXiS7y}1%oH&4M2U1 z!v^!2Y0+Ig!!B6%H$6$iA7V*QBB*zBCV$#l-^15Iw+6N)N*o5Y^JbO{YztX=Wyaju z$De%RS(a8;tG0ak(l1w~Ot0&LLdxKbJ18Tk-*dcC6ub1$izgysVd09CPuG`W6&Na{F`+Jewf#S3V|%YZ`KuK7zwG4P?V;#bai?EzpnqQ z|L6ApztaEy-v1x_%huL5_LFhR`8YebKc_waoWE7ux^i*U$Ndu1JEy+$%*oMnuG}o4 zebO-EY~SHM)0Nd0c`M9bY8@Vz_%umiS!~UQ2@hItUamj-qt5-zeYuuXD}LN>a@*Sy zZmzFvugt2s@}-Bbw&Um3k|(yCRWcp`#RHEsC?)MTXz}*&I8gup{r^~@e@q`)6iiJ+ zA@0!mcXHy5HaAY!UfyMMls4Mzahv{PR$g+Qvt8@f>ZuXBp2rt`XY85(@xUu(hrhp! z(rk1M>fISlrzxD~K0Kw^FzkEY@5#5d|CyU_->@+5z~xQc9o!$?9TE(hn9e`Gb(wQb zM~gY{^j1hJ>~8@L0B}3+xV=69@r{kiKj#1cT<_K0aCy1E_w#?Ppm6EwsxOL`);e)Y z*Wjr{)6=+{=eYMvYcofjZ*dujiy0+WP-?xHjlDoXgG4EcTkb@ho*tW;55J_xb4Pkp4MvlM|H`M zarl_y)y7$NnL5D=3rG|CQlppHbSI%i$q{r;-$QSXU=g6hQ zf;l-W7v}72I+{?e6Sv|(QL5xLE$;{WDp+{vYfnmun+5JC5vWSABLjcZ0x`hNZWe1M>D%`*gy~5>O|=Yu>Q*M_j|oo0gg1Ym2I$g}X1Y_IseJYxh^lawncC0hTZRe zx{qy{lGxN7kyzgsUr?>6x|9jsYlUHrH z+-HCDYjf#pVYaI4TXG%k&&av_k-z9xzx$8dEy0@~H-Ay>^f#ZE^?P2T{o)I?_RHsH zJU@T;?Z?fEUxgp18sC!NHu2H#J%5vmgPEs#@osIq6u5dp;Od1ppZ?F=>z#OISHIc+ z7Wu+F&?T`v&bbLjxevVgfvW?c9xW2*9%vk1`d5g)1 ziwwyp&NIy76}^_SI%MBh|BTIdGc@eyYrpgFb=%4|)2Gk$>hIhYHv^J=^_pWG-=1ms zf8JI3)~{DuDcN7Xx2x&B63<#Hvs$}IBh$%H;+edwk6+t5?~LCEw7xQmwi?Sk>u*ep zWs|(=sM}e?oEYMjC4TFh^53;_?M`~H%C>u4aQf@C#eCK8xgVrNuWA12is2Of`JQjR z>t?<79M`t3U+?F~{<*yWv1^CHR2gj}NfG%Mum4-jp1-1J(fhSYp*^Pd)pE-$gPu6f z+HSXOZ9w&sZCkxQA8bFi-=D3wy6dXw36V$LcDz5hU;NsY!>Yad%5%GrI<4hyOB?3A z`ds|A`>d~2ak^DN+|9Ooosm6vR=XBo_x9nI)idGNRWT3I^3?aNpX_$?R#5V?<)XUg zQ6JLEnD+X=Tm15-4ri3${D&oWpy5^f&wQ7%qPOFi;D zUTAsYt1F&6o|!*BduiI08N5foo?KXBX3E8L|AgBN+ox}DcpkY@mRJ;yZhfg_6p1Wi7DG%tRpm^SqD6L%(MH)Zi$#H7qgCPf$fu)-AXGyScZjIReOEx+!dbitI+V!9NT~&DRNm|0bB3% zOJC$G;Mvvn{Ikiu7w>(#ZF1HmuhNuaoObWTZsR4ni*NtiU%H|ux+}70@5@8Yj9J;U z>Mzal`^zo;St8G;ZE?5$rujM!o-==|NI92Wnaa4Q<|~uFTE-=z;>imOb?{y1?9@JZ+a1Kdn{sEwJVC^Rv@DNH( zcmElZ=0(cacbwv=u28vi_{J`QQz2=44Q7AYUi48>ujk<>UN?H@{c^dAmV;(|X$jo9E?~l$Jg``oiW(mf?gwspZRsnxEzU-~OV# zy8DYnzWApBu`oQnk{hjq|bEB&2gx+t{-{mv6it+nmzlYMNBd+Y9v}?_|_$SHb zGS%#}%TMp@eYow9-;Y@4^?J6OF6LF|^S^THV)(3Usj*X_>-A)--7g;9;9EbxO=89q z?6f<+d0*QVeZu^;y(Pqy_4-PU$=9#h1h-~SK({~RtnCx7;lSq7Kos)L!2 zdAUo)@c+&K^Z(yvcGkC-A3dA9Nh+uM$@b6N7aDnMr-AM zPidO8%O2eR?fJzE>MR`wr^{mAU;F)T+h;}TJ<;LvR#96X=RcUY{b9@h-IW)%KkqxM zap$%{)Aq!Bjfa|!ojDk!J?Gc&{|DyJ?JlTy*)6$YZu~>BXuShB&$8L^e!il0#A+kg z!e{mmO71x8tkl^1w#K7W|L}{wDl_VK%Wo`SzHrg20NpPOZv4`&snz4S z&4eHFd$XD+|1V^DICZ@SThZ-*Z57LVs$R6-e!V=NJ^%OAxEQuKKd+rHnA@$CttPiv z@3#0yUG9jd>35Gvgx5pStp|-TT7oZ?KJ~i(i_|sd%oB`tPo5A9ufzwVbEKT*UZ=mvzH@ zrcW146Ehy}HP^qj&F^G__fMIrm6jYQtJy3i<9~G5&m*CJ;CMq7xTTXJN{_iTVQ*d>%Lr}yXSqYE4%h&KNil7ZR}hYAoKUP zUBxt;qc$%EPR{#P`1(-yYh&|23AMjuoL04|d|c1FdH4JMx$eKNf5};wTt4rp?d*u? znl|s}ZzhI*l(;K8ub}I!M$V<+<90 zeZj`poKl;d6KDUE2s&*L@ZFvr)X)7aw<7pOj`mJp-g!^w98}!Rv&FOd%(le`-S+Z5 zoyEk}%9Q>3(GJGDtHsYBYm<6C>A%#qYu6TpXl-;`v-(8+`^El-9T_3{kAs7jFQ4e} zYJo{Z&WdRU*CjfVRkrDu>pJXRcc=7?2iJDJ;~7)+o0OXZF7D?NJ-Se+w&1I#9(&~D z4asJ^L|+SNd&OCZEZ_+34%78T&|K6^=!MRxUfAlQXEvhq;_a__W zed#eHXSD0-a6U0#%??Ovh}`Dnacm%-5D*m zWq*zeGH%-Yg8fjPk{|0$w>h?V1a-N-#B#dtSTS&K5n)s*XZ-h&CAH$ zzipYV)Y34$#HQ2i?4;@qUr5_vFPqpZd5hYOly;niQnymn$FocF{q5Sb+~l3o z)9=JS@ZjFMKU(!s(C;NXB5xH2b#7i>{U~g*uFLEz|JLe$_LHCbYtD(y8*Xy3-Tsug z^}o%mxU(K_OUmmP*Jv!iC{@hHe#f}5+Cf$Jq;RR)R_=@WC5cN*R_JDhcy+nYp0>^J z{;}=ix%;-gc9JO-tTdGgTYPET=IwvIqW1^wzQMWtY_o*Q?o;;+1c$+rs^S z+rBA5kzXF)Y+k`4^#AZvKZEV}+W*?mX}aE8Riew`xM;8cj-}stDt_)z_%Y4z&;C`; zJ$D5oRLZ49{wM3M++bTFPaRq;$^59kST^~pp26=|JW1KV zw{Ty{t#^L$J^19YMgOLnsO`%&UeveLFs?nca~`-oCl+Qv4dcPv>n@eihH9 zed)HJ_pJC8wlDhDb|zOp`uDbMqidb@TW=Y5!H<$f$tVcc~y^z9tAXNFUE z_;2AB?2oz8eJt_fvXaUP6aH-fyfXXo?Qe@-2XB8QAo!~9&~xoeGxU28UfR3ppXPd# z{EXu^clRuo-|pdUZr`;lDt?}u|2Iz=Zf&RUOBg@u-}+Z}*E2V^TxPgc@#5YcEYbTyT}ju_J!sdbn}J`x_Xk#RX}?kVwfu$sl)e|&+di+& zy`F#E?o@k5a!~Z5if>sAcWoEl*ZCIL`hFtwhEwm3ZQAl%|CQsH#8-Kn>S8|IwY~Sb ze8EIx@r5aBy`}kEo4ul^i_9)&6N=yxm5N0Nwp=t<9W^KEHeLb8SQg+wujA(mR6|vEF|p5ofRC((*TRPI33~ zr|ir#B2+rpzj99w&#A0nWo}`*GFyEma5h^o!#nbv(ndj!CK2RXU`-gX0m2` z3(wii>vHI-_=L*}8*K$UGJ}+*UEK?H{>bWR?y+1G>pb!57dG!p!h2P1Jia&mow)yz z{eH`i{pMk8(U6Bdhc)-d-(3ftSgO27clk82+vjZ;M>qXb)}`w8|MjypG}`-6fN_4 zakKTGiNZfkCY>+$oE%%EbI$lAW+m~yUsANC|I|{>crWL@9sho}c9d;xTWWc;?M+f@ zDx0u&`Hxh)13iD%YKVT%S^4Z@^!!c_F8wzdFXwiAQh$7@kGEIohQ$8f&l_2H+`7}r zZf-1?QO=isey75tBJMbD)&;K)|Gg9-^Z%oE&Hu04Es8#|`0aF6+}g`F>#F&V@R@xt z9*KR~_Du22HIZw2j2hLRP4%f9<;8~tL%xX!uTJ`)Dy*&cVT%&?!MsiwHM8xF_9v}Fn#Vt^Fyl+_|I_gP|36c{c^2K1 z{Tey;n`!8N+n{)SNeDaN1|#$4l~cKyjPoBqo#SPF;`OV9<$5lH>ed011v%8S;?>l? zad_k(xE#6hx0hSgTc@{2W0u5bI-j5HrLtr4+VeLLXfKpmYm(Vj(^+*hD&8U9TTaR5 zO@d$L_M%RgxX%V*F7s4A&Tle4E?cBxr#AN!pBPEYX=IX(BMhmXHw==rW4j+_2Ydm}2f$yi|4t)|S2 z%R-A^?4H}GP+dI7I-RRJJ}cT^Tp@by!O2YQ5%T{T>;HcCx2X9fqVrZ}!uJ9uql@{i zXOA3NA^gAH#wg%=mH1c9eH-j*#g^q2YGlec`YiE$D)=)}GRwJgkNKOkz6Hnbur93Z zeGx5I_kLQejIB&f0jJ@;ZyOfWL|U~dtlr4qt#an;?}ZY-uVy>%5Ebq_^h(TTUwp4f zeD)l>bf)bVtL`hZPL1hMI>m9#@cSRLPtLY0n5( zG^p#(o+$74)ZDOjZn}`xu5{i!CXWEQ1%XLFx@3Lc{+$q9Bv?3aU&r1*62(f9tF#~1 zSwD(@laQCNZnvCeo_b;5u_$hC?ue+%JG>uHP4{55dX~wvsQ%T?=^3ZG+S<2j`kcvm zAQanl?qcan%?s1g#AK%{O^CeTxJ_EMa6U{NRK*BSn?porJ_?pBX48xHIYPAlL#y?m#ym4@quAD_1{JwN<7;CtH(cq<)Uc3CUk1HnGKANB7U7vfVbfKbh!zZWQyJz_RWp3S~ z5PCTxq}8Oo_Q$u|?>}7K_t>KJl2jw-Rf&+iq*=FI5=*4Ac2@;Ve!yp3>|U|d?!cCy zwwvpWT3S;deCuFZr7Lt(f3<*MWK*?mf5B=Ero3s-HR9_$_O6p%yku_C3d8?Tq&6|j zCZu`g9ym1VnboZ0=jJuOZw)Fr7kqHZMxXt)m+mhzIj!>Hu;1hM;*S|iH^%h*z4Ywz zGOp9Ml?66i_~n+aSF}A5=J%-3Rm&+O&S>Gr+lO8=W$j+RK;)|D_r0S3A1fKeg)CLF z-N?Z&c5+=vxzhSo3)4cJK{|2A%H z*_ggo}~f=O6a(x8#05Wp&1$Qfc#bDvBpwnVezv zy0}-Tj3M0c)--ncX!9e_rZ0SW&G*Xmr-~PkO9pN|Z?If|d8(PNq1hXu|9=-P&fah- zwyA<$*kYo_x*bedCic+8uz>d2+t)4Lq{3R~=cth<&FY(fz`;<) zPwv2cpFa~L?=|f9H4mKmkomLlH4eR+7RjqZYVr4-+ty2-;g3|Liny-Sm$sb-g(| zbgNeA9a&&|xoM6<`45*%PLJJ`^J3S#)-6%52|jhv#__6Bw(Gk0o`2TM=L%M?6!P7k z|1iqa#^&7IHeVOhIPFZ0xUSyTI zy`A6w*6~GKzUj@%zweUS#ePo4y1V05`V2ASX-@usvi+M9e>oaHx?}smchTa1$8_(k zIO#qmpTSArBsnMcbJ&-c%8wR#X8p^wi}9c1aA$-2yrM1 z$Td1tyx;rXWZTQUTTJJrBd)~#{~ zd>6}dW7a`SOZWXu>kk*b&WsG7k%=g_-F8C|{GJxfx5Jox|T zn8>^9mjYeBuF02V?HW{@C57v^2|0 z;hj%ioc4b!4VB~<>$F@7Jtcd8dhPM=ZMj^&zd6uH^O#2U)gXSaNqZj({*sV<-xV14 zOHMz-=f{twk0(1chy{vO$xY{Z`>^=6>ZcRar%v0NP~qs7TqE@KJJZ7la!pTql0N-+ zt*F-Lxn~yVQFoPvQ@W_SG|<_~zT=a6;Rhr0mfLI1Pw4+`xs|7sAFY+sGwtE3e)gt! z`xO3%v;}=y*}tgv;rAcP$IMGEoT~bG|uWzTkAnZ?Eq&U3eD9nJ7$IZ`Sbry!?YeFSUEqYaJr* z33IKwdqQpHJd=7|o^1-&*E&1aD<@q#$sTUDsMyJ?_#|7K{Su`+ccnj`|9s+}ci^92 zX6sy96wLaYRJZ+DI>YsGaQuzgyOr%!*;VB|_;+e@nA!K{J8!z6epB`TqdkmwExPhp z4jfh&>c2mA(V7Rmm*(v|e0*P}NBGIkJ!e4Ag9-P*(ZpJ~il{0vk=3X_L^)BM^nnIr5=vn+FvuCWV>-*gw{J~~_%b$9? zIJF%IET1rXvEA^p+OWs{Un?i$*X?&6=1TW6ah?vACpPyG5t>k!kr#VHkYnYx`H`|JOD=%;kgbIMts#GFOquK9M0)@@<> ztGrlZ{?eU(_vO@j{5$^tcwKOFa?9lUKHPk)@{nMJ;L{m5Ss%d-2 zyX>F4fJN7X_e-O9jpB`hy+gGuC}+%R0_Tw|{se zbaGpcNbmR9f@^=QPAwNP3AhrmzNhi&PLGF*JMMnm^xonHhvkNgXA)Xk)fbn1JD@P3 z^kL{6*?n8Pj_y7fI*}uh*EWCggPMc9>(#FWoHJK2KEhS@{gr{H*<_>mqfUi9YwpS| z&dO?T+idaRX2Rc|KmA!NUj**y+|oAncdAJL%Wz@OC9?X0IjQW+XP>=tDR9bNp2p5> zsj9DCw|_(hrjF<^ zI%2|K&}y&ayweN_!Uze6E`FYbCFwpqa(n z8rh}7Tp!;Z?qezSuX&L2sC|zl*Qv4-p6ky{oIK6pu$sS`_t%OCC5+ov-gUVYdSToB z)|0H?7F6@9-Ee-aa{H6ulXl5N2mTA%`2@AE+THO-xH0j`{G-jA!cyES4(Wa~wfa6~ z_0rBSYC<)CC!TyKvGL!%%e`_s>Z6S6(K$`Lf=^zx#~m z9Gv@7JH*jgH3fVUAI=;^$e_~5l zX>2k>Ce3F6Q<2PVK`wvLp%4o9oH8s`)eM)a{P6{u9W8Xxn7?-vm<^? zXb85yrEIp}JEnK}HLc~vf~gq|H_DqM6535x&yY)xZY}$|%Jp`wdBiStrSgu6#y3U0{AgjxXFksc!qj2q*iW9Q?=CGvaR_DGC>J@;Rqyu$X&I^^6?P z_5hLmj?EMGFYYa9__KN9zL=hjZ0;TR*0Sy9KjE@H(Xz)7KyNi>ngNLVP!bN>`*<7c4MeNVmo^jM%Y{>j(Ak5(YIhxd8m7~f6GZ*dIWKd#yJq%#m{{}9*1kF;jk`$1ChtsdqLY&7yxzpB0})nx*4|l}Uv=Qh*0{n6 zf3z?BF%VcNH$8CInV{~sA^cAs*0mVgZ0_72FzaGh`v>#lgNu(XwViz_m*=8PKHv5& zZXRZy=hY{bUgJ`Wo}jCjTpD`6Qa$eb^oJ@BpUi*kQq%co^V46svvN{`bv0_Lm_H^S z%&rIUxo+Bsrb(U{CG~urA#*5-wl&)2M)Vn2ijPLx{DnWIDvUdtU<)>?Hh!bn-UVi@Ijic<1 zjUnRk@jrh5KK%62`h+))tsTt{K4ssx8FB1P-tmX; z(Am8mF&8Ws8kN+Rt1Z%(Qr?xs%vIKNQ~vMWKz@bGzm<9>YZ+`gHpg_c{i(8Sp`G`h zS$wyhs1d)(vEKg2FIJ@s=h>G3yUgeKJ$mJzG8_Klw%j+?J)4tSHiby&?dakN4&Ngc z$ocokiRopzJf%DN3r*FS80Gj3tjgJzue+8UbnwJ~+Zpkn9JpBL9cuCXcADe5%Q9}C zoW&m&wH|9*$DZl(FShefxq+$wo};1Hjei_FAouTKW7D}mOid4x|0?^ed0VhI@L18M z*H0N*er8PnBmL;q^3Yd~mP_VOO2`-Nn*MfmgluKIKyiAEu58nsS4!EtSjFGlPx`|y z8YHmvz?tPHEVHE#>uCsn zbWmtY;Z8d4q(0ZBxQ=VxaaY9)wMgN#`dByJh=KFQkoPBG~XT<#c`SbAo+p>Jm>v`M1q@6jbvchxIp_L1+XUr2` zQmP+>MN9)=%o^|J1yDt*TyZkK@lCmSopXgMAWh-#>e4 z&EMLyAm*UMEXy-OaW&iCy1!t)D&dvSa_HbCv3s7H6~Wd`sX?nAcxyiWR>8Q=mF>{u z!tHg^@~b8S3J(@ z`0)Cu#G=z__uCE}om-PE{?xsAuAghN=_%QJ6KB2{vA_|O`>=0FW(r;XJ#uddzv4=_Lt*`QAFnP`Tt)( zf)?+ExBJsLb!UbPJQMCYaXa%w)%13A^Sy~btleFfmtVNDwe@M4lHI>DgH0b>mbXjK zk-aNW^jtP7{O0cJueHaFg8dh2>vD+SjMfQREVoQO+ok<+skQr^w^g7v?c z_N-4;G=467Z{JC`GBZVW`J<|_CO21p;kS}?|F_b1QcX*Qpkm!Ep2Wqhvu{LL?#i4J zU*5E~md9(FhQO>9$1Zy)O$&SXp`g5d_Ga!qi-Q9VRhR$j{>gnkcYX5L%b^l6CGIY- zjQ5;eZng1OO1k3nyVZO0=Qg%fzBp+ zuRHPoP3I@}$NUuj|Fw7hcRh86K3~(ux%NVGsS4)*b??2orfM!fb#HJ)Vs!PDZi)9= z&gV|Md-Q#2pS@ar-Tsxwk2LBA?|xGMgjxCjyc06_RR3q4DO;}G)U@|D@9YVIu@MSK z)O6++F}+vzDX{vZTh%ecFtH}7I-6zv9o+{h;@#FWTTj%le!tl~f8ylNoVLP8*vw{6 zkbB3{-EsZJ@@1R%B?}*LxF_l|t6HJDNhj02?c}kNBR_vAJULan=R{rqo8{LZq!#g| zudDa__x|eHSDh@O4$F^)^l7hobNIuS%(->%C;#^|ljq;-l+<9FI5!`XT@mUd@^(`F#4j}y z|NORJV5o5KmcWUoW%4r>j}>&Cb9tX4FW0)`@s?VqOWZSf(_j96!Mo|S)I}rB`_%MpC9r4(XBi3ol}0F$G@B3_NHw+-4UrjaoW*K89Uh8 zA7rxkUOy>wWJe&w-yPFAn(ZzmcwSA`*#ElOxGenQKC|w3*B@GB%e61cRElhxo0C|( zip9Lww_tOXWVY2ewq;Brd(IwNef>(`nw%?rddZXDBs@F&{K@>Q&enk)x9iSv)Lv`3 zckFow&v(ZwdOzRmJ1qL^YN2Az^<@6l%Kh&}XJqeTU*4^@BTQE?%;f*CC-Y4Mnp>Bd z9Jzg4!EAf||Gs%8GE@0AHI(L0{iG$mbEjp}{M6KmveOr3Cdqo8m9aY#`Jm)Q_7!e! zu17OcO4sjy$u|4JZ;hELsR6d0_j4X-M#(GRIw_ENzB)pm^Ktcb3ElFylm4rI%(Ykf zbkg6WT$xGiufX}uMc->b&5qflzwY>pRa{z;_C`6p4N?~0{jlsO1b1$Cy{bApOp3`5CPyS!2^#8dY+XKn!Z_TM^ewA(J z_0Q=M{J%_o^BT_8ycLy}B6f}!cTD}q_MWS#;PP#b{hPgA*t-9%h+CYW?E2~ccK?6R zrROPLdnXjLey`iR+p+skP3Zr$%TZ^&x4{HaPUgqbKX&ZX>=R5o+@)Gl_pmX3TYqhF zA&czNn-w1o7zORt6@1>;z1?^76}eTMH=lkz(XjZ*el^xQ@i!A4ww?Rs5Yw^fQ=j7h zPt$h0{EIEzp5|81-g-#ms6gE1h@KsRJ&w=9uJ8J2cT#v=T3EryPeSuf7V+(AeN(n6 z^V0FS1Jyb=Y+MfB`oXReIk)}oT(>j2<%-A83#2m@U08WvX~WuyNea)klxmp;%B@+~ z-+#L3L7>4I|Jh19i=TEsvF|xiFZr`u`H6k{v}0#wq~cm0et-VAaPF^1yf^>n?cY-w z-?nLX`7c&7x3F?9+tbRf)MjYM$0d$2m{lDLQAJNaI_E#B*^OYxhV?I+_`LuWEUB zHdl#X)q|V=@Y5~kCzST4h=(jJ4pZ@C+L=_9_%m4d+U=kRDGz$n4@+^i_6crlvizp{ z$$#=a#!vTWNp%_QHW1;yZ-3#m+|T;9ee)ImCri)1vBtY;WvI=)-go{n^Es9EO~hEQ zPGUX7dqugKW5L8+j(MEX@@4Ak)0|o#Zo1u}@acb5PapU7oaCkU;+@B)KlohcR&gPE z#uJU5d5Z=4-{>EBRi^9KdbH@0u4Z|9HsjHUVjGg*+ue`-`q$@0Ql{<8-`r2`pW(JN zDa;bT{n6$K!}7gyixVq+x9C1qi2EIXs8Z>F@|(v6Oh3OLX+FmKFl^I>3u{eF?rm(H z__}n@+r=k6ZZ|LAt^HqTaw`*8!(D+7TVl_A?%2TTvSsD@CWeCrd=u*nUpU))*)2cw|Q9+q^8 zm=A|%2X3>?5`BC$yzt-yJuP+fmJ+2%;nx)t{>Gs?2rt0nC+jfBUy7-K+bBkW9pZMRa)w$#88;hH_ zzisya7pZWb<7&xS<(6OS+^cu_J#-1^__H~ug!}6uv5lYQUcN|N-Jz1BmwZrfuFG|9 z|Ck*D4dKm654QUMo4=rT=2t7_+#Hz=*(=sCKlz{aa7D)A-Oo9zx2Zbh>@Qj-rpd@x z(5Zij<93LzJF9iz*#(++<=I3szHs<&k((-eSEnmkr_kz&=*0hje+56iIQJEE@NtPf zudeBfZFGB^VGufpMcp`BMP!q&Vp3I@xZ3I1{DQlu`#PSVlKXl6%+^P~FIaEfdo3_e z*h0%XAiv}>tMY%tdX=B)+W!^nZ9mCRH}2S(%erjy-olfYq|NV@CPwb4W%5aBJ#a*m z>t)ZM;G4?%H%`?{#2KfpnZC78N2TJHl19Dfp4UYY+rkW|NIM?6Ex+@!iC*ipi8*=; ze{YoZd1M#=GhLu5^Ww!sZ_D<7Y?qol6=gIf#A_a?{9L+1p~B{6`-evyjz@l8cyBZD z|Em2{{!CApuxg8Ge6PFtB*FT(f}1pM)X$7IQF+Mc zjH*Y>%-sfis;b%j#McM}8?io&WlYKbbkWI0D)_(pez)6mekAd(5mUSbn&0w0KKcLe ze;*2coaPxM8rcTc^NBukw21zhzHMjEw>1$F67CNVPpVsePEqhx?VHN z_ni2j8{%X1X?^piw`Ob3TRrk>t#jXB#UlT1*P}1{qm*B{PB+-$Uc64ZMa_5l2bLp8 z-4uUm{1W(b=*kA~fBB0_S}J#?n%I`K_pa1_7v3rVBcU(N z!oG9P5?!V^o(A0peUlWWi!YeoG%XU8mhOBpBjZS{-_hT@DjzB;eVqH~irA%zSu@ql z-g6s#^mcwbr_O(O6|?czof@wt4mZuZW%iS;&gEZo-K0P37n|xztb4v^;=P?*TfMqW zxBYkY|JS?E<=@|qKk+LMt(elc*7QN!T0tG<%@;Q=ncsQ!wS=C&gYS2iY1_9-tp44x ztBsLEfB(&}gUeCnU9J3uCs+R|F&tYS^ReTE-fcdWH4=gg6RN*Hw~uSvJJZxDe%srv_1?F7`Q@bo zPVd{lAmbP3r}v$ASb47qZ{6|yk8Dr=rssh#wxsbgTXt@+`psOo<5Gj>x2hX`yAAdg z2=aY@Ww-R$Hbo2G9GQIA_P^6)@sFKqQpKAUZ1Q5R+W*<Y1Xl5?mL&*P|jD8AC~5u3tuha4!XPl@X^y>Z=~#*yq9z7 zgjcl>`G3^ru(q{sU;by}6MGGom2aB4U6y6P|9SA#3E}=%syc!vt$uwAB(**RRj-4?VxL|6zS}+k;cbLv0Vn zAFnvc`(w7`q2CwTdp2&rzJXhFfsD=`w)kM@AIr8U1f6e@{CQsOQ~fw=Z&6 zpL10Be}9(p&I%uu|LiV2B4xk(>h!-ac(TrZkMO32&tI^+v>u(t!y+wl$arZhlf}ms zdC6jPTYqNEidwucQY8Ez|Acx+{VN0knx}~dg`xqFNufIFI-OkX7gF2 zdBWzGzrue#_Y?I`Ha@xk^1RZ2PM3erOQj`tWVd`i7am}xT%xz-@Y5ZSzbXH_s>G;o zJ(Fp>;T&JH%!h5&yh2j$vu`Bm&zivZ;BIR5oXrX4tIMsO8h;*i6Pa3;?CWg$;;h?~ zqn8VIMRpq|GGuPNG_6r$_WKXJ6Fz0L%l$}QUw1E%XTi5*=BO_}-)6ijIDb}Tn~59S z%Br3}@t-%flsq~d6Z%pP zJQbNb^R_aJ|Ls$mCQE)D`mp`E;H;ZkIR~ePxp6h4jw5RZ0ljNSwH>a6=x@cRuBe7WO+w^XORqv<0J?r`<{ON+60H$wAee<=y+d$fV zyPxd;cZ~JV!B<*dErAXx%^u1BQuQq?st(zkahl65e;oQNbw|zn?jP=Nv`dSBeEDRy z=+L7=>!*tE?(OTIef{x+_Zi;8pOmNgX0`4)T9k2Q)7~xeogKR^4%@4+ef${6@%5`v zob8WQrHpUZS)8?*q07~FaC==v#P3$y{~|Nil&2_MJIFCzXRg4EIbD0Em4133TOpRz zDlhYHL94CBkw3;y4Z=#r4=z9ZamQp^4%f-2e01l<^EFJ5=TJ(1Bq%T4cVmajozv0> z78S_yRygXXl)q7q=S=%??&GF^%JEm^JheaNvm82pR$Q=9-F^{6Mr%8&3SJ- zal*MvAud1MA0J!HzGS-fj#6XEhxhslN_<_!EW@_?@fffaIrr7u)XJaA;e4Px_&|7h2R_TD|d0za8DJ(Y>j$KUhfXrt`rkkHT)nDc*dk zq&I7qg2hk9qTMUbxlAf{^8L8@rtlXTwf|S9spqA46x1FNs7^lB$F{k2eSxtC17n?F zM{mDtP6*Q?A-RPcs@aT`Vje$TxkL0(yxH!?d3S}l zRh}kk!3!iGv^Q*sP&sm3!OV*1WB=(32CiX?Rvb>Q*Op+}BOtdat@gOub-P&%O?nm& zp0-F?yCmJ-ocu5=lmGn-mYChYSg1Q4>Kr2d73!EXee3L(lWdnC_#=0d z>t(~l|6g}$Zz$|ra3ENILcZTZJ5#B1+qM<-bU0sHd%R*&k#L>cyAPl0`% ze9%}T(M0ojEBnFYllSi0BFlZK;c~)Wd;Y>O@q~oOZ3{Z2Pbhy{Ea1`T@o#$w_lmx~ zEZNf;Vs=h$a=(63?ojQ+ocG-?md`ghH}Bh$|L@oIFL>t4(#H% zTQg$Qh4)#klPJ6srn^{OpHnWZ>_cDA@u|1_KfJja9K*Nuh&kue7oyV|T&+`|#IJtg z+;&!S2CI#G^MT!(!j~>PC339UTk~NI73=ZPvNyXVKQCndjP$7j zZSSwKSk3$SXg2r56Z2E5SI?Xt=M$eLF)Csj@lR% zyC)|uZPW?--4XGZZ~x!{l$4bO#N_%W^r-c$%T3h0FD$nIr+DK33Xb09 z7biSi+S(Ni>||YYxvz+x*&dK){aWITf5XlCKk_xnat*7^Crz9p(Y#{C>WUfH9LhX( zmm3-el<*tI{o~;2a*C08Dn7C0h_rs^o6{%4!~-#xAbKl!}`xp*x#E6 zu5fpHoU-_vSX0dz!LSo?zfZXIKHN0B;MYB_ji%CHy!$#gxkSt}J2cnq+qU_lv&t8J zeEa=f#U-_i>(5QL)tvS5zz-dzxNk|bavThQu!ZlDeegoAW5((26F=EVzdV`u1C;1KlwpU8n5~N0+smM4t2?uJEYUP>|ORbK9{+?qRz*lI)s0L$y2MQy-p|W zC-Z+@ZYWukqp}>8q;>a> zAKtya;PpA*7{0Qmy}2B60k@~OZxSs17`{Vb0`IpOZ{PjVy45OIQW0&|IP1pcAJhN; z&YofWV$p+um=6NS{y&q@dRKMu=tb7Li=8|EX~;@OnyioC;v5vU_QN5;Z+m72zTy@U5_}ujjdZ;)E+(it6ouMEpkroTtS`YzPIhV489@Dl{61<|4Y2s(Ye@C zx9QLKtH(8D63dsb;A^ivc%DU_Q~ke8y~quAyJuG30Ra~~PnK@=Fq`ptcIM&sIVCnV zQ`+8Kk~_0rKY7DO?zwkA&&b}{ugzI_B7tS{rLOB4hOt$m3;V8ltPqorxuxA)y2P*V z=Q*w?duzI`sIRP;7TVHQ_$(tr%;Z9;)(JJ~fM5Ig_7+PE#O*F$Fpp`*7Fl9b z*;XF%Y|V!9#vO_AW^(Gnfxk5T*bbSNT&#K@9aFf6;r$)fSsx;b%Woepy5eYgxoOj! z=N`V5O;?`h2ZY$Kt7kX6nwrZo-|KY1Wpj4RRJA^K)@8G_1H)ZQmv4%+d^7i*u&ir^ z>)Wbt-2G&y%Y>34R^o{JnwK^mti|Yf~#e>xgDX z9$g_H$M)MmBzt|;LhjH?{fXy(J-C_u8Y;syEMh_Qdip~EuM9mtu`i?B)5p}J#cwSh)sQX z^SYnBYy8X>FUgi${pN6bx2=YL_VF7b)>&O~k6$Klx;V$HSd zrb&-$5~t66r@7ZVBc`P(zU937K9gEiS>=PlY=mD!~-xgmM49v`om72DXi zQL?A>`@)rJEJj>Lk+ZAq_8jJ3xZmjGyJUW`D`(b4M5er#(oB=)nmG5|v1G|x%QI{? zRlRtaea>yrxa z%%4jhOD@~}x2)ssGDD5!3Og-?GgIEb%(q*V8`k#qW!RJwA7>@uOKR8Zd`zrC`L(*XxI$292 zWL6ygJxQ%6_l3QX%dar6;}_#%TDIM_yRrJU-3|BVYwcQr-xf}*5xzbBZ{_TWn_VyS zLPBd6)rD~!Z+U%#^HVkVUeOchYuw)YI%ZX=-MjwP=|{)== z;O#Fwdul%N_-CH6Nj3ZMg0+Zi*M=3Td!3x>QueKt$)9)G!l%;9ETC)GMbnQ*Swwd9 znNL$%pC?d!X!Ruip2CHPPnuP&o-o_<@86D^qpy>;_{jYY+QM-ye|CgqzKbK<&;QT! z18*6%@_zEv(bf>Z5c@N1!`$Ofg5yQ6Clz=q)=0*9J9^buDr!l&Z&3NNe}?+%{<59o zrbnl)c$*mg^+v^=rmS_`U$;KZShBPA=1;!f&0kmggMCGrp{CTml$j9cH6i^@tpncT`B)^b9??cp5vZn9Q5JI(dsL_I$iuH zHvJ0L&$)0XeCl5j|9NSPR!qB_d$X-%9lI&((~Md7T0R8YF7uu?@7cYtooON>A&cv0 z)}{n8f2~pX;?ln8`Oi)D2luAHuUc%{-4}0IGS1v1e!h8e!QW8s7HK0L-l_YyhPCcK z_ijVK-cgma;;~Ozuaqwqagh1?mPzBj?5ynT?yEn%t5uX*F7k_CdsFeFJIi9<9#g** ztJT{YepGkHttm;Hmdf7UdpTjZW9Sj-Rdyi@Dnqn>NH6ohnZs|l>Dy%a^E!ElK3q{b z)-Dy```D%8Ti@5y)16?@Kkp`zJ&AC_3 z@2vi1xy|Qq{K4Zd{@2cV^YZTkCcZBZ3#%5gbw2-MAK6-Vx!WS+eNwcY{eR1Ew~8-> zeQodSx);V*&N#XGZ|ROl?Z*#_A6uireu*>lYrFmA$G^Ya$a?$FHpf5y!q0mbnM>{V zaqWKYss78Ey>vr8@6DzEZUo9~x%v3^mim7M_VO$KJN{f*Ca3(yd(-i4`Tl& zaba2E3-i~^ZYE#t?WGt0X77LTxZ};O2NUh?x#-9y0&rOuhJXFAv|3WqZE``pq`o@$wPd_Zc={at(I*Zd!cUsaUGXwn<>t zz1`bmJ5El2+w_pTcERz7+hlA$RPO$^-gedeSj);ee@bHiL&hb4%x?I~UVS$9>E;NQ zs!By2JI2nFCq25j9X)QO+UUMHx@y9^AFua^anuFcbzb}Pn&V@cyN6HDg6U6M3|iWc zJ-Bwd>dfjBo^tcPSXzKUoo0dzLl95BhjY_<`8`d^nd`hAijstynw147hqY|0lvU0@ z9)JF$IAq;ZKATu(zV@<-*I{&dB~JsldW8OSGr1s+0Zt zBmrsp{v)sZC;lyLU{bO=9`?R(XJ0c2JQinmV=4=ok*9s^^X-!!J^ijVmt>cSl_q&r z9KCSP{w~vl``nNgRItIK-#@>J{@uvD|G%cAm&Kme*4)NN+W%xgAf7j4p7oy8X|{(C zg#Y=!4LoN1!#W}Q#r#;A`OoGHF6p-2D^orXWMlJV?E{TF`|jWTcl)u*!zc5>Vj$wN z_CZi)V~2s`;Z2~`x_mIuw;s}#1K9`0@tl4_e*YjL2MWD?LUUB+{3$Mgff`kaJ0hER zO|8{L_WzgsjF(Q5bl;!;VSCGmg$5fh=)XuaKe+DC-sA26g5UXkZ;Ioq zQToeWm2}@S;bp(|_wP*~*0L__IzIn@@PS!}i>xbh-V1+jzgPWt`_a7k4N3jh|GgP+ z{)%({x3TQd!Gi}s79RuoOqluB~FS zO?)d`{LjD9O|iWwc;|h4mpxNgf0mGyJpN==SIL*kU;9N%q$GQr9eH}rCb<4ImtwCM zf3aZFxx|73p3gx~ zXzu+TS`(Gl{BTA|J!8w%*t=G**4=uv<~Q#Y9&OfW8@;#jyg8{=>D-&vsc+bs(cbc> zZp~&V{&g=V%`55FJ+bQz@8;t4f^#p8F4>>uD*atu6XW`$V(~8V^UOU7(jORlgR~;d z8^3p&Z7Az;{MpoZ^~nKKpNU33{3eS()QVs06!~wt+7LDo9*aduD(4Ki0j4mN)pnykGph{Bzp#@7tB%oO-v7<>5Cw!Pn2!`#9?Cn<~FPf8HNgp7EPo^~NA|-U{qx<((UB2J^vhTK@)AU-sH&TlHVXbmYeoGxV z^1uru&VF)v)O37O2*m!tk0iVo!F9etU6V0757}rkoo7&%ZV7? zO^oe2ZogT4Vf++{_2-Y0rX3sV`=)oPPLQ!H%=Lb1mhL-~6&JC;yAW|1uS)ygjbZZ{;m)FFo-q zc>eKsAC-CSERKD#O0}{G`Q})C>u{dD9OtXD+dHfO6wEB)wAPcD%2a&ydcn6X#_wuo z{qk8qzgDq&`@N87|28HcyX^mBa&vR@tQW3n#8mOw3|hoSZB%dz!N+_s(z zN7gQ@I_tl==D2~O_fxGa7i8z0iTpQ9@rg_1fpec{)jgdrd#5pMXW!AAoLSsctqr+3 zx4Iwq+xS}2Qz7Gt_l(75y}iB3HAzy&#>O+|%{z4Gm0HLzU5}p68>asM_4=E|`%3Pm zzoyn(@yreCT6)v7r?)Suq=-+usJEgfxGVJ}(=;=a-=(QXf|j>AX&u~?!mQ=CW`$nX zrMqvQ>_Rcx=_JDDGaYbc}qvLy%H1X*h zx2+U?y?bd>PNmr7HTxsxpIPs;Oz?M6_m_2Hc?rIodU&@=8J*-grg`?r8?7nr+haHG z=ANA*T%01Tv%b?(=kyBx2uYT0pZ+UdVENYA|1Inv_vE(;uVux4Py2S{wql)LOS+ao;qO8k}Z z$-&(blQ(WMxe*h6@>avsvtjK?(BC6=ZJC3NITF=^xH9I&Q3ySP^si_z6vkZ!VksR_}6Q8uOVityBNcHn4c#_j~K3Q~T4J z*TvpFuq@5ozsapl^d#3cvoF!jEe9HN5^5d4xGp}!c5Okz<4pY>j~_gE@T00=Qo*P5 zg*p-o8;TeYzgK=B@Rx9^)xQTZf6sNdUV8$*@q9QN^HNk zB`Gb|TRh*bMQY#*05+E8wi}*yGpfARw6-{Z8LGs)s)&S0^zq)BE{*rS+w3 zv#qVx^i6-XYO>_HzwfU9_%=tdM@J?>{IguGw^qhdXN}$RO#a!yGiF8~IA$!RFRivg zuOOmU`o^9*o}bs(@I6jW6WViUO=7Cw)2JHwbjdY!^|ap~6jg=dzL{;< zwyEXj(#=2K?Y4igrIEGi9$%8--~2!xAcH zHqGiZy2De_^!;zz2@k;>(fAeLyv*;#7d9n3sfv|`wBIay635-TeXXd@+BJ!2 z{~Hlx_^XuXXSzxogQxB#{oRk+?@ze5@3)}av10~&l^t{RSV5h1P4@DYHPuXE>J4O2L(SE`A z?>Vx)i)YNXRQQo-SvBoyi2b#_^)f%xe@*JY{+w^Uc-WTLANYPH{{HRQ z!@I*cr<~SJX}`C@&~#~4)FItJcAs{sT#5Rq$9>vO`{mK&x5EtVKHL6#zCArLBa4Uq zy}};31q{<}t&M!^dh%|;4~rXn?C$YREas$Q+V{xo?tKaWi z|M7dRKDH!DGWELc1L0{+daW{Z^-pfA)l;r->kYa#f6WEnp0NAv`%4w#zG!PIL?1oS z9^%5j*)X3?+d94F&Z2L6S{ZXD+%h%(G2uiM`24GC%S^TBN}W^mi1uk`?Xh!9Wu?uYI2=MU;BN%JNPty#C$ z`CM4a)Xyt+yx~j#zx%}d8ON8!bRGS--J|6D@vUq4JhpB=(O=v=w^*sm?b?Q&E-gDY zv0Ys&d_iF5B;mwr0SA>ARhDkzsR2TbnWuL*3rsiIBN>=j?A`sfGs@=cK9>G1RXjR5 z39)WR>Wg`q&&MXb6KhQ0`Zyv!{y;{po!yqcmj>E zTy6U?Nl`(;i3T!?{}*RzIrn|L5>T9F^9roSpyzcnSV5_e~-ghCh6s{F3oWpM$3udRYwBOkNVn%dph*8(A=Ya)F4rqNj zD{{l?ThM*!mVd9zE#6OPxbm#<%=$ISvAa0WzB{~<>0qd%ZCu-P|4#vz3_4A3T7~`I zEa2E;VYHR+bI7%A%fuz?pPc$X&5!$O3A2&@?gKhX{LS90Z%o+4K6CrR7)htnQ?)4} zWl34CY%gQ9KPh(#xIbOsyy)n+Ea7*%ZXNtyWfqmb^hU%h_qsK@=i*A7zszRTc+#w~ z>lttLd2J1e<)2SKez8K$I!9W(8I#rp%4>M; zKH&3|xuX8Ryv6HM*`ISiM?|XbKHt~8cqK3EwBQuZhLRV|auqj%8X|i4SxV&xnVGFS zeAkNa{jX^YlFE*sN-SG3F7r-j0YuNomvDt?=8wH?wvSK`apHubf{{|foIZFW1p7jkTVJ$pis z!L0hj+}8?QU&LOS$a`j?#TTV>b=91XD??w*nwV6&N>0DYr|{fs#jf_FwN*`aD>`&5 z`Quu49y)2yI@icZU}1!4n^*p!(}$SLtukUfe+7PirZ}(e)!`>nQ!NAg=df7MXnC?Q zy|u}I(Qo#X`@8zSEQxw}{Dk<5xgy7fT9@CLcd~K6E61X^vd(h$XLb+bR+!s7j$c?? zDBIPW`sI0?Ye|B^yQ^ygx}0yffqJ5K55YZ83o)jj;rdB0wu#*#jSo-Ro|1dnJa{Ixc<*^)Y-6eeoFMl zj%BaY3`%zQ&DmDDM*GGu-K*|v|NiN9=uwbW`2Y0?lgQllf@%4j-`jH|?%#D{mVNf> z@`L=OwhNOiGbAg0Ob+xrq9M)oxi>n;-zxO!o3YPZEHQ-xFz|d{m0ZxpM~s>SslFgkxfhd zi(f)Dld8F`<_S-yi!#yjqN(X~J!1E8^URsQ(0k?KB7d<%`^+0IFXlVbxqF6$=`H1x zN+pM`ajlH$^t~yi@_F-&IT>zUmsM}Hr!AVQwPo?!--}P~U(npED01&>TWFH$SKjQF zc@r{2M9!R8kZ^mV*mFbktL@)P%47D3^p`VhU+_yi{L(%D&JP34Po6@)pLKUs9C`Ha zcxckx+zPQ2{_X=IHIvpP#-99iZKAB)oif)1@qFDyZ(VhIa_1)|gQ|NR zg{;gIxTdBqI_Ohrbmm;uPMl4>i>v5z&Pmj~bRkM4$J$-!rlDGV2mb0Ai^kKH1#|+*{ z`;()Kx0#6tNT+*zo7O3|EWOOjyj7+?`iE=k;^|AT@?N&sAau-;x7xwwX7`&2{)kFp z!SD@buQr6t?_;sfJ`%w6G|AkkiMK%0f;m=r@{DIK&&5+pNb+IlL6pIWj0r2LsLi_EUd+e;i#sLz>uDj;#+vt#K`_M6}36Hks?l=q6^ z=lruv4`|C2uCHFWtRU+{B*z>X=7Oy?BA1PWXUv$@l%gU3ooVj_k#N4w0>%0E78Vx! z`Kc4R|edx*+mSe|`85pVt z)%_|r=sWMy>(cr2lY7UO9Sz>!>O5+qdbV|j7Orl~5|EeQ{rG;}S6MxY*@s$keG}_> z&p&GS7TZvDsXKJm&C=}H6DmwEfA5O+cac;KalZFiY~#6UI}%>BO|V`ccyV_Fuc+@Y z_VD*BR_%Ur|K4Y|Syv7u{miYfu72#b*J=Op_$4~KZdNV%HMcD^NH?V`a?3uBhK;vt zr|{}JXvH3MIc(fAGwN~JQHyIgXWWWSO|O(u`7dmJfzRH3!ddQ=Kyz|SPgEvo4oe^{WugcH+*K`-?%sR3Eu;>)7B^_G3;<}$Nl-tm)dqBne{I7_N ze^S5h9XoXE1bgJ zx9__tmKR9g`l@!Pw9MthUZWCkvjr2*F8H_6{7v(FrRDRdEE*Kf<{VweH76@s zmYF)=C|$Jw^^tX#b;T4fDe@fD)J^)&agnw9cK?p&_KW@;VBUZF?1{w(&P+S;e^X8q zlcV;ZZ?l7UWF#>K3%{!Wm7P-|C$2xIH9yRJS~+t-;?wxHjXCx^Dvmu)eXPQoVzlPl ze#b47&6O@ovn1`_?EdfOH}kLsQo^j^0?!^aMe7!2T`;k;<7wG>x76fRX~4NF4;Nmo ze>^+*d7@J)w^>dS%VF;4g%921Q#SAB7txsNHv4f&1>dbQW}mG6o4%^usTKOTz>6p6 zyIeVAKux1|KacA z#uJi}%WXRTT>q!}WPksP{UW8;|0aB!zyC1*v1g0yGHqtgns@l+{piE?J7tOwwbuMY z?)|aEZSwxdJ1M|ORg_aGxhKcJC{#vAR;l1|ta972wu|>Kch9?L$HUj3l$9xaXtw3m zqBUoE7OwtfDtD~xB!9<>KUl*;Pe!>5`|Cz)zU*!qK zSH0BZ1RKAces^(ly<0`Zx%P!uJ=y@LQ^e`id;XHfli^$Zi*KRY$*3bB(KjpsC zr}xdPd*3j|7ry2bz8j-_>5tz*n_tXJtaMX^w^lq#=i-*uzH#f;;d7Q8CoSL3m~6cF z&F!0lpX~jf)W6W&bEih|#}1*tkA3uQUo8E`%fpvgR48ebXx<`p_^A2AT-k>7xtm{{ z^laH&D#7*a#=c*vJWt&o&k0m9vVO7aq2iaxc~!rE8JbwNo)q22nl8QR-Q(?lJD$}4 z)A%{P?$Bldf2IGf0xQH`Coj`ea{v22DKYs)=b1v?XbPFv{>5xg#An3?TtUX zR(~ihaXoPMx<>BX<0lGK-&so^IP+dp>R-&K`rF*oK8i+&oUoU@ebLeC7vuKWH4j88 znDw=18JgT^-K8!k!qDYwg+@lN9xELCIrx&5<4MCXrH{*KdTO{l)iU1olJ zH&n&FITZVs z>)?5|Tk~8Goc*hHkN5n7Uv&m==ITAZEEGJCZ}r{Q9nTXPk9Sm8L|<)t*yt8==kvjt z-HsKe8>)1EzOT$`<~E#}F+=S20Wpnj;-3Vw$%+D-l27dSs4)rPTXOj99qyB@&5xW*n=U-@ zd@^(5k{4HLt$4TO zxK6G!`t<+Od5(MQJN~?1SN5vpLY9Vwz=-2XU@YlBPiu}lU zU-zj>(O;c}r+-<#){Fgoe{A>ccLx1`6(-fI-Ad(2X@7WUrNEvXb))lrx$6_x8L*^1 zSDJ8hugf;G?oAyIG5dbAiOSl(c$ejV{sq&u&YzpZ!;Px1aLB(Avf|dNw2nJ|`|u@8 z$*DQv0)l0YA2#kW4bG^L;&gr;lE^J``RBPlChlI%0*mxUZPDZj+*!g=JSUe&Z?JmZ zke%v$Lx8XIZQ}ncx*|%4^jI2qKZ%!MxA*;*yKZ~J)MGKnPa8-?#=3o-wIe@HAnkY5 zrs`MCd;cXQH|*WJ@~`5bq&&s9wY-A2<+haaCRRnR+b){Me|$o{&SG6_xrOuUJnAZ> z_mwqPSo%~gGTzn7m(nJ6z-XnAw#%3D)|>mn4(H71TI#U!gTK=Mq*$}xn+3$>82dlG z?%*$L`@Og9#EiKwp9TCk5pbLS=VMg`>zW&~Ym%PCiqx5K96J3kbkpR;PxilGse7*a zj{DD!x_+sx4#tx@9l8aMq;AZ$kCv#B<52Inuz71|p=cDpsCC+(h6Lp-(JN9POLtWN zdJ3uuS|5R?Dc@!bTU9mNY`)tO%AHgq%Ue=JOpsBQi{VZDChm%q+?bXGg=PvO0^ z)KSFc<^iQQE9bp<^|s+^pzgls_fI|LT6KeY|Ea(!cRseQOce;qQs3xPI*n)A+DqquwqMk5(X}A@EYB;Wq=Hxf|ESk(DLuS5 zO|m3s=B&9_S7I{cr7EDa$nf^rS%h~l#yKEY#UH@3Hb%U$JF@uSkTrE~c0%gC1 znz)v2)LRief4yAJ{GDaVOs`ouZ&aTwk=b5V$`);-O$LUukch6ntTmtKA3X6YTFFW4;Bpr88`6>o?_6X(cdl>4=JJN}8^$#+?k@POyrTBM z*v$944fLlb)vglhZ_0Tf@lmQam}C0I+3PQZ8(W*f(i=@#r2?v) zX$Hj|$9Hd&T;maLa6P=wI!?{PecMeN-FKDubNOeoZc|D1DB0HJ`NTNnJ5N$kl9&$L zEX|#4*4pAM;<`eX>8+DIq&_@bC9N|lOHt8jzhOj^^tWRh`p>(6UcvHuwUAG_`2q#i zsUGiy%)YPecNAyfP2=f9JxLSmp3AbbhU>NB8%Z zwW`%mm#OB+Hcy!HjdAa<@5|q3bhb>1X}r60vqg3aZ~v1Zp0y`7UYpnXM1egbaz^mJ zX&b)pZT>!&`%&29yJiN9Hg5m4|NkoguSxn{!HXXpitF~cK6_^5L2WBm`S$PwX7U-+ ze}DW~$l(-xI`HtFJJU)=wy*xRo2 z+ni{Q;!O+xtb9AouYkY4<<2$F6B?~M(&u!Po#t-&_prX;q&4&2Q?iQw4`u#NJ#V7; z-fzMOsSxf<{ae?HJFVaEcy~@r#rvQ4PSyV_)c#-G9`^6%zsglQtZK=j`UO`{iD#M98PjqQ`*1d)AC z^+$5g9tBrQIi1N@*k8@~y7=f0nNnfRo$?niUFb`wg{nd2Uar3^qmD4%?^rcC@mE2fh``^}Y?snHFleu~VH;bRHPkQ>l zh*#|9gB!Pw2uVvnoVQ`Q)#mU`QM+9fG#7I@zlr_*Mvr0Y;dePNrgUfQl<&*tk6XB8 z9dEAh*A@Az+)g$#Y~q){qa~@ldzM??mb$0)om(frI&o?$?;?$x4F4;S37TguIW$B3 zmH+=sn$s`%SM_~R+dFgSWZqi`ENvw9?f8;=H|E_v(>V1%^OOJG+W&b z=AB-Caf%R^yD_sw#Ek>CuN3cz+PP2hRgz0B?I@2en-U?t@=&+-zk*FFyVY-RFI9=I zifNMj#Qmz1>&_;7X6E-kJR7a;Og4OfeFn6gL!CDo^=`aaxkkRunyc~T zv>d_5%&{5y+0NDJ_HNIvFT9W^{P2RKa{R$J1^QOEU&dTlP*cgNUpr60Ak87gTT#+I zKHd7(?b{cx+PB>lb<30B6gz!EGu*(`rLyE(LP1jGj}z1Vl#d?z!B7>kPu5mqR-ABr z7adc#m48i+*`&D6Zc{ktw!LnPN(D9@ z+um|ruWM0TkqURC_?-_c3ZwS7S8wOhH4{@aG}_wn)%WA!Kqt*rp=bKOELyx)Hdpqp z#GI%@ZL?T;e!V^sS2{a;LQii(jT_fj$4R<6`-5z+ryE|+PgN5|JjOgLcY zWM*jA`17i-(nR)ksxL~k)|~fCNNW1kd*V#pzA0;`l^EH!+~ZsJ+3!p5nXZ(+Q_1|- zS4Gb6?MrO)cy{l#Er0W?bK7R?`13Dr|Mc>O*;SNuH5!%w!f zl=xd+6l9#e+{*WyqgQQ3%si{9BHfE?U%Yzbc3*>4UMtN#YDKu(#rt<1zg=y<7^!*H z+p$vLd6%YZ^e*W;>(dtd{%4X@d-J&P?f1D4q$*flg+szkUw7M;1jTcd?eJ)sTk^-! zFtt?S(s4TtGvl6n=ls6#_&OM7W%bib+th7>NktSm+Vw#6gu7g z_UQD4v<tpYaiN?AfGpT&Ws~(c2(7PcSf~cZO!b_&v7`r?F{E#N0+KPp1-lX zlapmO2bJ-@RyVBw*082VNq(W)!ipeM&77Npt+#AucWY?NyV&KoHl6c$UUs~3pU77u}l7g(7g(m<7#Xx<8N7CFg9xM^<@o9Q;mpHt3Uk9Eno%T?9;&# zuU)K{k>)%XntzH%^c9et3NCfFE4i2ZT;B& zod3kTFs{bKa+B^BGj%MM@!j-Ul2h>6TV|y@lk7F`+Zvo&HvR7B9+Q{+vQuPj9V1?z zKAf5?{A0OUVu~l{(`RaC;=ew=T&S;eSN`$kCv1VM&v!n~t7Cs%u;X5n43}TTgbmv6 zoi7e<{=F&sSlquW`t#RXaa&wCcDTxNSukH+Z|VOxg+JvEOnmh=UX#tDTJ*~GpX#rc z9G^HV(CS<3edSHlzlybO-jncBq{Cg*d4i_-!VoP5S>0o$=I$+bwGM3fSGu@{NB*`WHE3y5$G$^PsrC7T{?b+hg zL}4w}>ey#YQuA6595R)YUW3vGJJD5b^b5jl&$^ym&K=RDvta1RMi>W zKA-zWLiWAfg6<<`@_){*v#hguf2z>RPUFx|X0>mXwcx3Qnc%5}2RB$JHt2rUI<`9Q z(W?2PYzIec)?k>-wx@6&&a=}Bfy^q&D{qeh*V{Te4hv`gDj;|Y9=Qi8Q%#nHU z>Se$7OVJmR6NDxI&ic!gJ)39Jw(mQ{A9A}_TU%J%ns~PAu=O*+IMd#wskWBYZ?=M> zR0g{4%Yx4#Lg9+$#v9igR$X!u5ffYZ#M##B#=X3>jN|thAO3J})V=jC=Umc)r^;Sj z+<^fRP5h>JXH49vZn5v*)R;oY==P@@`L0Y8pSV|S!G+UmSA(3LwjOQfYA$u=b4ItC5T zkEY<2nU6VU=v{UB_gq!zA>RSMm*1y1yqznnvvy5FZtSU(_K;`LbA&VgeWnzA&@2}T9gwP0c)Z}{T3eUcHHIP~RgAA1RXur9C zeCbaiKb8Me?r$rSjb812BQE~Dyjp|zyb_HmNOc1DykBDf9(fdD{sbTVo|L3+z&3+mf@$si%_w;?olm0}nNbggB zQor-XhRq*1S37QtFtYD;T`jPtOz)w^hteQRgl8r!a8lV)2^`;)XG$cX3bG@ey&Bs>lY zDbByj=~+5iG{Upw(5uygD@wdHqBn1pdfAfd&#t3t+g#$(r)$Wh5Sa8iSw`%v?S?A` z|J5hd%bLuVj+m^&;dt@TAEmHsDFLthw#|kl%I3$@`zE(c{=c5bC-2UV#{XaU{}ZqK zx;p;h{{O%Cf8787`~PEaZ*OlcJ|0^vQE&3|{=5d03nImFzn|2*U+wQ*l5ys^ZS$Vp z@?GH%XO#0Fh`P%qVtReUgA~7m4J^06*q>eA9FawG%F-zm~x@5&ad||t9ZKyiTemXy`V}ZWYXEm13X?787*EBENEIMnR zUB>YhHESdOo!iDU`>CPQ&-|D_lfV7BY|np^PaU$#TSg`xoZI&YZ7~1K!^5*+{d)f& z-{&uyFMQJecKiON%v%;u_$8fq&6f2Pr_%S5is+zbQ++OuL-U>Y)wGm48wa&aouZ&~ z#I{96hx5|T+(nOOUkJN@?2u})2iNfhiQa#!o$SlDZCD)Hyhw$K`EMuk{o zrL?w(&DxD`-gaJ{qPAj6R!;x3B;7lLLZ9;u{{LOSF(JM4srJu+Gunk4mcmz>|Je*J zE&Wvf-;T;SZr$gQybOEg_aY2AdQO+me)`1{nyehs?0wcB!*EpqL$KyB0vhzNj@aB)@(--Sgzy8_$ zrfj`k{k*&XYd2V%)&JVMyX03#?>qjP^LKo6nE&RDOz}VYroUdY>Ei6UKR)c7uiA1> z@ag$YpY6rjf7Soak9l`+p>E&Oxqs^}Ex-A@c8Q+JzcT-M3D4_wbKZaDy!v^@XBioG za14JGR$zDEbvIBqam7RCu6s`(-QCSJm=9h>07DQoR3{PE*; zdDheZn~nP3O>^7Qzt-`y`mMX)w()7b6xsDU>w@uCzNzoHcl~frj^@4jicQ4m(naxO z&%SLF^P0&M3qnR(6N+O>{Z*F1Gjn%XnpKmTgo zp+^qfx7e3&Sd+5<`}zF$85<3+_Aa}o$D{jK=2bP*iGb}Qr)Ruc>FWI6CS_Lgr9Zp} zzATEF>&D;PKdn&HB<23`rN6Tq1Ye$AYFKjJd_ipj=jD>V8gxz2fPhC4$UdLvgJHyxmDkb zHdEK_4ck`nS4_U|T$PYAv(K^l<+0-*e(hCs;!4+8VQ-VBA^S3FamJzC%l9AKuels( zzBo->&g1j<_K5GIA;)GPHq;d6?SAupU3`k$nqvpL9V8-uDr~HpJNM6!WQ^Yl=tGpKf-&xN%Zhk?= zfjwlg#?EIw`Fckm9_I25iT2K@eb=5}|Hb`XOOxCDx;dLQ>e7X~w_Y!J#=mVxj2+X? z2dnv}f4<&yK{-OYu6@RP|ATK`<;7;){J-Xy-SP9h;FNH@*>}&Z)H5RP2d>VcoGOw|5R}NwNB1@^Q4(~--*h6 zH+-w`^Uu3ug_>WAF0e(O4%aF;eO%0}@V&vIFP>i$R?l=VUhukvo$Ynv@_ox!F;70P zcvE$v&oghO2MLcmPrJ6C*#FNe^K!9G7pLn%Bkh^0H&nHbU(L0Dzu3Yr{&%J9n&%Ho z=0C5T^VdMhvwr*ajR6xmn!E2+T-)kr@@m0`wTwElQ+Gx+&n~k+^w&-B*9jfhPv;kNYQo^?iXje8c#+>SE%oIeJ#Q&7p1$!%TgtIYsJhbY=^n^c7$1Ul)A~R zkZa1^q;$ldz2%i-rSZgVcFrrBHEra|xu4XB^Sw^?FWp`1{L}c;eya(s4WI8TKk|KV z_jCCX_fs}^&L!uh`G2XDnWq2s_qW2zw@)1-w47X?)Y`0#7d^i*nSGu3x@&xo&Mtoa zVaNUMv$mJRlecN|{#$-VAks{K!vETfzlAR~hgo)=x^jER=VhY7(pEteB}8iUzWl!N zOFn;dbzQpt{N$k8Y4;Q7o(?F#D)ugX^97Z}{pH6Qy_X()usR^(2K%F|vy7kW*T{YS zTb6L<{Z!AF-?O$qSLWN+zE0J?$YKpL`nONNsp43f!=aHp_<$2 zvP!4kmHwK)W*>X)5661VUkevM?XD929QXE=VViyhmCzSs>=zypG4U>(3nW38QLh5NZsZE}-8&;Dw9~G;bB&~=p`HQnIS2l3@f#auWzD9$ zU%VRPd_83Gx>APAM$^Q7%dSjss=vA>an*j#()p_bLoau1J6O3!yL7_&vW15Kwcebo zRQ#66r*n@Yr+M@KY@W1zVa#loeD3V;?pI8{Cnfb>&E)>{wD3zQZ$e@* z`>xn_ub(fsyq@v$^`Q%MWV9Ub7MF}=f z*0q{VlheOm%1J2PBphkp^dS3nhjxB;;Kd7l?;seL?i8N-XiMKLETy8(G?oN5~ef{KTWeXx_AA0b^X4=x%5%W^aeh7y4#!9H}7TC@q z_%pw{&p^GnV(GW!VC}<~BKUW)EmvIZkTBgO_wtQ^wda#JZ06q_Wcro&<-AjAPwI0G zq#ecmm0#t`Py6~c$t5Lo5&PNFD}I;abAKy{i99;_R(Gz1N5;|!iA;Cd*Wps)yuK;h zeR(t9yf}P>?P{F*;X@AEy{4~@OTAkBurep(#jWODiKUCrY};CNhOcbW-@se1qPFmD zSgYuyIKl7hB2#OzYkZ6xJO58R7O4E|*mMQ)lH=>{AA0tRht1x`ZOMFgpQibXo;dSH z?NZyYaqZ!l)5a|E8ysU^N{F?2wqLnEU&`9RX1xH%3BJwFbA?u4R+g9(*BEp5i|gAE zy`(LxJ+~abCHCc_W$Uz4f7bl-X%G4LNu+94cI%e;H-1EiaaL~r7wUcT*fBQ4-5WVy z`q6ZJD+$sJbetu0#{rFJl)T;DA`)8^&rJC_1>ubjX8_=;Su zW!|s0>+0UTt5Lp`!LYTp?E0 zHQJYT)vVoy+ZHMQSLrKUSi0HwMBfiVwZNq_614yOoX`_3Zhn!aazE6!?3!Dd+t;PD zCic&Fj6biiEbPXNJ6Elh7jm89=T9go;agN4o|wCgU-!!TPSXJ-1Dt`fP`rs>?Vs1W1(7cTX$G+nwOHE(;*T3ySVdHTQ7!xWS^iyzaR8?48( zxW40MD<9Xt*&B9tbY4{K(aCXI9Ch^05e_x6FLf;^>VL}S|H&wo3|+1C^ZkMaZ`TA$ z+>>|yH(l!8fgerm@51<#({{<{ZoSfPnkzBmuE3uy2KJk3wu@93yInmlC8b}S5SkRW zA%FI-n(bRBoShhZby95T)eApl4$AoE_#Dq(%(^2dp{3zXT+hVbZHD{9Zui`K$$QYO zb)({qXM7>Qe(s2p;9n~-WAV;T(VbiSUOue*6fC4M(@x^Xwk@rkJYV@H-e0ik%eQ5r zYZBVNSZ!Lzb)!~Ht&^{l(=z-+0wr?jXjT_H$WC49lY~#TMe7; zcbcie_uRs#Z+%%REf3r~DA;#y-3R}Q zwp$xRd#1;`_k5Q8bAJ8#2TdzYWn|tKEZ!h}*@f%ctxbjIHyF2V`&Lj=(3GAd)m0UK z;Mv4jr2{TsC4Z*7Z(A^}WbubOg*&lxA3S=aG3B*X348wQ&N+eB^cAU3T5h%+iK=V()6dm)qpfBaujO~;hfm+~s8?58 z?EbCz&6CyYddiHo^-T`J+%YTfHaN}Tn4xxMLSMS_l@n~!Pp5D2Q_21F*jUB2>d?uV zp9+c^(m(L-$w-;3q5YWq$^5wiJ&V=UIn$okANaoCo;NwWI)Y8*Vc1nBR%sUt)!2%9 zeSti^D|uP_nYZ4zeIi#hd)Z36keRmZ{%@5xEtyub;DMt}Q|z3FPG2{ae|LRy*Jg|V z=FYZD`qLk$Es&p+H1EON-6AT#tPh^MBQj0yWXSzWLw~c66Z}2}FA{w5(ruf=uT9_G zzFh8?NOs9cm-P;vo|y2U;l%UGipfQyj&gnm=7!DAXNs@*1wJ_R<|)U|>q$$uGp)SN z^zhL$l^Y%1#W~$J5ppj%O_7+A0Tw{o6)b&k5={uYRF6zG8F85DfsEURcZH|g=)el1?OFH+vLe^ zeQCb6(EK`w|EHE+m?!&I%57ELgRBN_nYl3Jmh8K-Zqd}5 z*8Q_>*SwWk@lTG!`)`MxqSdYU>1JA?pKE{ocq7%c_wIvNDeT$O;w^VI9gYhrpRlnHe99Re|nl{n?EfS7wl^* z*zt{Tt@o6+PeP|t#bs3gTfYkBnYLW%;$6mbeovTZ-Y?;GN=nmpyI#IPX7z#GcJG4f zd)_T!8@MvUsy-ZgYO-&0ecLq4#QfP4&U-A=)~S_WP&23RWLv<@GReesdWUADlydL= z$kzKtQaCO4wW&uhXG`pZ+?1zx+;*(CeEE&9=(m}sd0_aOEy_>k_g?PQT5_EI@b#OW zHT*wQ%;v>iOPa%^sw1X&>k`|;$hR|u*(T07|E-91-Gu({`y)GDt<+xX9C&P@zhhE= z>6Y@g+4(YZqT7?*-tYc)^1s59DA}Jg;`Z!)=wEy4Pm}GOJ&Ou==ZIwIxHi4%o%ym{*G0Tv3T6sewF)?;ikR4%h)+Se{(7Nsb%JVPvPKQrg#0j zG=D7nylSI;?;rEKtrsh$f849?owNM9pl9*ar5icl9F|{PQxdCkdqU!(ccyvnI}Y{! zRJ0baV2O3C3YBk>e!jb|)wHE5qjpJO$s0!LYW5p9Zyr|tp>;`G^~vLHnQ|I#Mn9$7 zXPE4%nA0O5uO1OK*!@hy%>G8O-Md0fjTg~Pe&%i4 z4c8TLRyC!~3~+q)rt*qS+3}gdd^_%{+){sS?^$j3XpKz4-olAZtC?cj-T7q`8{>X^J|N% z3#QFk@9=)Z(itv^I)Ysb+<#b{y(1gD{a(uz0eP2u+6N^*AML9Bbl=;rEPJb9{`#sz zdM3cWP3*L-pkq>S56s*mGYX2=L*cc+Z_4nu(cPbTJ))lW?T7Kqj_Id zvmN&i^iWLFn_-!{XmQcI9T5l2-t64*)p>j0!e?b$c+YgKmie{K(9+{y_{_(X`WL)X zv~-xPV3Qjs;;}hxrEKfTZw3ZEju(4GW=~4Hv)!=ee=ir0f&9*`E$2eyC8T~SXzu5i zwt80kwd}-}&EEfnKO3lt-0E(cZ8A}C;*TvZUl%&>S{h~3{jufu+%z5iGDY=sLT(#V zXH4SCE^R2{+xs%lpvceh?}0(-l}*;t%`0}iZ`-`=phx~>4i#_R%%i(2q$MPdE|_vxHX$=} zlTetaDff?(%c6XY{T~u%2nz0UxY&O^QIBJ9c!QqU;k988^BNTYechw}ZYm##@S41j z%I~Tr4j=k!7xkp1;l$s9V8ggQzdAG%|F?^Ii?FcHh<;c!SLD-M-ZfWM9-sNXyztz2 z!7TTgkIFNWyqpg#egE*&3bw^wTGhTP|L2)|UpQZJr^xhO`;J4uv*fBozsz>AozNTc zKcPi<;iSz|H^uIiVGur>#2)wS4}k`2mtzN1BwKWjh&3v^;bY1kEMpq}vUr((A zw%lvn{qg;W=IgdozN`!UpO?Jy9iz=TLB}2O{M#NnU;M}P@YmFO)vw3B(!~yS-q~gM zx;*rG}&9#c;{MS?FAk8+qL2nRW=SEJAy8B@A+zXXK(Vy6;<<8 z1w3v!%5I*Pb2jXuUyppuyV9=P^IMi>8{GdWwD14>a*Ovh!mg87rxZt>FLAB zQoCDWE}PcBsOV!6m;SLwLg2)$V-A9OlK;)?HuN0pURIeCRGs3?>apdoV4LUEh-*2C z#p?vW=boQv@8kGZ#aK`G8hf1bp;y%s0SoT$JmmG4>F0Xk*Cz4bB^0N%73}jXJI~X2 z;?;%Ut3Mc-wpRa=2>E1Kv*&S#cizkw!g;S(Zm{|!^)6li!JqeYKWs{$SG07_^QmuRBt z^WxuWruWqUKb;Yf>%99$o4Me%0v|2ceis?_Pm-IY)umOQl{#(Gbv|uc9noU%{&dTO zQjRE}r)>vr>I(JGetKi3{-IumaFz+N=Qu1cY{;$b+i`%wW$%1;>0_7T-2V9{{;m<} zTI<^LtmPrk&-*$`Gon-EFTE_Q$&fibE2e&VQ%-m9~>b1`7A76EorXrgI^!cEB@D7V{*UoVSoCE>f@__ zT-fbZwzj#4sY_$^1)r<4e^Vj?xi8qrz zEeQ_zy7<#N^>erGxE+;QGyTw5uN%D1 zEighiq?dt17FB(=+&|Z&-a|=Av_fTOXvFDF2@+v1ER?=y!pv z8qRksr)Ip(aQDuZwu<)ppmkBW?VJ9EjTQ~%oy&Gegmvr_EmRWxCX<)BAeEop=#%{i zCO@}}6_*UOCvZMHTB5ym$I~?K*9Y1)UP~U|6QVm|qV8P38{E5Z>#6;IvY+)ydS^(w z^2s|AdHR`2f)$L#r>!*9?knFr!r_?DW?&i8^`iLNjyNl~%M&zaH6Ab6(s5{R7iXi* z8$qwI)wZAJ_j6ip51ijraJ#!;-#0ngSlNusOxN`5>U(xcF}Y99&X~SFamgF2H9XB3 zo1)X#F8j3l!+~22bvN%ugn0zk-(^aFlk{=V7EamRwHdj&?%AKYf6Om;EOJwpcptH_ z^TX*U@x6_IPIGKl{URA$?lGeQN_{FRCPJVT_vfkIOxafTS^s0i(x5c)J{NdT$6fw=E zarXQlhmR`>R2u6z&oVNVT5*+YjW7S=?aQToUHb*|N(III**2`1mb~t7$tr2KZcm(D?Sd0oR$sOQHgf34S{NCQ$dS!>qHFH;xE>Tz1m` zUXiq3daS0w>*WknLnlQof929;&sr{+Z{X#+%IeFL|DO70kZYR*_kgxBJO|o}^E|I&1P5eqARk_%vEWQb@h%TCVl2(ku6h z+E#iq-Rlk3-*&HQ_H^;2Q|4c8T~6GkAo%dyJP-NnPUdlqx8F@qkel5W6XsrWZJZEb)A4rk%Za)?%=Z!se7k=3dm8+@D6YwC z%)}@zy1!}F>1`i&OzV9%KkDcD^oyq@xYq3mW~dPk6%pLDd%4r^smv>Oy>+$wz&I<# zs3-hh^Xx!|dwyCAgLRWM>^ZI#@_$gd!0~XWY{a59X2 z@uud+KAQ37)uH3n=L|k}9k?rI5xu=qJn6z2v5h8V)FyS2Ul%!|Cg+F;{0jD z`2P%L>1PH1?_&FEGA}AR`eIyo=bI1mg=SXLcZIknRmmNGIf*|{tiOvT=xXcpx@#YL zPP^Lwd^_>^ne&zkcFP&>KG_|0%j&|#4^o}$)){<1cbsYF{w?3RA=lg$PN!qp=M?Yi z`m@lpu`P>s&%NbS92=jj<&=B(M0pt3zbFa$H$j#9c6UBEObnH|x0OFR zYLUpPJ4xMMCTZ8*xF^;eWVk1}cZM)e|GH3*3k8=s#23deN}u@Ug74>xuIWcMIz&ih z=p>gNf2_FY>>Po)#(Y;FRjd50KB&Jnydo^|+hcAU-wzHiuCjN0+}j;?ZQ9Z_k$VMA zXHCtP=Ok{`7yp>f`|x{}fY9F^H>T+?`qibVcO)e-KuPb<+wa`R?WMr8F!sDfa*yJi z%qHd~g$vLrfw$$dqwWGJ>UOd%3fmD;Ug_e&6n+e;&Sfx0c{=e*v-5ZkFvQ=v%j4o9F}`@ zpQyT~S!Ax`#mRbW{@AaZDbQ)L`uR1rW8U)0*Y0toeY5MRozUDrH#j0RqvhB8ZJj6W ze&#D%tvXtASh+>&#-q5^ZV_*jbJOyIwcj3p&7eHTU+3_J9DOe3mbEyV+lfl5{&gx!`3GOA&kc!ke?416MGfn{U_qdAd*Hs_U|+4?k$KmJ|8T z*?7i(@_*kidwdt2R2SK491?%cLupR({C7etw;XORGxgn|q?oYWWOZ!C%W}@=le7#^ zgZUylS5Fk9{K8ypCzSbu*n zVq);RIDPg5F554)@A+`qpv3R^g04Qp+L~#76SvB)c-AeDTf`L>Zl9Db1M)-uLYfvU1qCpL3`zeY$vEJ!7`lrbo_i)7Hnu&VTK2 zboK5IzJm76s=Z$?-+!?G-&yhVQqMDVHYvQ2yHlI=v0<-Y*OSjSE}!#x`N{kwVUzmm6c_bm2<1q%xw zJP>WKj-2{pM#QoQuLAyR+P{5f(wq{_lV0t6W=_ z+OF6KKVbjeWC5! z66_==xQ^H7Vublu|HJKpMN^C;KWyCB5dXjN*pi>`W|%Z~9*msBS;jZHLkYS;<#<><1tH|0k8+bLLx(_~+m0{+7y*vZik6 z&0Xy8|8G98qu;OabJriv+|R1xlPf6jS75yc$BdY;j`Affzb?hE+URH3+*J^K$$t0c z<<9?=<2OAn+OqYDlGZ!6pzP}lCF?s+JTN%2XST?nMTk6k|Br2*iJ|?2&zci#-?pwkKTGk>n#Jdq=Xjb#ysPfFUbjS? zTf9Yex$^pT@}EUogSaOCi8pDf|2Cn3z5nF7XGXh%&m_$G&~?Io;urr;17rU~n}4}3 z)O#Z-s`z>-m&)#_%QfL{2QTW}Q)M}5`^8kPMCi)W`>v0s8kKz8e|+%_1{`oh__dAOHHDcSiSIa#>l}kv}%m%$_9N$^74V zKU`v2ja!khV9JZ8-@KnE=$v^QYizE5@K&4D>|)-ECwUB;tKZJJul!%>p-_#^L^HR4 zvU*1qw&r;iJbChzrD*r!P^Wz+y%(?9EK#dG@T5n1(fn%(j;A^H-T=3Nr=Hl)x4Gv} z{FXz#X=@$oW;&;xy+7&eqt<>#J(c<+Cw{3dS?e$G>JgWBI~NO3$xxadrv=}G2@lHACIrp zNtZp>SN_{2DbgZr=y}fd@dg&|&sDMK&m|@nvKlSj(VF#7ef!@RC-=un+~d4mH)DlC zT&?)Q=W7dhhwj~ee9qIQv9AvpukYSbv6LE+E7fs3P^!A(nv96)VRfu_=eTHr$w1yE{R^CvI=UIUap3x4$22nkKTN>LNLjz zEU4$&Hk+8z4M(T?b6*H@od2HFZo?=23H6d%i#nei-G6y<{i$xvnRkC`aJ0@{$}Jb7 zb0J@|itC-inUf;l^zsS{zc@VjsuSXWVqtPi>sRyM*`_B|cCFr2pngGc*|cvS8nXm` z1=eil`F;9bOX#Cr&8jj^?;e%P_Ws{#`Q^F$!8cR%+}{e zYj0N0(5q-k@lXDfF}dUUlXwYLK~8DWh4YRtcNZ+!^Q7a(bNLIlTRWZ}bXxKw>fkMX z{>xL!`u{Bd+Q~o1Xko)b+vbV?%U{Gt9eW_{Kc~E=Sw+J zci2RGp3-JhR{i;t|CjPSJ%0GtBW)knRXf^{oRoj!EF^r@IxrKGf)@v(OI{Qi@nmS!{OE#>v#RsJOZQR>LY75@F_ zX0JImfAW7*pD8K_FBmXIIt$ORez93x<;nBVL)JeR-ag{wC?Io3Xu@UFmRq~y3V13T zxV{}Ye$L7+_H)B*U!9`r=^HJ|3w+i&cvTvR%G5mwzj0VE_TZfWVXojs50YI|j4h;d zxxKvhOG^ZQv$C9ax+(E}z4X$z@)Ly0c>ObaR%|;yQBt6Fx8&3xGiJA(SaJHq!ibj6 zo)h(F;(h+zE!GR^-Qt6lRxh-bv|0MR> zeb%?7G_FrKxEkBKX10jT?C3*!JDDa+$u3ED-BHuHb{~I2;Cl{9#`iTjY?EDVf3Ejj za6pSKv+9$6kL|RAjXa%vVg-8wnK)%7Yh{}Sq!wGsbANo6$g+rABPq1x;n%|~6}D?! z9`C;WVUv&B)RwI|+^6@t{5vnHWRT8ssLyhe>`|dv?*r>q(vl0lujNoVvy88=ysw@I zw6@rCA4hMO+Le`=9$`-scB`-`GHnQ|7dM-}=i`Pnp;a z;I^$Rbf=Wms@aRIMZd8v-0?p%sNhu3gI8N-PdGU>;FbJ@+qd2v39b*mQE}ICvO~<# zGQ*qd{~v9e-`mJ^XN!5`+2*h_oTn{(rbN6u>F~idx#fxf1r_t&W7=XI`=@uGx@O12 z^DQYMMX30+(F)0#HZM-hJZ-z-`TVoyx8_aX@g|k=zPjv<+G|}>ZMvqcIwARM=V^HS z+B7jGx#=1+I9427@^7uV31iT@ z+mo_lwwbyfm>lKv*0AW6tJ3Q=_owDG*);x{z9!f4YJ7*q^6m}ZFWbBn4qM-Nq3|kg0{VhKw>19XC=Dl0Yju~|7I{oJP-O)dxQNj9lR*p~D z<7tx(Z@wrtIWw6l<4|uNGyf*Z#E1&)_ErsF!2aOrK?HoVv@|xlD{dZsJ zgAd#0%YIo_ec*r@g)ebw@=pU&HLloW6au6};)3;Ug}4-JRkial%$a|`;H z^J)E5=Upo=-amS)sq6ZVRl9{w_V?p1j8BwyG>*BKXY-#X{RDOVkJ=+gVU zolovtsE9jfXU|yg)$}pb-X>?pz1_lF*Ge4U&B1LEd(QE+f8OuJX@=eW<$Dv~%5;|t z=a{@qUCR5jlW)h1KMcM>VmZ2ri~jKCod2!xa+BevrZSrrQ2h0Ri&Hwf|N=vH!)qEL2Np>wbp`6Is`V>2<8W z+7xiteCds{$7PR$3OFWgagQ{3-cqQ@?$T92zKpQca#f7?V%WDbZZu&Oi&SlwF z_6q;63g0;`uPn4eLFj|TJGEyY_QZA^i?g~P+RNWBT)4b~<@4+(uiWDf-oGN^cRg<5 zmSv2wZ%Ugs+3l(S#-yolzc4+6r*`X0j`9Mh2YdTxq-qEM`eqo&yXUCyb@_zCosKox zU1@xCEiY~`D0K|Vll`?)wwAZjgL6~s($ltAbT8+uig9?In|>o=#(_J>82i4JZP>iE zb#Jv%+%+GEQ**xMF&ubt>=S#;cZ2rI5IGS)Z!^T`W-PYiOI)#B?tR>T zhgYlB)?6w*^ENxIEtBb|*#3X>?H^cA&-s#XaLw1{>HqupO6`wMRe4$eT5r0<^Uc%J)5_D1a9oaf*fN>fL`dj_V*dnl4<<{Ko;?n6C#nVF7O-8skyj&q>vHeq z^2o2Z7}qA2JvqYzT6@;2 zQ-L8Nec@TDThm|o=?Nt@%YIU|lWl(X&v6Zh=aE~J+AD(No0sLOFFW@8VnHj@`5CDi zD^@s3pZx!i_vvmk#*e4_7T?kcyJe$ob>;f9lNVR(KT*`JQ~efO)tKgW|K6pB<Ux>JJ2YrYy*J96B-dq@L-|2ZbTV#ExV%}-fD=VxoF0Jde zX5SMruxgD-~M=Y`u)VJ4}5W3%bYKV^{b~BO^}${c-Vhe;=%`C7QbZu zcC^0ri*5ObxSqUZ-5pUoggRH3J-o6xY?3>3>Eq-n^Yjis_3GV_XA*z6@yU&=8mqRS zi%J#Wf7>vidR~qWn|GMlqfeK&rwB`F{kB~tKkxXuGb}F4-yS)ew)e_Lj(@)Exh8)3 z8uVt_ftW~6k;ZUiukiIw*M4=sP_Qn(dND;yktcJX^S+KLS8jb3QGZm|`~QbllwG-^ z-E6~|Us(RDePvDg|ML5Zy?2z}Uc2fqtn;ehbGF~ZvRG>?Q{NSOxBY6>WpC^4VLi%x z<^HU~()&^^`az18rlOiBJ5vsKdfY5v5|uKR+52^cO=ySl`IY)vb_>4!%Y34yceE{9 z_LSzKL)*I=(r>YyzMbsR8`E~E%WB1LkL6#l7phoG?f?ILs>N3?_r{~{JMPatx@(8W z8m&D|>iJR^oP>Fe@4nmYJSnAW1y}m@qZe3G^-s;ezIb08FSl<}X}-VWYkt8KyKl=2 z{3(AF=g6_|=4T#RrB5>N4_!6A|G0eT^+0{vQ{c!DAc5;P9 ziq|2-hFhCY3rs2%%$hB|ZsBx`ITDR0MBM}8mb-%Z}J_x~f} z@%o3hO?QiM)iJYe+jlCarS@y|pXwKZd#-dTO!r#S8IgXJZ;g+~;cdqJ7d`&DF3vl2 zdXut6?Q20Pmp_@+y_2&a2}}%b3rm!q&-L$j+~Y&L>=O##$(Hn9tVno!@N@Ia4&}Y` z)-KtWe|^E)Z=!kA-?+Y9ZPxSS+vDCvS@mp>{=Js}u=>g39be-nhTB|cNEX%0@pr8$ zQ`8IDdUE5Ut?$^p&c`{teC%lX)5J9X;IAD4N9JU<-(A|i?Uzj1{F;vG?@fPP_#`B{ zJ6K?HE#p;YzMs0&g>1N`McJ0Q{=DU)^Xq!OOU=jRnhDx!D&5bNexAAi=>5N3Rld8Q z?ANDeYaBiHBtXaZ*E!)o-Ol@rKD1`B%T@FJT=l<@cOOrr^pgqtT>tLBIb0rJ*L>nn zcg3Q24&pzTzVe6+l1e$m8Z-U-_ttyvMvRMs57C~cbY{!PQKZ$ex~C$D|-{+JbDVs$qo|HxM6&L7kF z{}(pee&3~k&I`Z8mulGA#s3S}$!-#vJ-;Wxr$lB_&61x++iJebZqnO&_(9-rZPQJeV&z=gEU*YHtnpRd+l(YpT&?BL;FUOi3m{}tyI zzK>ip>wXIql-@bKa*yf>t2qA2<)RntI*xVOo;cazx=N-Qm@$O}2zVPkag6&^*y^0@yxcf@D;?`1~?I(*`tmSJvC;85d*gBsrATa%x zkHwktC9A(?UR<);V8x?pNoN;wf6_I3^rK>@m0hai`R2C)t?tQxzv}J$es%xXB$>W< z=06Jk&4d;m5)>2bR9$WM;7M~@PsP3Cwtsdo++6*s;K{{?n9wim|L2`NTA-}Ow?yg9 zY=ISFZw|~1pW?S=aWMn$Qx*HI$y40_y|I0|;-%Tp_tV>yzSd~F%@OMfo9j?@EH0)hpy_Usx7Q4=i<;7be>`{##riia zKY4Mx#ZD`+*Zww#*w1hJ{Ubws-rW1Gq8|l0;(fNw-oy0sxz@sI`PzmHUw-maIkNuf z##@mG?E~`)f4^wkxu0?Bar2~7uA7=S7kar)ZcMGz(5dG8y1|!C;gdY8`LtP&e<jf=0*PN z33c@UkbmutLtTvuS6;2cZqd{WTl=0{Y@hx{vF(@V3a&uS*8zVVWSG?6%{=tr@#7o& z-wT|({>9OLg>$(-|1RnEk6%b}>6ra%QJc*8{?vCR?-LSYT_=v@y!pA3@9(*jGp#0? z8Fk#cWTWERo;Xp9)sj7b@v>)T-LQZWj zCbrhi-?H%WLC-bewY~Owev4}I7IZUBSa{6)hST3|A5Ko##M@_JdaHZ6tM|l$MO?N? zowwH>y({Z2xbwH(EZ!-{uC|?8qX!-uXlEyS!?t|e$8b4 zSP#t#p0zbbbAL(rGOW^n=Tuf?a&`I_ne9wF+utw9yJ4BUdcoFpEZIr>mvO!eF6mM) zv2|Q<;&5(7f3@n*2r=JnQi6>$p6H0osqW)J?E6<78 z&f~3g^1AC0rQBdax$+wsiwri`SDb#gYoDOzef7r&oO?gzYCe2iV!iJ25@nNQ`@Hh5 z6_Gy={QIZHb$v;~TT9a;mT8Zqmo7hA*>TRICw%dgyi*;|KIL}!-s`NkdipRr{)7G7 zmPWNtD^FGj6s={cE;1Gn6>_Uh=jFSvR4txgu;@#Y;Mt4u-LKoPDfXuD^#>^G>9V)4 zzqTOUMpfp9`U&%I<}ruwol&v;*Ro1zLS#hStb>N9(z%|!^OA|0)ZqJ6>c!rJ`!9D) z`@Aw@*L$OTr_~Er`b3+rI>ncmTcoo!SmRKLXGC<#bhT4SS$Zh~<+m;rt!3a`IP*rS zyY^Sjtwy(cPVM~|psRggapKWsEqf(z`*^zW90h1C!T=D=shEzFj=(u7D5U)4R{(o@>`UJYHz+ z<6QgV?Mv77WekVY*zY7=4OlW$rulACQ{ol9?Z;0{m3p}6ZkPnyl$z?&<_ohk1q2(H z?r=}BdfJql!cZIa@h5|u+uTnD;y*v=ZT-IFGSA-zzNohfJz-C`woFcX|31O~sb^#5 zeFL|-kBgZ#7u`rok-e(@{p5D{idQCi&5chkbXk4nb;vK&<|{bV_-s>X#FTQ*(&keF zI*z3|y1woy=M?Wu4_sb;LEWz>P?AkT*KfyD(|k#jl~R{9cL^2Ee9PE=jl(bLQ0mbW z9dUA5&rbUX?aH^%RhCfL`z65kcN@=!o$5>cTxJzs>b{sUDO&N*?Z`bMPkRoh2Hy%X zcSyC+SRs(>S~^>5x!FwR|1p0(yUOO595A`Fe#!N3$tPZXSR;1%xlgW z!;WWu5GkF##Ac0~txp*H-NjQ>p9q>6UGgto+^~InzJS{A8FNxjR^2#R$D=Qm6Z=yCT?xu} zC0lII)V}R-XCD~Y3HJtUE#m<#J?_Q{PcOs_G!N+-+NRkyXJk|s?#i67bZr0 z+uA9a>+n*ezwoz4+wYb$Q{3%;W?1`{*}hcs_@AcylV_J&&7?K!6FL5-+`hDb#*s+l z`5)Y;{68Y`veM;CX8%0alXi2{l)tEPzq}qVA1U@zee&7l?US#_g}r*s=B0is@)yUZ zWZR!A-*w~_{|fbrE{R?~`PbeLjZe;(GP~Va@wfKx^d~}pFXtL3d7OCgHa=9zzqjs}gY$XY^XF|j!WpTrb^CdF$04)Sn za+~MS_D<&;f4&?2H=DyR^T)V;-l5578YTXuEt@SEUh%;H@%iL$T3>R`#MFL`;c56^ z{O}ah!Y%*rRNKk!(Bk-0Q#Oz5$t(`mO@I5jHl%o}m4$~ayqvMT;Rr+Q=~R(*9j<;K zgEOAAeQ7XadcwEn@cGwX0X&oD@vq4FU&`OCbxglq|Gz)W1f~BDENrtJpU%H}t&t~A z^!@XD{OQ{odpue9{5*HASx960k=iBPKdTK`p1fYoargCJu`RaEFHg?4dldC%_g}`tYFw92=Ixf7dtlp|oE)3mA|L*EDm**2ykMQ*)a!@3l=haH zX!rVGoB8FX_o2zhmd|In{q>}~{nEHUpQ`@PYj*g{aw#}m{zd+K(V6$YS0>#57`?#! z{LE7E5Aht=$`~zwtUtf5+hM_-N&l>wYnPwrTXE-R(YdwKOFun*yC*|H_?z(<}O@)keRjp{jDYc0P9*jsk4T|HgNPK~e7(0js;D%-x0J#O1QYYrbUJMrhU#&Pd# zZaJC%jGdO^3(77vcr}0df9-*0(-F?ue5Kd>6N{s4rX4w^;P>YH`8%zXx6d}nn7?yt z`^gleng9Potr4`{nCh>yzRcjiQs3L#B|F|s!Y*M(>7+Zpe7RSD+g zn@s+B+Vb`5&;Oab_FgMKUT3_#vp<1acur!5<$B zaA3>?aZ-%zh0(P>+|2p!sG~7NdZy1ar)M=ka+>Pdnhz!zNbs;VfBgL5!F(wDV1&Zp e;s`MOV>e@|Sfx{GkjTKmz~JfX=d#Wzp$PyWGa(!R literal 0 HcmV?d00001 diff --git a/docs/_static/doc-code-member.png b/docs/_static/doc-code-member.png new file mode 100644 index 0000000000000000000000000000000000000000..eee897a496bd7ad895a57ca8c1669f7a7cc33576 GIT binary patch literal 75946 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 ZpE*ISOZ4R8A_fKq22WQ%mvv4FO#r-{g--wg literal 0 HcmV?d00001 diff --git a/docs/_static/doc-code-void-function.png b/docs/_static/doc-code-void-function.png new file mode 100644 index 0000000000000000000000000000000000000000..36eeced6b8bf0dabff9dbb43b4e1434d2ea662bf GIT binary patch literal 68465 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 JMIrT3?xD?ncNi~T>xP=l;= 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

)u%d`#&u* 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 . You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks like for example ``/* @param ratio this is oxygen to air ratio */``. + +Doxygen is phrasing the code, extracting the commands together with subsequent text, and building documentation out of it. + +Typical comment block, that contains documentation of a function, looks like below. + +.. image:: _static/doc-code-documentation-inline.png + :align: center + :alt: Sample inline code documentation + +Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get the taste of available features please check data reach and very well organized `Doxygen Manual `_ . + +Why we need it? +--------------- + +The purpose of this description is to provide quick summary on documentation style used in `espressif/esp-idf `_ repository. + +The ultimate goal is to ensure that all the code is consistently documented, so we can use tools like `Sphinx `_ and `Breathe `_ to aid preparation and automatic updates of API documentation when the code changes. The above piece of code renders in Sphinx like below: + +.. image:: _static/doc-code-documentation-rendered.png + :align: center + :alt: Sample inline code after rendering + + +Go for it! +---------- + +When writing code for this repository, please follow guidelines below. + + 1. Document all building blocks of code: functions, structs, typedefs, enums, macros, etc. Provide enough information on purpose, functionality and limitations of documented items, as you would like to see them documented when reading the code by others. + + 2. Documentation of function should describe what this function does. If it accepts input parameters and returns some value, all they should be explained. + + 3. Do not add a data type before parameter or any other characters besides spaces. All spaces and line breaks are compressed into a single space. If you like to break a line, then break it twice. + + .. image:: _static/doc-code-function.png + :align: center + :alt: Sample function documented inline and after rendering + + 4. If function has void input or does not return any value, then skip ``@param`` or ``@return`` + + .. image:: _static/doc-code-void-function.png + :align: center + :alt: Sample void function documented inline and after rendering + + 5. When documenting members of a ``struct``, ``typedef`` or ``enum``, place specific comment like below after each member. + + .. image:: _static/doc-code-member.png + :align: center + :alt: Sample of member documentation inline and after rendering + + 6. To provide well formatted lists, break the line after command (like ``@return`` in example below). + + :: + + ... + * + * @return + * - ESP_OK if erase operation was successful + * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL + * - ESP_ERR_NVS_READ_ONLY if handle was opened as read only + * - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist + * - other error codes from the underlying storage driver + * + ... + + + 7. Overview of functionality of documented header file, or group of files that make a library, should be placed in separate ``README.rst`` file. + +Go one extra mile +----------------- + +There are are couple of tips how you can make your documentation even better and more useful to the reader. + +Add code snippets to illustrate implementation. To do so, enclose the snippet using ``@code{c}`` and ``@endcode`` commands. + +:: + + ... + * + * @code{c} + * // Example of using nvs_get_i32: + * int32_t max_buffer_size = 4096; // default value + * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); + * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); + * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still + * // have its default value. + * @endcode + * + ... + +To highlight some information use command ``@attention`` or ``@note``. Example below also shows how to use a numbered list. + +:: + + ... + * + * @attention + * 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode + * 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. + * + ... + + +Use markdown to make your documentation even more readable. With markdown you can add headers, links, tables and more. + +:: + + ... + * + * [ESP32 Technical Reference](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf) + * + ... + +Wrap up +------- + +We love good code that is doing cool things. +We love it even better, if it is well documented, so we can qickly make it run and also do the cool things. + diff --git a/docs/index.rst b/docs/index.rst index e194992089..da03346e71 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,7 +3,11 @@ ESP32 Programming Guide .. caution:: - This is DRAFT - mind your step + This DRAF version of documentation developed within `ESP-IDF 1.0 Release plan `_. + It is scheduled for merging with `espressif/esp-idf `_ repository at the release date. + Before merging it may be incomplete, or not fully in sync with espressif/esp-idf. + Please mind your step! + Contents: @@ -58,6 +62,7 @@ Contents: :maxdepth: 1 contributing + documenting-code contributor-agreement .. toctree:: From 73944e680053610e7718cdb21763e28dae08b246 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 30 Oct 2016 19:54:10 +0100 Subject: [PATCH 088/285] Typo corrections --- docs/documenting-code.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/documenting-code.rst b/docs/documenting-code.rst index f07f464ed9..51a0dbf7d3 100644 --- a/docs/documenting-code.rst +++ b/docs/documenting-code.rst @@ -4,7 +4,7 @@ Documenting Code Introduction ------------ -When documenting code for this repository, please follow `Doxygen style `_ . You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks like for example ``/* @param ratio this is oxygen to air ratio */``. +When documenting code for this repository, please follow `Doxygen style `_. You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks like for example ``/* @param ratio this is oxygen to air ratio */``. Doxygen is phrasing the code, extracting the commands together with subsequent text, and building documentation out of it. @@ -14,7 +14,7 @@ Typical comment block, that contains documentation of a function, looks like bel :align: center :alt: Sample inline code documentation -Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get the taste of available features please check data reach and very well organized `Doxygen Manual `_ . +Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get the taste of available features please check data reach and very well organized `Doxygen Manual `_. Why we need it? --------------- @@ -35,7 +35,7 @@ When writing code for this repository, please follow guidelines below. 1. Document all building blocks of code: functions, structs, typedefs, enums, macros, etc. Provide enough information on purpose, functionality and limitations of documented items, as you would like to see them documented when reading the code by others. - 2. Documentation of function should describe what this function does. If it accepts input parameters and returns some value, all they should be explained. + 2. Documentation of function should describe what this function does. If it accepts input parameters and returns some value, all of them should be explained. 3. Do not add a data type before parameter or any other characters besides spaces. All spaces and line breaks are compressed into a single space. If you like to break a line, then break it twice. @@ -76,7 +76,7 @@ When writing code for this repository, please follow guidelines below. Go one extra mile ----------------- -There are are couple of tips how you can make your documentation even better and more useful to the reader. +There are couple of tips how you can make your documentation even better and more useful to the reader. Add code snippets to illustrate implementation. To do so, enclose the snippet using ``@code{c}`` and ``@endcode`` commands. @@ -122,5 +122,5 @@ Wrap up ------- We love good code that is doing cool things. -We love it even better, if it is well documented, so we can qickly make it run and also do the cool things. +We love it even better, if it is well documented, so we can quickly make it run and also do the cool things. From 8d6b78232728d7a987a2cae690fdd820cf8cf36a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 1 Nov 2016 09:22:09 +0800 Subject: [PATCH 089/285] Modify UART driver: 1. Add a ring buffer for UART TX. If the buffer size is set to zero, driver will not use a buffer. But we need a task to send data from buffer to fifo. I tried directly copy data in ISR, but the code looked too long for ISR. 2. Modify the format in uart.h --- components/driver/include/driver/uart.h | 712 +++++++++++++----------- components/driver/uart.c | 421 ++++++++------ 2 files changed, 633 insertions(+), 500 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index dafdf54a84..3ea77b2d02 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -24,175 +24,168 @@ extern "C" { #include "soc/uart_struct.h" #include "esp_err.h" #include "driver/periph_ctrl.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" #include extern const char* UART_TAG; #define UART_FIFO_LEN (128) //Do not change this, this value describes the length of the gardware FIFO in the ESP32 #define UART_INTR_MASK 0x1ff #define UART_LINE_INV_MASK (0x3f << 19) +#define UART_BITRATE_MAX 5000000 typedef enum { - UART_DATA_5_BITS = 0x0, //word length: 5bits - UART_DATA_6_BITS = 0x1, //word length: 6bits - UART_DATA_7_BITS = 0x2, //word length: 7bits - UART_DATA_8_BITS = 0x3, //word length: 8bits + UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ + UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/ + UART_DATA_7_BITS = 0x2, /*!< word length: 7bits*/ + UART_DATA_8_BITS = 0x3, /*!< word length: 8bits*/ UART_DATA_MAX_BITS = 0X4, } uart_word_length_t; typedef enum { - UART_STOP_BITS_1 = 0x1, //stop bit: 1bit - UART_STOP_BITS_1_5 = 0x2, //stop bit: 1.5bits - UART_STOP_BITS_2 = 0x3, //stop bit: 2bits + UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/ + UART_STOP_BITS_1_5 = 0x2, /*!< stop bit: 1.5bits*/ + UART_STOP_BITS_2 = 0x3, /*!< stop bit: 2bits*/ UART_STOP_BITS_MAX = 0x4, } uart_stop_bits_t; typedef enum { - UART_NUM_0 = 0x0, //base address 0x3ff40000 - UART_NUM_1 = 0x1, //base address 0x3ff50000 - UART_NUM_2 = 0x2, //base address 0x3ff6E000 + UART_NUM_0 = 0x0, /*!< UART base address 0x3ff40000*/ + UART_NUM_1 = 0x1, /*!< UART base address 0x3ff50000*/ + UART_NUM_2 = 0x2, /*!< UART base address 0x3ff6E000*/ UART_NUM_MAX, } uart_port_t; typedef enum { - UART_PARITY_DISABLE = 0x0, //Disable UART parity - UART_PARITY_EVEN = 0x10, //Enable UART even parity - UART_PARITY_ODD = 0x11 //Enable UART odd parity + UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ + UART_PARITY_EVEN = 0x10, /*!< Enable UART even parity*/ + UART_PARITY_ODD = 0x11 /*!< Enable UART odd parity*/ } uart_parity_t; typedef enum { - UART_BITRATE_300 = 300, - UART_BITRATE_600 = 600, - UART_BITRATE_1200 = 1200, - UART_BITRATE_2400 = 2400, - UART_BITRATE_4800 = 4800, - UART_BITRATE_9600 = 9600, - UART_BITRATE_19200 = 19200, - UART_BITRATE_38400 = 38400, - UART_BITRATE_57600 = 57600, - UART_BITRATE_74880 = 74880, - UART_BITRATE_115200 = 115200, - UART_BITRATE_230400 = 230400, - UART_BITRATE_460800 = 460800, - UART_BITRATE_921600 = 921600, - UART_BITRATE_1843200 = 1843200, - UART_BITRATE_3686400 = 3686400, - UART_BITRATE_MAX = 5000000, -} uart_baudrate_t; //you can set any rate you need in this range - -typedef enum { - UART_HW_FLOWCTRL_DISABLE = 0x0, //disable hardware flow control - UART_HW_FLOWCTRL_RTS = 0x1, //enable RX hardware flow control (rts) - UART_HW_FLOWCTRL_CTS = 0x2, //enable TX hardware flow control (cts) - UART_HW_FLOWCTRL_CTS_RTS = 0x3, //enable hardware flow control + UART_HW_FLOWCTRL_DISABLE = 0x0, /*!< disable hardware flow control*/ + UART_HW_FLOWCTRL_RTS = 0x1, /*!< enable RX hardware flow control (rts)*/ + UART_HW_FLOWCTRL_CTS = 0x2, /*!< enable TX hardware flow control (cts)*/ + UART_HW_FLOWCTRL_CTS_RTS = 0x3, /*!< enable hardware flow control*/ UART_HW_FLOWCTRL_MAX = 0x4, } uart_hw_flowcontrol_t; typedef enum { - UART_INVERSE_DISABLE = 0x0, //Disable UART wire output inverse - UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, //UART RXD input inverse - UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, //UART CTS input inverse - UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, //UART TXD output inverse - UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, //UART RTS output inverse + UART_INVERSE_DISABLE = 0x0, /*!< Disable UART wire output inverse*/ + UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, /*!< UART RXD input inverse*/ + UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, /*!< UART CTS input inverse*/ + UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, /*!< UART TXD output inverse*/ + UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, /*!< UART RTS output inverse*/ } uart_inverse_t; typedef struct { - uart_baudrate_t baud_rate; //UART baudrate - uart_word_length_t data_bits; //UART byte size - uart_parity_t parity; //UART parity mode - uart_stop_bits_t stop_bits; //UART stop bits - uart_hw_flowcontrol_t flow_ctrl; //UART hw flow control mode(cts/rts) - uint8_t rx_flow_ctrl_thresh ; //UART hw RTS threshold + int baud_rate; /*!< UART baudrate*/ + uart_word_length_t data_bits; /*!< UART byte size*/ + uart_parity_t parity; /*!< UART parity mode*/ + uart_stop_bits_t stop_bits; /*!< UART stop bits*/ + uart_hw_flowcontrol_t flow_ctrl; /*!< UART hw flow control mode(cts/rts)*/ + uint8_t rx_flow_ctrl_thresh ; /*!< UART hw RTS threshold*/ } uart_config_t; typedef struct { - uint32_t intr_enable_mask; //UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator - uint8_t rx_timeout_thresh; //UART timeout interrupt threshold(unit: time of sending one byte) - uint8_t txfifo_empty_intr_thresh; //UART TX empty interrupt threshold. - uint8_t rxfifo_full_thresh; //UART RX full interrupt threshold. + uint32_t intr_enable_mask; /*!< UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator*/ + uint8_t rx_timeout_thresh; /*!< UART timeout interrupt threshold(unit: time of sending one byte)*/ + uint8_t txfifo_empty_intr_thresh; /*!< UART TX empty interrupt threshold.*/ + uint8_t rxfifo_full_thresh; /*!< UART RX full interrupt threshold.*/ } uart_intr_config_t; - typedef enum { - UART_DATA, - UART_BREAK, - UART_BUFFER_FULL, - UART_FIFO_OVF, - UART_FRAME_ERR, - UART_PARITY_ERR, - UART_EVENT_MAX, + UART_DATA, /*!< UART data event*/ + UART_BREAK, /*!< UART break event*/ + UART_BUFFER_FULL, /*!< UART RX buffer full event*/ + UART_FIFO_OVF, /*!< UART FIFO overflow event*/ + UART_FRAME_ERR, /*!< UART RX frame error event*/ + UART_PARITY_ERR, /*!< UART RX parity event*/ + UART_DATA_BREAK, /*!< UART TX data and break event*/ + UART_EVENT_MAX, /*!< UART event max index*/ } uart_event_type_t; typedef struct { uart_event_type_t type; union { struct { + int brk_len; size_t size; + uint8_t data[]; } data; - }; } uart_event_t; - - /** * @brief Set UART data bits. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_word_length_t data_bit : UART data bits + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param data_bit UART data bits * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit); /** * @brief Get UART data bits. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*data_bit) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*data_bit) */ esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit); /** * @brief Set UART stop bits. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_stop_bits_t bit_num : UART stop bits + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param bit_num UART stop bits * - * @return ESP_OK : Success - * ESP_FAIL: Fail + * @return + * - ESP_OK Success + * - ESP_FAIL Fail */ esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); /** * @brief Set UART stop bits. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*stop_bit) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*stop_bit) */ esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); /** * @brief Set UART parity. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_parity_t parity_mode : the enum of uart parity configuration + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param parity_mode the enum of uart parity configuration * - * @return null + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success */ esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); /** * @brief Get UART parity mode. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*parity_mode) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*parity_mode) * */ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); @@ -200,32 +193,37 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); /** * @brief Set UART baud rate. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t baud_rate : UART baud-rate, we can choose one from uart_baudrate_t, or set a value. + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param baud_rate UART baud-rate. * - * @return null + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success */ esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); /** * @brief Get UART bit-rate. * - * @param uart_port_t uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*baudrate) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*baudrate) * */ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** * @brief Set UART line inverse mode - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t inverse_mask : Choose the wires that need to be inversed - * (Should be chosen from uart_inverse_t, combine with OR-OPERATION) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param inverse_mask Choose the wires that need to be inversed. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (inverse_mask should be chosen from uart_inverse_t, combine with OR-OPERATION) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; @@ -233,57 +231,65 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; /** * @brief Set hardware flow control. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_hw_flowcontrol_t flow_ctrl : Hardware flow control mode - * @param uint8_t rx_thresh : Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param flow_ctrl Hardware flow control mode + * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); /** * @brief Get hardware flow control mode - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_FAIL : Parameter error - * ESP_OK : Success, result will be put in (*flow_ctrl) + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success, result will be put in (*flow_ctrl) */ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl); /** * @brief Clear UART interrupt status * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t clr_mask : Bit mask of the status that to be cleared. - * enable_mask should be chosen from the fields of register UART_INT_CLR_REG + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param clr_mask Bit mask of the status that to be cleared. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); /** * @brief Set UART interrupt enable * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t enable_mask : Bit mask of the enable bits. - * enable_mask should be chosen from the fields of register UART_INT_ENA_REG + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param enable_mask Bit mask of the enable bits. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); /** * @brief Clear UART interrupt enable bits * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint32_t disable_mask : Bit mask of the disable bits. - * Disable_mask should be chosen from the fields of register UART_INT_ENA_REG + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param disable_mask Bit mask of the disable bits. * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); @@ -291,42 +297,46 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); /** * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_enable_rx_intr(uart_port_t uart_num); /** * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_disable_rx_intr(uart_port_t uart_num); /** * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); /** * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int enable : 1: enable; 0: disable - * @param int thresh : Threshold of TX interrupt, 0 ~ UART_FIFO_LEN + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param enable 1: enable; 0: disable + * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); @@ -337,29 +347,31 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); * We can find the information of INUM and interrupt level in soc.h. * * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint8_t uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * @param void (* fn)(void* ) : Interrupt handler function. - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR" for now. - * @param void * arg : parameter for handler function + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param fn Interrupt handler function. + * @attention + * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. + * @param arg parameter for handler function * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); /** * @brief Set UART pin number * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param tx_io_num : UART TX pin GPIO number - * @param rx_io_num : UART RX pin GPIO number - * @param rts_io_num : UART RTS pin GPIO number - * @param cts_io_num : UART CTS pin GPIO number + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param tx_io_num UART TX pin GPIO number + * @param rx_io_num UART RX pin GPIO number + * @param rts_io_num UART RTS pin GPIO number + * @param cts_io_num UART CTS pin GPIO number * - * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num); @@ -367,97 +379,107 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r * @brief UART set RTS level (before inverse) * UART rx hardware flow control should not be set. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int level : 1: RTS output low(active) - * 0: RTS output high(block) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param level 1: RTS output low(active); 0: RTS output high(block) * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_rts(uart_port_t uart_num, int level); /** * @brief UART set DTR level (before inverse) * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int level : 1: DTR output low - * 0: DTR output high + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param level 1: DTR output low; 0: DTR output high * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); /** * @brief UART parameter configure * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_config_t *uart_config: UART parameter settings + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_config UART parameter settings * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); /** * @brief UART interrupt configure * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_intr_config_t *p_intr_conf: UART interrupt settings + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param p_intr_conf UART interrupt settings * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf); /** * @brief Install UART driver. + * * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param int buffer_size : UART ring buffer size - * @param int queue_size : UART event queue size/depth. - * @param int uart_intr_num : UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param rx_buffer_size UART RX ring buffer size + * @param tx_buffer_size UART TX ring buffer size, if set to zero, driver will not use TX buffer and TX task. + * @param queue_size UART event queue size/depth. + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param buf_type UART RX ring_buffer type * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type); /** * @brief Uninstall UART driver. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_driver_delete(uart_port_t uart_num); /** * @brief Wait UART TX FIFO empty * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param ticks_to_wait Timeout, count in RTOS ticks * - * @return ESP_OK : Success - * ESP_FAIL : Parameter error - * ESP_ERR_TIMEOUT: Timeout + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + * - ESP_ERR_TIMEOUT Timeout */ -esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait); +esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief Send data to the UART port from a given buffer and length, * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param char* buffer : data buffer address - * @param uint32_t len : data length to send + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param buffer data buffer address + * @param len data length to send * - * @return -1 : Parameter error - * OTHERS(>=0): The number of data that pushed to the TX FIFO + * @return + * - (-1) Parameter error + * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); @@ -465,12 +487,13 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); * @brief Send data to the UART port from a given buffer and length, * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param char* src : data buffer address - * @param size_t size : data length to send + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param src data buffer address + * @param size data length to send * - * @return -1 : Parameter error - * OTHERS(>=0): The number of data that pushed to the TX FIFO + * @return + * - (-1) Parameter error + * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); @@ -478,57 +501,62 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); * @brief Send data to the UART port from a given buffer and length, * This function will not return until all the data and the break signal have been sent out. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param char* src : data buffer address - * @param size_t size : data length to send - * @param int brk_len : break signal length (unit: one bit's time@current_baudrate) + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param src data buffer address + * @param size data length to send + * @param brk_len break signal length (unit: one bit's time@current_baudrate) * - * @return -1 : Parameter error - * OTHERS(>=0): The number of data that pushed to the TX FIFO + * @return + * - (-1) Parameter error + * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** * @brief UART read one char * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param TickType_t ticks_to_wait : Timeout, count in RTOS ticks + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param ticks_to_wait Timeout, count in RTOS ticks * - * @return -1 : Error - * Others : return a char data from UART. + * @return + * - (-1) Error + * - Others return a char data from UART. */ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief UART read bytes from UART buffer * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uint8_t* buf : pointer to the buffer. - * @param uint32_t length : data length - * @param TickType_t ticks_to_wait: Timeout, count in RTOS ticks + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param buf pointer to the buffer. + * @param length data length + * @param ticks_to_wait: Timeout, count in RTOS ticks * - * @return -1 : Error - * Others : return a char data from uart fifo. + * @return + * - (-1) Error + * - Others return a char data from uart fifo. */ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait); /** * @brief UART ring buffer flush * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error */ esp_err_t uart_flush(uart_port_t uart_num); /** * @brief Set the serial output port for ets_printf function, not effective for ESP_LOGX macro. * - * @param uart_port_t uart_no : UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @return ESP_OK : Success - * ESP_FAIL: Parameter error, or UART driver not installed. + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error, or UART driver not installed. */ esp_err_t uart_set_print_port(uart_port_t uart_no); @@ -536,141 +564,155 @@ esp_err_t uart_set_print_port(uart_port_t uart_no); * @brief Get the current serial port for ets_printf function * * - * @return current print port: 0: UART0; - * 1: UART1; - * 2: UART2; + * @return current print port(0: UART0; 1: UART1; 2: UART2) */ -int uart_get_print_port(); +int uart_get_print_port(void); /***************************EXAMPLE********************************** * * * ----------------EXAMPLE OF UART SETTING --------------------- - * //1. Setup UART - * #include "freertos/queue.h" - * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h - * //a. Set UART parameter - * int uart_num = 0; //uart port number + * @code{c} + * //1. Setup UART + * #include "freertos/queue.h" + * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h + * //a. Set UART parameter + * int uart_num = 0; //uart port number + * uart_config_t uart_config = { + * .baud_rate = UART_BITRATE_115200, //baudrate + * .data_bits = UART_DATA_8_BITS, //data bit mode + * .parity = UART_PARITY_DISABLE, //parity mode + * .stop_bits = UART_STOP_BITS_1, //stop bit mode + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts) + * .rx_flow_ctrl_thresh = 120, //flow control threshold + * }; + * uart_param_config(uart_num, &uart_config); + * //b1. Setup UART driver(with UART queue) + * QueueHandle_t uart_queue; + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example + * //b2. Setup UART driver(without UART queue) + * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example + *@endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //2. Set UART pin + * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //3. Read data from UART. + * uint8_t data[128]; + * int length = 0; + * length = uart_read_bytes(uart_num, data, sizeof(data), 100); + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //4. Write data to UART. + * char* test_str = "This is a test string.\n" + * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //5. Write data to UART, end with a break signal. + * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); + * @endcode + *-----------------------------------------------------------------------------* + * @code{c} + * //6. an example of echo test with hardware flow control on UART1 + * void uart_loop_back_test() + * { + * int uart_num = 1; * uart_config_t uart_config = { - * .baud_rate = UART_BITRATE_115200, //baudrate - * .data_bits = UART_DATA_8_BITS, //data bit mode - * .parity = UART_PARITY_DISABLE, //parity mode - * .stop_bits = UART_STOP_BITS_1, //stop bit mode - * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts) - * .rx_flow_ctrl_thresh = 120, //flow control threshold + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + * .rx_flow_ctrl_thresh = 122, * }; - * uart_param_config(uart_num, &uart_config); - * //b1. Setup UART driver(with UART queue) - * QueueHandle_t uart_queue; - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example - * //b2. Setup UART driver(without UART queue) - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example - * - *-----------------------------------------------------------------------------* - * //2. Set UART pin - * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. - * - *-----------------------------------------------------------------------------* - * //3. Read data from UART. - * uint8_t data[128]; - * int length = 0; - * length = uart_read_bytes(uart_num, data, sizeof(data), 100); - * - *-----------------------------------------------------------------------------* - * //4. Write data to UART. - * char* test_str = "This is a test string.\n" - * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); - * - *-----------------------------------------------------------------------------* - * //5. Write data to UART, end with a break signal. - * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); - * - *-----------------------------------------------------------------------------* - * - * //6. an example of echo test with hardware flow control on UART1 - * void uart_loop_back_test() - * { - * int uart_num = 1; - * uart_config_t uart_config = { - * .baud_rate = 115200, - * .data_bits = UART_DATA_8_BITS, - * .parity = UART_PARITY_DISABLE, - * .stop_bits = UART_STOP_BITS_1, - * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, - * .rx_flow_ctrl_thresh = 122, - * }; - * uart_param_config(uart_num, &uart_config); //Config UART1 parameters - * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) - * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level - * uart_driver_install(uart_num, 1024 * 2, 10, 17, NULL); //Install UART driver( We don't need an event queue here) - * uint8_t data[1000]; - * while(1) { - * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART - * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART - * } + * uart_param_config(uart_num, &uart_config); //Config UART1 parameters + * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) + * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level + * //Install UART driver( We don't need an event queue here) + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF); + * uint8_t data[1000]; + * while(1) { + * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART + * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART * } - * + * } + * @endcode *-----------------------------------------------------------------------------* - * //7. An example of using UART event queue on UART0. - * - * #include "freertos/queue.h" - * QueueHandle_t uart0_queue; //A queue to handle UART event. - * void uart_task(void *pvParameters) - * { - * int uart_num = (int)pvParameters; - * uart_event_t event; - * uint8_t dtmp[1000]; - * for(;;) { - * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { //Waiting for UART event. - * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); - * switch(event.type) { - * case UART_DATA: //Event of UART receving data - * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); - * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); - * ESP_LOGI(UART_TAG, "uart read: %d\n", len); - * break; - * case UART_FIFO_OVF: //Event of HW FIFO overflow detected - * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); - * break; - * case UART_BUFFER_FULL: //Event of UART ring buffer full - * ESP_LOGI(UART_TAG, "ring buffer full\n"); - * break; - * case UART_BREAK: - * ESP_LOGI(UART_TAG, "uart rx break\n"); //Event of UART RX break detected - * break; - * case UART_PARITY_ERR: //Event of UART parity check error - * ESP_LOGI(UART_TAG, "uart parity error\n"); - * break; - * case UART_FRAME_ERR: //Event of UART frame error - * ESP_LOGI(UART_TAG, "uart frame error\n"); - * break; - * default: //Others - * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); - * break; - * } - * } - * } - * vTaskDelete(NULL); - * } - * - * void uart_queue_test() - * { - * int uart_num = 0; - * uart_config_t uart_config = { - * .baud_rate = 115200, - * .data_bits = UART_DATA_8_BITS, - * .parity = UART_PARITY_DISABLE, - * .stop_bits = UART_STOP_BITS_1, - * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - * .rx_flow_ctrl_thresh = 122, - * }; - * uart_param_config(uart_num, &uart_config); //Set UART parameters - * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) - * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level - * uart_driver_install(uart_num, 1024 * 2, 10, 17, &uart0_queue); //Install UART driver, and get the queue. - * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * @code{c} + * //7. An example of using UART event queue on UART0. + * #include "freertos/queue.h" + * //A queue to handle UART event. + * QueueHandle_t uart0_queue; + * void uart_task(void *pvParameters) + * { + * int uart_num = (int)pvParameters; + * uart_event_t event; + * uint8_t dtmp[1000]; + * for(;;) { + * //Waiting for UART event. + * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { + * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); + * switch(event.type) { + * //Event of UART receving data + * case UART_DATA: + * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); + * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); + * ESP_LOGI(UART_TAG, "uart read: %d\n", len); + * break; + * //Event of HW FIFO overflow detected + * case UART_FIFO_OVF: + * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); + * break; + * //Event of UART ring buffer full + * case UART_BUFFER_FULL: + * ESP_LOGI(UART_TAG, "ring buffer full\n"); + * break; + * //Event of UART RX break detected + * case UART_BREAK: + * ESP_LOGI(UART_TAG, "uart rx break\n"); + * break; + * //Event of UART parity check error + * case UART_PARITY_ERR: + * ESP_LOGI(UART_TAG, "uart parity error\n"); + * break; + * //Event of UART frame error + * case UART_FRAME_ERR: + * ESP_LOGI(UART_TAG, "uart frame error\n"); + * break; + * //Others + * default: + * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); + * break; + * } * } + * } + * vTaskDelete(NULL); + * } * + * void uart_queue_test() + * { + * int uart_num = 0; + * uart_config_t uart_config = { + * .baud_rate = 115200, + * .data_bits = UART_DATA_8_BITS, + * .parity = UART_PARITY_DISABLE, + * .stop_bits = UART_STOP_BITS_1, + * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + * .rx_flow_ctrl_thresh = 122, + * }; + * uart_param_config(uart_num, &uart_config); //Set UART parameters + * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) + * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level + * //Install UART driver, and get the queue. + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); + * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * } + * @endcode * ***************************END OF EXAMPLE**********************************/ diff --git a/components/driver/uart.c b/components/driver/uart.c index 29e4522d6d..eeb2c64208 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -30,13 +30,15 @@ #include "soc/uart_struct.h" const char* UART_TAG = "UART"; -#define UART_CHECK(a, str) if (!(a)) { \ +#define UART_CHECK(a, str) if (!(a)) { \ ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ return ESP_FAIL; \ } -#define DEFAULT_EMPTY_THRESH 10 -#define DEFAULT_FULL_THRESH 120 -#define DEFAULT_TOUT_THRESH 10 +#define UART_EMPTY_THRESH_DEFAULT (10) +#define UART_FULL_THRESH_DEFAULT (120) +#define UART_TOUT_THRESH_DEFAULT (10) +#define UART_TX_TASK_DEPTH_DEFAULT (256*2+64) +#define UART_TX_TASK_PRIO_DEFAULT (10) #define UART_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) #define UART_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) @@ -46,13 +48,19 @@ typedef struct { uart_port_t uart_num; SemaphoreHandle_t tx_fifo_sem; SemaphoreHandle_t tx_mutex; + SemaphoreHandle_t tx_buffer_mutex; SemaphoreHandle_t tx_done_sem; SemaphoreHandle_t tx_brk_sem; - SemaphoreHandle_t rx_sem; + SemaphoreHandle_t rx_mux; QueueHandle_t xQueueUart; int queue_size; int intr_num; - RingbufHandle_t ring_buffer; + int rx_buf_size; + ringbuf_type_t rx_buf_type; + RingbufHandle_t rx_ring_buf; + int tx_buf_size; + RingbufHandle_t tx_ring_buf; + TaskHandle_t tx_task_handle; bool buffer_full_flg; bool tx_waiting; int cur_remain; @@ -66,20 +74,6 @@ static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; static uart_dev_t* UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; -//Fill UART tx_fifo and return a number, -//This function by itself is not thread-safe, always call from within a muxed section. -static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) -{ - uint8_t i = 0; - uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; - uint8_t tx_remain_fifo_cnt = (UART_FIFO_LEN - tx_fifo_cnt); - uint8_t copy_cnt = (len >= tx_remain_fifo_cnt ? tx_remain_fifo_cnt : len); - for(i = 0; i < copy_cnt; i++) { - WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), buffer[i]); - } - return copy_cnt; -} - esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); @@ -315,11 +309,11 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* //only one GPIO pad can connect with input signal esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { -// UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); -// UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); -// UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); -// UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); -// UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); + UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); + UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); + UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { @@ -443,7 +437,6 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_obj_t *p_uart = (uart_obj_t*) param; uint8_t uart_num = p_uart->uart_num; uart_dev_t* uart_reg = UART[uart_num]; - uint8_t buf_idx = 0; uint32_t uart_intr_status = UART[uart_num]->int_st.val; static int rx_fifo_len = 0; @@ -478,7 +471,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_DATA; uart_event.data.size = rx_fifo_len; - if(pdFALSE == xRingbufferSendFromISR(p_uart->ring_buffer, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { + if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.rxfifo_full = 0; uart_reg->int_ena.rxfifo_tout = 0; @@ -544,108 +537,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } /**************************************************************/ -esp_err_t uart_driver_install(uart_port_t uart_num, int buffer_size, int queue_size, int uart_intr_num, void* uart_queue) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - if(p_uart_obj[uart_num] == NULL) { - ESP_INTR_DISABLE(uart_intr_num); - p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); - if(p_uart_obj[uart_num] == NULL) { - ESP_LOGE(UART_TAG, "UART driver malloc error\n"); - return ESP_FAIL; - } - p_uart_obj[uart_num]->uart_num = uart_num; - p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); - p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); - p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); - - p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->rx_sem = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->intr_num = uart_intr_num; - p_uart_obj[uart_num]->queue_size = queue_size; - - if(uart_queue) { - p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); - *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; - ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); - } else { - p_uart_obj[uart_num]->xQueueUart = NULL; - } - p_uart_obj[uart_num]->buffer_full_flg = false; - p_uart_obj[uart_num]->tx_waiting = false; - p_uart_obj[uart_num]->rd_ptr = NULL; - p_uart_obj[uart_num]->cur_remain = 0; - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->ring_buffer = xRingbufferCreate(buffer_size, 0); - } else { - ESP_LOGE(UART_TAG, "UART driver already installed\n"); - return ESP_FAIL; - } - uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); - uart_intr_config_t uart_intr = { - .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M - | UART_RXFIFO_TOUT_INT_ENA_M - | UART_FRM_ERR_INT_ENA_M - | UART_RXFIFO_OVF_INT_ENA_M - | UART_BRK_DET_INT_ENA_M, - .rxfifo_full_thresh = DEFAULT_FULL_THRESH, - .rx_timeout_thresh = DEFAULT_TOUT_THRESH, - .txfifo_empty_intr_thresh = DEFAULT_EMPTY_THRESH - }; - uart_intr_config(uart_num, &uart_intr); - ESP_INTR_ENABLE(uart_intr_num); - return ESP_OK; -} - -//Make sure no other tasks are still using UART before you call this function -esp_err_t uart_driver_delete(uart_port_t uart_num) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - if(p_uart_obj[uart_num] == NULL) { - ESP_LOGI(UART_TAG, "ALREADY NULL\n"); - return ESP_OK; - } - ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); - uart_disable_rx_intr(uart_num); - uart_disable_tx_intr(uart_num); - uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); - - if(p_uart_obj[uart_num]->tx_fifo_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); - p_uart_obj[uart_num]->tx_fifo_sem = NULL; - } - if(p_uart_obj[uart_num]->tx_done_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_done_sem); - p_uart_obj[uart_num]->tx_done_sem = NULL; - } - if(p_uart_obj[uart_num]->tx_brk_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); - p_uart_obj[uart_num]->tx_brk_sem = NULL; - } - if(p_uart_obj[uart_num]->tx_mutex) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); - p_uart_obj[uart_num]->tx_mutex = NULL; - } - if(p_uart_obj[uart_num]->rx_sem) { - vSemaphoreDelete(p_uart_obj[uart_num]->rx_sem); - p_uart_obj[uart_num]->rx_sem = NULL; - } - if(p_uart_obj[uart_num]->xQueueUart) { - vQueueDelete(p_uart_obj[uart_num]->xQueueUart); - p_uart_obj[uart_num]->xQueueUart = NULL; - } - if(p_uart_obj[uart_num]->ring_buffer) { - vRingbufferDelete(p_uart_obj[uart_num]->ring_buffer); - p_uart_obj[uart_num]->ring_buffer = NULL; - } - free(p_uart_obj[uart_num]); - p_uart_obj[uart_num] = NULL; - return ESP_OK; -} - -esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait) +esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); @@ -657,17 +549,9 @@ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait return ESP_ERR_TIMEOUT; } ticks_to_wait = ticks_end - xTaskGetTickCount(); - //take 1st tx_done_sem - res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); - if(res == pdFALSE) { - ESP_LOGE(UART_TAG, "take uart done sem error, should not get here.\n"); - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); - return ESP_ERR_TIMEOUT; - } + xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, 0); ticks_to_wait = ticks_end - xTaskGetTickCount(); if(UART[uart_num]->status.txfifo_cnt == 0) { - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); return ESP_OK; } @@ -676,11 +560,9 @@ esp_err_t uart_wait_tx_fifo_empty(uart_port_t uart_num, TickType_t ticks_to_wait res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); if(res == pdFALSE) { uart_disable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); return ESP_ERR_TIMEOUT; } - xSemaphoreGive(p_uart_obj[uart_num]->tx_done_sem); xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); return ESP_OK; } @@ -696,6 +578,20 @@ static esp_err_t uart_set_break(uart_port_t uart_num, int break_num) return ESP_OK; } +//Fill UART tx_fifo and return a number, +//This function by itself is not thread-safe, always call from within a muxed section. +static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) +{ + uint8_t i = 0; + uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; + uint8_t tx_remain_fifo_cnt = (UART_FIFO_LEN - tx_fifo_cnt); + uint8_t copy_cnt = (len >= tx_remain_fifo_cnt ? tx_remain_fifo_cnt : len); + for(i = 0; i < copy_cnt; i++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), buffer[i]); + } + return copy_cnt; +} + int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); @@ -727,7 +623,7 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool size_t sent = uart_fill_fifo(uart_num, (char*) src, size); if(sent < size) { p_uart_obj[uart_num]->tx_waiting = true; - uart_enable_tx_intr(uart_num, 1, DEFAULT_EMPTY_THRESH); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); } size -= sent; src += sent; @@ -742,12 +638,55 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool return original_size; } +static void uart_tx_task(void* arg) +{ + uart_obj_t* p_uart = (uart_obj_t*) arg; + size_t size; + uart_event_t evt; + for(;;) { + char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); + if(data == NULL) { + continue; + } + memcpy(&evt, data, sizeof(evt)); + if(evt.type == UART_DATA) { + uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); + } else if(evt.type == UART_DATA_BREAK) { + uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); + } + vRingbufferReturnItem(p_uart->tx_ring_buf, data); + } + vTaskDelete(NULL); +} + int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error"); UART_CHECK(src, "buffer null"); - return uart_tx_all(uart_num, src, size, 0, 0); + if(p_uart_obj[uart_num]->tx_buf_size > 0) { + if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { + uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); + if(evt == NULL) { + ESP_LOGE(UART_TAG, "UART EVT MALLOC ERROR\n"); + return -1; + } + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + evt->type = UART_DATA; + evt->data.size = size; + memcpy(evt->data.data, src, size); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); + free(evt); + evt = NULL; + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + return size; + } else { + ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[0], SEND DIRECTLY\n"); + return uart_tx_all(uart_num, src, size, 0, 0); + } + } else { + return uart_tx_all(uart_num, src, size, 0, 0); + } } int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) @@ -757,7 +696,29 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s UART_CHECK((size > 0), "uart size error"); UART_CHECK((src), "uart data null"); UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); - return uart_tx_all(uart_num, src, size, 1, brk_len); + if(p_uart_obj[uart_num]->tx_buf_size > 0) { + if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { + uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); + if(evt == NULL) { + return -1; + } + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + evt->type = UART_DATA_BREAK; + evt->data.size = size; + evt->data.brk_len = brk_len; + memcpy(evt->data.data, src, size); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); + free(evt); + evt = NULL; + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + return size; + } else { + ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[1], SEND DIRECTLY\n"); + return uart_tx_all(uart_num, src, size, 1, brk_len); + } + } else { + return uart_tx_all(uart_num, src, size, 1, brk_len); + } } int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) @@ -768,18 +729,18 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) size_t size; int val; portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; - if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { return -1; } if(p_uart_obj[uart_num]->cur_remain == 0) { ticks_to_wait = ticks_end - xTaskGetTickCount(); - data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { p_uart_obj[uart_num]->head_ptr = data; p_uart_obj[uart_num]->rd_ptr = data; p_uart_obj[uart_num]->cur_remain = size; } else { - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return -1; } } @@ -787,18 +748,18 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) p_uart_obj[uart_num]->rd_ptr++; p_uart_obj[uart_num]->cur_remain--; if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); p_uart_obj[uart_num]->head_ptr = NULL; p_uart_obj[uart_num]->rd_ptr = NULL; if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); if(res == pdTRUE) { p_uart_obj[uart_num]->buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); } } } - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return val; } @@ -807,23 +768,22 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((buf), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - uint8_t* data = NULL; size_t size; size_t copy_len = 0; int len_tmp; - if(xSemaphoreTake(p_uart_obj[uart_num]->rx_sem,(portTickType)ticks_to_wait) != pdTRUE) { + if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { return -1; } while(length) { if(p_uart_obj[uart_num]->cur_remain == 0) { - data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->ring_buffer, &size, (portTickType) ticks_to_wait); + data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { p_uart_obj[uart_num]->head_ptr = data; p_uart_obj[uart_num]->rd_ptr = data; p_uart_obj[uart_num]->cur_remain = size; } else { - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } } @@ -838,11 +798,11 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp copy_len += len_tmp; length -= len_tmp; if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->head_ptr); + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); p_uart_obj[uart_num]->head_ptr = NULL; p_uart_obj[uart_num]->rd_ptr = NULL; if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->ring_buffer, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); if(res == pdTRUE) { p_uart_obj[uart_num]->buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); @@ -850,7 +810,7 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp } } } - xSemaphoreGive(p_uart_obj[uart_num]->rx_sem); + xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } @@ -858,30 +818,38 @@ esp_err_t uart_flush(uart_port_t uart_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - uart_obj_t* p_uart = p_uart_obj[uart_num]; uint8_t* data; size_t size; //rx sem protect the ring buffer read related functions - xSemaphoreTake(p_uart->rx_sem, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); while(true) { if(p_uart->head_ptr) { - vRingbufferReturnItem(p_uart->ring_buffer, p_uart->head_ptr); + vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->head_ptr); p_uart->rd_ptr = NULL; p_uart->cur_remain = 0; p_uart->head_ptr = NULL; } - data = (uint8_t*) xRingbufferReceive(p_uart->ring_buffer, &size, (portTickType) 0); + data = (uint8_t*) xRingbufferReceive(p_uart->rx_ring_buf, &size, (portTickType) 0); if(data == NULL) { break; } - vRingbufferReturnItem(p_uart->ring_buffer, data); + vRingbufferReturnItem(p_uart->rx_ring_buf, data); } p_uart->rd_ptr = NULL; p_uart->cur_remain = 0; p_uart->head_ptr = NULL; - xSemaphoreGive(p_uart->rx_sem); - uart_wait_tx_fifo_empty(uart_num, portMAX_DELAY); + xSemaphoreGive(p_uart->rx_mux); + xSemaphoreTake(p_uart->tx_mutex, (portTickType)portMAX_DELAY); + do { + data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); + if(data == NULL) { + break; + } + vRingbufferReturnItem(p_uart->rx_ring_buf, data); + } while(1); + xSemaphoreGive(p_uart->tx_mutex); + uart_wait_tx_done(uart_num, portMAX_DELAY); uart_reset_fifo(uart_num); return ESP_OK; } @@ -915,7 +883,6 @@ esp_err_t uart_set_print_port(uart_port_t uart_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num]), "UART driver error"); - s_uart_print_nport = uart_num; switch(s_uart_print_nport) { case UART_NUM_0: @@ -940,3 +907,127 @@ int uart_get_print_port() return s_uart_print_nport; } +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n"); + if(p_uart_obj[uart_num] == NULL) { + ESP_INTR_DISABLE(uart_intr_num); + p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGE(UART_TAG, "UART driver malloc error\n"); + return ESP_FAIL; + } + p_uart_obj[uart_num]->uart_num = uart_num; + p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); + p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); + p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->tx_buffer_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->intr_num = uart_intr_num; + p_uart_obj[uart_num]->queue_size = queue_size; + + if(uart_queue) { + p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); + *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; + ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); + } else { + p_uart_obj[uart_num]->xQueueUart = NULL; + } + p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->tx_waiting = false; + p_uart_obj[uart_num]->rd_ptr = NULL; + p_uart_obj[uart_num]->cur_remain = 0; + p_uart_obj[uart_num]->head_ptr = NULL; + p_uart_obj[uart_num]->rx_buf_type = rx_buf_type; + p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, rx_buf_type); + if(tx_buffer_size > 0) { + p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT); + p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; + xTaskCreate(uart_tx_task, "uart_tx_task", UART_TX_TASK_DEPTH_DEFAULT, (void*)p_uart_obj[uart_num], UART_TX_TASK_PRIO_DEFAULT, &p_uart_obj[uart_num]->tx_task_handle); + + } else { + p_uart_obj[uart_num]->tx_ring_buf = NULL; + p_uart_obj[uart_num]->tx_buf_size = 0; + p_uart_obj[uart_num]->tx_task_handle = NULL; + } + } else { + ESP_LOGE(UART_TAG, "UART driver already installed\n"); + return ESP_FAIL; + } + uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + uart_intr_config_t uart_intr = { + .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M + | UART_RXFIFO_TOUT_INT_ENA_M + | UART_FRM_ERR_INT_ENA_M + | UART_RXFIFO_OVF_INT_ENA_M + | UART_BRK_DET_INT_ENA_M, + .rxfifo_full_thresh = UART_FULL_THRESH_DEFAULT, + .rx_timeout_thresh = UART_TOUT_THRESH_DEFAULT, + .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT + }; + uart_intr_config(uart_num, &uart_intr); + ESP_INTR_ENABLE(uart_intr_num); + return ESP_OK; +} + +//Make sure no other tasks are still using UART before you call this function +esp_err_t uart_driver_delete(uart_port_t uart_num) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + if(p_uart_obj[uart_num] == NULL) { + ESP_LOGI(UART_TAG, "ALREADY NULL\n"); + return ESP_OK; + } + ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); + uart_disable_rx_intr(uart_num); + uart_disable_tx_intr(uart_num); + uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); + + if(p_uart_obj[uart_num]->tx_task_handle) { + vTaskDelete(p_uart_obj[uart_num]->tx_task_handle); + p_uart_obj[uart_num]->tx_task_handle = NULL; + } + if(p_uart_obj[uart_num]->tx_fifo_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); + p_uart_obj[uart_num]->tx_fifo_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_done_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_done_sem); + p_uart_obj[uart_num]->tx_done_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_brk_sem) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); + p_uart_obj[uart_num]->tx_brk_sem = NULL; + } + if(p_uart_obj[uart_num]->tx_mutex) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); + p_uart_obj[uart_num]->tx_mutex = NULL; + } + if(p_uart_obj[uart_num]->tx_buffer_mutex) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mutex); + p_uart_obj[uart_num]->tx_buffer_mutex = NULL; + } + if(p_uart_obj[uart_num]->rx_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->rx_mux); + p_uart_obj[uart_num]->rx_mux = NULL; + } + if(p_uart_obj[uart_num]->xQueueUart) { + vQueueDelete(p_uart_obj[uart_num]->xQueueUart); + p_uart_obj[uart_num]->xQueueUart = NULL; + } + if(p_uart_obj[uart_num]->rx_ring_buf) { + vRingbufferDelete(p_uart_obj[uart_num]->rx_ring_buf); + p_uart_obj[uart_num]->rx_ring_buf = NULL; + } + if(p_uart_obj[uart_num]->tx_ring_buf) { + vRingbufferDelete(p_uart_obj[uart_num]->tx_ring_buf); + p_uart_obj[uart_num]->tx_ring_buf = NULL; + } + + free(p_uart_obj[uart_num]); + p_uart_obj[uart_num] = NULL; + return ESP_OK; +} From fc6b52574a337fd4f4b7a13155eea488582e1b0d Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 1 Nov 2016 13:07:10 +0800 Subject: [PATCH 090/285] components/openssl: refactor the SSL port function and debug function --- components/openssl/include/internal/ssl_dbg.h | 28 ++++++--- components/openssl/include/platform/ssl_opt.h | 48 ++++++++++++++++ .../openssl/include/platform/ssl_port.h | 16 +++++- components/openssl/library/ssl_cert.c | 8 +-- components/openssl/library/ssl_lib.c | 24 ++++---- components/openssl/library/ssl_pkey.c | 8 +-- components/openssl/library/ssl_stack.c | 14 ++--- components/openssl/library/ssl_x509.c | 8 +-- components/openssl/platform/ssl_pm.c | 57 +++++++++++-------- components/openssl/platform/ssl_port.c | 6 +- 10 files changed, 148 insertions(+), 69 deletions(-) create mode 100644 components/openssl/include/platform/ssl_opt.h diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index d6ae47499e..1b0a73f167 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -15,21 +15,33 @@ #ifndef _SSL_DEBUG_H_ #define _SSL_DEBUG_H_ +#include "platform/ssl_opt.h" +#include "platform/ssl_port.h" + #ifdef __cplusplus extern "C" { #endif -#define SSL_DEBUG_ENBALE 1 +#ifndef SSL_DEBUG_ENBALE +#define SSL_DEBUG_ENBALE 0 +#endif + +#ifndef SSL_DEBUG_LEVEL #define SSL_DEBUG_LEVEL 0 -#define SSL_ASSERT_ENABLE 1 -#define SSL_DEBUG_LOCATION_ENABLE 1 +#endif -#if SSL_DEBUG_ENBALE - extern int ets_printf(const char *fmt, ...); +#ifndef SSL_ASSERT_ENABLE +#define SSL_ASSERT_ENABLE 0 +#endif - #define SSL_PRINT ets_printf -#else - #define SSL_PRINT(...) +#ifndef SSL_DEBUG_LOCATION_ENABLE +#define SSL_DEBUG_LOCATION_ENABLE 0 +#endif + +#ifndef SSL_PRINT + #include "stdio.h" + extern int printf(const char *fmt, ...); + #define SSL_PRINT printf #endif #if SSL_DEBUG_LOCATION_ENABLE diff --git a/components/openssl/include/platform/ssl_opt.h b/components/openssl/include/platform/ssl_opt.h new file mode 100644 index 0000000000..01d438eb8a --- /dev/null +++ b/components/openssl/include/platform/ssl_opt.h @@ -0,0 +1,48 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSL_OPT_H_ +#define _SSL_OPT_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * if not define "ESP32_IDF_PLATFORM", system will use esp8266 platform interface + */ +#define ESP32_IDF_PLATFORM + +/** + * openssl debug print function enable + */ +#define SSL_DEBUG_ENBALE 0 + +/** + * openssl debug print function level. function whose level is lower that "SSL_DEBUG_LEVEL" + * will not print message + */ +#define SSL_DEBUG_LEVEL 0 + +/** + * openssl assert function enable, it will check the input paramter and print the message + */ +#define SSL_ASSERT_ENABLE 0 + +/** + * openssl location function enable, it will print location of the positioning error + */ +#define SSL_DEBUG_LOCATION_ENABLE 0 + +#endif diff --git a/components/openssl/include/platform/ssl_port.h b/components/openssl/include/platform/ssl_port.h index 995d33e0e5..4a319c91ae 100644 --- a/components/openssl/include/platform/ssl_port.h +++ b/components/openssl/include/platform/ssl_port.h @@ -19,11 +19,15 @@ extern "C" { #endif +#include "platform/ssl_opt.h" + +#ifdef ESP32_IDF_PLATFORM + #include "esp_types.h" -void* ssl_zalloc(size_t size); -void *ssl_malloc(size_t size); -void ssl_free(void *p); +void *ssl_mem_zalloc(size_t size); +void *ssl_mem_malloc(size_t size); +void ssl_mem_free(void *p); void* ssl_memcpy(void *to, const void *from, size_t size); size_t ssl_strlen(const char *src); @@ -31,4 +35,10 @@ size_t ssl_strlen(const char *src); void ssl_speed_up_enter(void); void ssl_speed_up_exit(void); +#elif defined(SSL_PLATFORM_USER_INCLUDE) + +SSL_PLATFORM_USER_INCLUDE + +#endif + #endif diff --git a/components/openssl/library/ssl_cert.c b/components/openssl/library/ssl_cert.c index e4fd4d7785..0193a441e0 100644 --- a/components/openssl/library/ssl_cert.c +++ b/components/openssl/library/ssl_cert.c @@ -28,9 +28,9 @@ CERT *__ssl_cert_new(CERT *ic) X509 *ix; EVP_PKEY *ipk; - cert = ssl_zalloc(sizeof(CERT)); + cert = ssl_mem_zalloc(sizeof(CERT)); if (!cert) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_mem_zalloc\n"); if (ic) { ipk = ic->pkey; @@ -53,7 +53,7 @@ CERT *__ssl_cert_new(CERT *ic) failed3: EVP_PKEY_free(cert->pkey); failed2: - ssl_free(cert); + ssl_mem_free(cert); failed1: return NULL; } @@ -75,5 +75,5 @@ void ssl_cert_free(CERT *cert) EVP_PKEY_free(cert->pkey); - ssl_free(cert); + ssl_mem_free(cert); } diff --git a/components/openssl/library/ssl_lib.c b/components/openssl/library/ssl_lib.c index 9740282d13..23b8bf4cea 100644 --- a/components/openssl/library/ssl_lib.c +++ b/components/openssl/library/ssl_lib.c @@ -124,9 +124,9 @@ SSL_SESSION* SSL_SESSION_new(void) { SSL_SESSION *session; - session = ssl_zalloc(sizeof(SSL_SESSION)); + session = ssl_mem_zalloc(sizeof(SSL_SESSION)); if (!session) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_mem_zalloc\n"); session->peer = X509_new(); if (!session->peer) @@ -135,7 +135,7 @@ SSL_SESSION* SSL_SESSION_new(void) return session; failed2: - ssl_free(session); + ssl_mem_free(session); failed1: return NULL; } @@ -146,7 +146,7 @@ failed1: void SSL_SESSION_free(SSL_SESSION *session) { X509_free(session->peer); - ssl_free(session); + ssl_mem_free(session); } /** @@ -168,9 +168,9 @@ SSL_CTX* SSL_CTX_new(const SSL_METHOD *method) if (!cert) SSL_RET(go_failed2, "ssl_cert_new\n"); - ctx = (SSL_CTX *)ssl_zalloc(sizeof(SSL_CTX)); + ctx = (SSL_CTX *)ssl_mem_zalloc(sizeof(SSL_CTX)); if (!ctx) - SSL_RET(go_failed3, "ssl_zalloc:ctx\n"); + SSL_RET(go_failed3, "ssl_mem_zalloc:ctx\n"); ctx->method = method; ctx->client_CA = client_ca; @@ -199,7 +199,7 @@ void SSL_CTX_free(SSL_CTX* ctx) X509_free(ctx->client_CA); - ssl_free(ctx); + ssl_mem_free(ctx); } /** @@ -238,9 +238,9 @@ SSL *SSL_new(SSL_CTX *ctx) if (!ctx) SSL_RET(failed1, "ctx:NULL\n"); - ssl = (SSL *)ssl_zalloc(sizeof(SSL)); + ssl = (SSL *)ssl_mem_zalloc(sizeof(SSL)); if (!ssl) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_mem_zalloc\n"); ssl->session = SSL_SESSION_new(); if (!ssl->session) @@ -277,7 +277,7 @@ failed4: failed3: SSL_SESSION_free(ssl->session); failed2: - ssl_free(ssl); + ssl_mem_free(ssl); failed1: return NULL; } @@ -297,7 +297,7 @@ void SSL_free(SSL *ssl) SSL_SESSION_free(ssl->session); - ssl_free(ssl); + ssl_mem_free(ssl); } /** @@ -343,7 +343,7 @@ int SSL_shutdown(SSL *ssl) SSL_ASSERT(ssl); - if (SSL_get_state(ssl) != TLS_ST_OK) return 0; + if (SSL_get_state(ssl) != TLS_ST_OK) return 1; ret = SSL_METHOD_CALL(shutdown, ssl); diff --git a/components/openssl/library/ssl_pkey.c b/components/openssl/library/ssl_pkey.c index 20debfbcfc..dbd82dc9c2 100644 --- a/components/openssl/library/ssl_pkey.c +++ b/components/openssl/library/ssl_pkey.c @@ -25,9 +25,9 @@ EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk) int ret; EVP_PKEY *pkey; - pkey = ssl_zalloc(sizeof(EVP_PKEY)); + pkey = ssl_mem_zalloc(sizeof(EVP_PKEY)); if (!pkey) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_mem_zalloc\n"); if (ipk) { pkey->method = ipk->method; @@ -42,7 +42,7 @@ EVP_PKEY* __EVP_PKEY_new(EVP_PKEY *ipk) return pkey; failed2: - ssl_free(pkey); + ssl_mem_free(pkey); failed1: return NULL; } @@ -62,7 +62,7 @@ void EVP_PKEY_free(EVP_PKEY *pkey) { EVP_PKEY_METHOD_CALL(free, pkey); - ssl_free(pkey); + ssl_mem_free(pkey); } /** diff --git a/components/openssl/library/ssl_stack.c b/components/openssl/library/ssl_stack.c index 4ea40e7259..5dbb69af9d 100644 --- a/components/openssl/library/ssl_stack.c +++ b/components/openssl/library/ssl_stack.c @@ -30,13 +30,13 @@ OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c) OPENSSL_STACK *stack; char **data; - stack = ssl_zalloc(sizeof(OPENSSL_STACK)); + stack = ssl_mem_zalloc(sizeof(OPENSSL_STACK)); if (!stack) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_mem_zalloc\n"); - data = ssl_zalloc(sizeof(*data) * MIN_NODES); + data = ssl_mem_zalloc(sizeof(*data) * MIN_NODES); if (!data) - SSL_RET(failed2, "ssl_zalloc\n"); + SSL_RET(failed2, "ssl_mem_zalloc\n"); stack->data = data; stack->num_alloc = MIN_NODES; @@ -45,7 +45,7 @@ OPENSSL_STACK* OPENSSL_sk_new(OPENSSL_sk_compfunc c) return stack; failed2: - ssl_free(stack); + ssl_mem_free(stack); failed1: return NULL; } @@ -65,6 +65,6 @@ void OPENSSL_sk_free(OPENSSL_STACK *stack) { SSL_ASSERT(stack); - ssl_free(stack->data); - ssl_free(stack); + ssl_mem_free(stack->data); + ssl_mem_free(stack); } diff --git a/components/openssl/library/ssl_x509.c b/components/openssl/library/ssl_x509.c index 06e6e7b544..d0426db18c 100644 --- a/components/openssl/library/ssl_x509.c +++ b/components/openssl/library/ssl_x509.c @@ -33,9 +33,9 @@ X509* __X509_new(X509 *ix) int ret; X509 *x; - x = ssl_zalloc(sizeof(X509)); + x = ssl_mem_zalloc(sizeof(X509)); if (!x) - SSL_RET(failed1, "ssl_malloc\n"); + SSL_RET(failed1, "ssl_mem_zalloc\n"); if (ix) x->method = ix->method; @@ -49,7 +49,7 @@ X509* __X509_new(X509 *ix) return x; failed2: - ssl_free(x); + ssl_mem_free(x); failed1: return NULL; } @@ -69,7 +69,7 @@ void X509_free(X509 *x) { X509_METHOD_CALL(free, x); - ssl_free(x); + ssl_mem_free(x); }; /** diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index eadd323e70..21c0ac58c9 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -86,10 +86,16 @@ int ssl_pm_new(SSL *ssl) const SSL_METHOD *method = ssl->method; - ssl_pm = ssl_zalloc(sizeof(struct ssl_pm)); + ssl_pm = ssl_mem_zalloc(sizeof(struct ssl_pm)); if (!ssl_pm) - SSL_ERR(ret, failed1, "ssl_zalloc\n"); + SSL_ERR(ret, failed1, "ssl_mem_zalloc\n"); + if (ssl->ctx->read_buffer_len < 2048 || + ssl->ctx->read_buffer_len > 8192) + return -1; + + max_content_len = ssl->ctx->read_buffer_len; + mbedtls_net_init(&ssl_pm->fd); mbedtls_net_init(&ssl_pm->cl_fd); @@ -144,6 +150,7 @@ failed3: mbedtls_ctr_drbg_free(&ssl_pm->ctr_drbg); failed2: mbedtls_entropy_free(&ssl_pm->entropy); + ssl_mem_free(ssl_pm); failed1: return -1; } @@ -160,7 +167,7 @@ void ssl_pm_free(SSL *ssl) mbedtls_ssl_config_free(&ssl_pm->conf); mbedtls_ssl_free(&ssl_pm->ssl); - ssl_free(ssl_pm); + ssl_mem_free(ssl_pm); ssl->ssl_pm = NULL; } @@ -392,7 +399,7 @@ int x509_pm_show_info(X509 *x) if (!x509_crt) return -1; - buf = ssl_malloc(X509_INFO_STRING_LENGTH); + buf = ssl_mem_malloc(X509_INFO_STRING_LENGTH); if (!buf) SSL_RET(failed1, ""); @@ -401,14 +408,14 @@ int x509_pm_show_info(X509 *x) SSL_RET(failed2, ""); buf[ret] = 0; - ssl_free(buf); + ssl_mem_free(buf); SSL_PRINT("%s", buf); return 0; failed2: - ssl_free(buf); + ssl_mem_free(buf); failed1: return -1; } @@ -417,9 +424,9 @@ int x509_pm_new(X509 *x, X509 *m_x) { struct x509_pm *x509_pm; - x509_pm = ssl_zalloc(sizeof(struct x509_pm)); + x509_pm = ssl_mem_zalloc(sizeof(struct x509_pm)); if (!x509_pm) - SSL_RET(failed1, "ssl_zalloc\n"); + SSL_RET(failed1, "ssl_mem_zalloc\n"); x->x509_pm = x509_pm; @@ -442,11 +449,11 @@ void x509_pm_free(X509 *x) if (x509_pm->x509_crt) { mbedtls_x509_crt_free(x509_pm->x509_crt); - ssl_free(x509_pm->x509_crt); + ssl_mem_free(x509_pm->x509_crt); x509_pm->x509_crt = NULL; } - ssl_free(x->x509_pm); + ssl_mem_free(x->x509_pm); x->x509_pm = NULL; } @@ -460,14 +467,14 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) mbedtls_x509_crt_free(x509_pm->x509_crt); if (!x509_pm->x509_crt) { - x509_pm->x509_crt = ssl_malloc(sizeof(mbedtls_x509_crt)); + x509_pm->x509_crt = ssl_mem_malloc(sizeof(mbedtls_x509_crt)); if (!x509_pm->x509_crt) - SSL_RET(failed1, "ssl_malloc\n"); + SSL_RET(failed1, "ssl_mem_malloc\n"); } - load_buf = ssl_malloc(len + 1); + load_buf = ssl_mem_malloc(len + 1); if (!load_buf) - SSL_RET(failed2, "ssl_malloc\n"); + SSL_RET(failed2, "ssl_mem_malloc\n"); ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; @@ -477,7 +484,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) mbedtls_x509_crt_init(x509_pm->x509_crt); ret = mbedtls_x509_crt_parse(x509_pm->x509_crt, load_buf, len + 1); - ssl_free(load_buf); + ssl_mem_free(load_buf); if (ret) SSL_RET(failed2, "mbedtls_x509_crt_parse, return [-0x%x]\n", -ret); @@ -485,7 +492,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) return 0; failed2: - ssl_free(x509_pm->x509_crt); + ssl_mem_free(x509_pm->x509_crt); x509_pm->x509_crt = NULL; failed1: return -1; @@ -495,7 +502,7 @@ int pkey_pm_new(EVP_PKEY *pk, EVP_PKEY *m_pkey) { struct pkey_pm *pkey_pm; - pkey_pm = ssl_zalloc(sizeof(struct pkey_pm)); + pkey_pm = ssl_mem_zalloc(sizeof(struct pkey_pm)); if (!pkey_pm) return -1; @@ -517,11 +524,11 @@ void pkey_pm_free(EVP_PKEY *pk) if (pkey_pm->pkey) { mbedtls_pk_free(pkey_pm->pkey); - ssl_free(pkey_pm->pkey); + ssl_mem_free(pkey_pm->pkey); pkey_pm->pkey = NULL; } - ssl_free(pk->pkey_pm); + ssl_mem_free(pk->pkey_pm); pk->pkey_pm = NULL; } @@ -535,14 +542,14 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) mbedtls_pk_free(pkey_pm->pkey); if (!pkey_pm->pkey) { - pkey_pm->pkey = ssl_malloc(sizeof(mbedtls_pk_context)); + pkey_pm->pkey = ssl_mem_malloc(sizeof(mbedtls_pk_context)); if (!pkey_pm->pkey) - SSL_RET(failed1, "ssl_malloc\n"); + SSL_RET(failed1, "ssl_mem_malloc\n"); } - load_buf = ssl_malloc(len + 1); + load_buf = ssl_mem_malloc(len + 1); if (!load_buf) - SSL_RET(failed2, "ssl_malloc\n"); + SSL_RET(failed2, "ssl_mem_malloc\n"); ssl_memcpy(load_buf, buffer, len); load_buf[len] = '\0'; @@ -552,7 +559,7 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) mbedtls_pk_init(pkey_pm->pkey); ret = mbedtls_pk_parse_key(pkey_pm->pkey, load_buf, len + 1, NULL, 0); - ssl_free(load_buf); + ssl_mem_free(load_buf); if (ret) SSL_RET(failed2, "mbedtls_pk_parse_key, return [-0x%x]\n", -ret); @@ -560,7 +567,7 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) return 0; failed2: - ssl_free(pkey_pm->pkey); + ssl_mem_free(pkey_pm->pkey); pkey_pm->pkey = NULL; failed1: return -1; diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c index b57e703a17..9fbc44deb6 100644 --- a/components/openssl/platform/ssl_port.c +++ b/components/openssl/platform/ssl_port.c @@ -15,6 +15,7 @@ #include "ssl_port.h" #include "string.h" #include "malloc.h" +#include "esp_system.h" /*********************************************************************************************/ /********************************* SSL general interface *************************************/ @@ -51,10 +52,11 @@ size_t ssl_strlen(const char *src) void ssl_speed_up_enter(void) { - + system_update_cpu_freq(SYS_CPU_160MHZ); } void ssl_speed_up_exit(void) { - + system_update_cpu_freq(SYS_CPU_80MHZ); } + From 16a4d56fe5af03005afc9a3b57e95219918841c6 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 1 Nov 2016 13:09:54 +0800 Subject: [PATCH 091/285] components/openssl: remove some platform interface --- components/openssl/platform/ssl_port.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c index 9fbc44deb6..b333fdbfba 100644 --- a/components/openssl/platform/ssl_port.c +++ b/components/openssl/platform/ssl_port.c @@ -15,7 +15,6 @@ #include "ssl_port.h" #include "string.h" #include "malloc.h" -#include "esp_system.h" /*********************************************************************************************/ /********************************* SSL general interface *************************************/ @@ -52,11 +51,11 @@ size_t ssl_strlen(const char *src) void ssl_speed_up_enter(void) { - system_update_cpu_freq(SYS_CPU_160MHZ); + } void ssl_speed_up_exit(void) { - system_update_cpu_freq(SYS_CPU_80MHZ); + } From 8d1f360ca6835c2d13563da10bed97bb7c38cc6c Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 1 Nov 2016 13:10:56 +0800 Subject: [PATCH 092/285] components/openssl: ssl port use esp32_idf default --- components/openssl/platform/ssl_port.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c index b333fdbfba..1f19510116 100644 --- a/components/openssl/platform/ssl_port.c +++ b/components/openssl/platform/ssl_port.c @@ -13,6 +13,9 @@ // limitations under the License. #include "ssl_port.h" + +#ifdef ESP32_IDF_PLATFORM + #include "string.h" #include "malloc.h" @@ -59,3 +62,5 @@ void ssl_speed_up_exit(void) } +#endif + From bc710e5b885df673e13470de8ce99997acb7e4dd Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 1 Nov 2016 14:59:50 +0800 Subject: [PATCH 093/285] components/openssl: refacetor the SSL debug function Add the "ssl_opt.h" file to make user able t add its platform interface --- components/openssl/include/internal/ssl_dbg.h | 21 ++++++++++++------- .../openssl/include/platform/ssl_port.h | 3 +++ components/openssl/platform/ssl_port.c | 6 +++--- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 1b0a73f167..5b909955e4 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -38,10 +38,17 @@ #define SSL_DEBUG_LOCATION_ENABLE 0 #endif -#ifndef SSL_PRINT - #include "stdio.h" - extern int printf(const char *fmt, ...); - #define SSL_PRINT printf +#if SSL_DEBUG_ENBALE + #ifndef SSL_PRINT + #include "stdio.h" + extern int printf(const char *fmt, ...); + #define SSL_PRINT printf + #endif +#else + #ifdef SSL_PRINT + #undef SSL_PRINT + #define SSL_PRINT(...) + #endif #endif #if SSL_DEBUG_LOCATION_ENABLE @@ -56,11 +63,11 @@ #define SSL_ASSERT(s) #endif -#define SSL_ERR(err, go, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(__VA_ARGS__); ret = err; goto go; } +#define SSL_ERR(err, go, fmt, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(fmt, ##__VA_ARGS__); ret = err; goto go; } -#define SSL_RET(go, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(__VA_ARGS__); goto go; } +#define SSL_RET(go, fmt, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(fmt, ##__VA_ARGS__); goto go; } -#define SSL_DEBUG(level, ...) { if (level > SSL_DEBUG_LEVEL) {SSL_PRINT(__VA_ARGS__);} } +#define SSL_DEBUG(level, fmt, ...) { if (level > SSL_DEBUG_LEVEL) {SSL_PRINT(fmt, ##__VA_ARGS__);} } #ifdef __cplusplus } diff --git a/components/openssl/include/platform/ssl_port.h b/components/openssl/include/platform/ssl_port.h index 4a319c91ae..1e4c2f31f9 100644 --- a/components/openssl/include/platform/ssl_port.h +++ b/components/openssl/include/platform/ssl_port.h @@ -24,6 +24,7 @@ #ifdef ESP32_IDF_PLATFORM #include "esp_types.h" +#include "esp_log.h" void *ssl_mem_zalloc(size_t size); void *ssl_mem_malloc(size_t size); @@ -35,6 +36,8 @@ size_t ssl_strlen(const char *src); void ssl_speed_up_enter(void); void ssl_speed_up_exit(void); +#define SSL_PRINT(fmt, ...) ESP_LOGD("OpenSSL", fmt, ##__VA_ARGS__) + #elif defined(SSL_PLATFORM_USER_INCLUDE) SSL_PLATFORM_USER_INCLUDE diff --git a/components/openssl/platform/ssl_port.c b/components/openssl/platform/ssl_port.c index 1f19510116..ae3b849ca3 100644 --- a/components/openssl/platform/ssl_port.c +++ b/components/openssl/platform/ssl_port.c @@ -22,7 +22,7 @@ /*********************************************************************************************/ /********************************* SSL general interface *************************************/ -void* ssl_zalloc(size_t size) +void* ssl_mem_zalloc(size_t size) { void *p = malloc(size); @@ -32,12 +32,12 @@ void* ssl_zalloc(size_t size) return p; } -void *ssl_malloc(size_t size) +void *ssl_mem_malloc(size_t size) { return malloc(size); } -void ssl_free(void *p) +void ssl_mem_free(void *p) { free(p); } From 12e78e9590655a494e72e7e6d6cea391ecda32b3 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 1 Nov 2016 15:16:14 +0800 Subject: [PATCH 094/285] components/openssl: add more debug stream output function --- components/openssl/include/internal/ssl_dbg.h | 34 ++++++++++++++----- .../openssl/include/platform/ssl_port.h | 4 ++- components/openssl/platform/ssl_pm.c | 2 +- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 5b909955e4..887fe2e82b 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -39,20 +39,36 @@ #endif #if SSL_DEBUG_ENBALE - #ifndef SSL_PRINT + #if !defined(SSL_PRINT_LOG) || !defined(SSL_ERROR_LOG) || !defined(SSL_LOCAL_LOG) #include "stdio.h" extern int printf(const char *fmt, ...); - #define SSL_PRINT printf + #ifndef SSL_PRINT_LOG + #define SSL_PRINT_LOG printf + #endif + #ifndef SSL_ERROR_LOG + #define SSL_ERROR_LOG printf + #endif + #ifndef SSL_LOCAL_LOG + #define SSL_LOCAL_LOG printf + #endif #endif #else - #ifdef SSL_PRINT - #undef SSL_PRINT - #define SSL_PRINT(...) + #ifdef SSL_PRINT_LOG + #undef SSL_PRINT_LOG + #define SSL_PRINT_LOG(...) + #endif + #ifdef SSL_ERROR_LOG + #undef SSL_ERROR_LOG + #define SSL_ERROR_LOG(...) + #endif + #ifdef SSL_LOCAL_LOG + #undef SSL_LOCAL_LOG + #define SSL_LOCAL_LOG(...) #endif #endif #if SSL_DEBUG_LOCATION_ENABLE - #define SSL_DEBUG_LOCATION() SSL_PRINT("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__) + #define SSL_DEBUG_LOCATION() SSL_LOCAL_LOG("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__) #else #define SSL_DEBUG_LOCATION() #endif @@ -63,11 +79,11 @@ #define SSL_ASSERT(s) #endif -#define SSL_ERR(err, go, fmt, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(fmt, ##__VA_ARGS__); ret = err; goto go; } +#define SSL_ERR(err, go, fmt, ...) { SSL_DEBUG_LOCATION(); SSL_ERROR_LOG(fmt, ##__VA_ARGS__); ret = err; goto go; } -#define SSL_RET(go, fmt, ...) { SSL_DEBUG_LOCATION(); SSL_PRINT(fmt, ##__VA_ARGS__); goto go; } +#define SSL_RET(go, fmt, ...) { SSL_DEBUG_LOCATION(); SSL_ERROR_LOG(fmt, ##__VA_ARGS__); goto go; } -#define SSL_DEBUG(level, fmt, ...) { if (level > SSL_DEBUG_LEVEL) {SSL_PRINT(fmt, ##__VA_ARGS__);} } +#define SSL_DEBUG(level, fmt, ...) { if (level > SSL_DEBUG_LEVEL) {SSL_PRINT_LOG(fmt, ##__VA_ARGS__);} } #ifdef __cplusplus } diff --git a/components/openssl/include/platform/ssl_port.h b/components/openssl/include/platform/ssl_port.h index 1e4c2f31f9..35c8dc18f9 100644 --- a/components/openssl/include/platform/ssl_port.h +++ b/components/openssl/include/platform/ssl_port.h @@ -36,7 +36,9 @@ size_t ssl_strlen(const char *src); void ssl_speed_up_enter(void); void ssl_speed_up_exit(void); -#define SSL_PRINT(fmt, ...) ESP_LOGD("OpenSSL", fmt, ##__VA_ARGS__) +#define SSL_PRINT_LOG(fmt, ...) ESP_LOGD("openssl", fmt, ##__VA_ARGS__) +#define SSL_ERROR_LOG(fmt, ...) ESP_LOGE("openssl", fmt, ##__VA_ARGS__) +#define SSL_LOCAL_LOG(fmt, ...) ESP_LOGD("openssl", fmt, ##__VA_ARGS__) #elif defined(SSL_PLATFORM_USER_INCLUDE) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 21c0ac58c9..92e72bfdb8 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -410,7 +410,7 @@ int x509_pm_show_info(X509 *x) ssl_mem_free(buf); - SSL_PRINT("%s", buf); + SSL_DEBUG(1, "%s", buf); return 0; From f0cd38a0799f13357761e134b108c8ab8dc40abf Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 1 Nov 2016 15:25:46 +0800 Subject: [PATCH 095/285] lwip: remove tx flow control code --- components/lwip/api/sockets.c | 30 -------------------- components/lwip/include/lwip/port/lwipopts.h | 3 -- 2 files changed, 33 deletions(-) diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index df658578af..1529382f50 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -382,34 +382,6 @@ static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, static void lwip_socket_drop_registered_memberships(int s); #endif /* LWIP_IGMP */ -#if ESP_LWIP -#include "esp_wifi_internal.h" -#include "esp_system.h" - -/* Please be notified that this flow control is just a workaround for fixing wifi Q full issue. - * Under UDP/TCP pressure test, we found that the sockets may cause wifi tx queue full if the socket - * sending speed is faster than the wifi sending speed, it will finally cause the packet to be dropped - * in wifi layer, it's not acceptable in some application. That's why we introdue the tx flow control here. - * However, current solution is just a workaround, we need to consider the return value of wifi tx interface, - * and feedback the return value to lwip and let lwip do the flow control itself. - */ -static inline void esp32_tx_flow_ctrl(void) -{ -//TODO we need to do flow control for UDP -#if 0 - uint8_t _wait_delay = 1; - - while ((system_get_free_heap_size() < HEAP_HIGHWAT) || esp_wifi_internal_tx_is_stop()){ - vTaskDelay(_wait_delay/portTICK_RATE_MS); - if (_wait_delay < 64) _wait_delay *= 2; - } -#endif -} - -#else -#define esp32_tx_flow_ctrl() -#endif - /** The global array of available sockets */ static struct lwip_sock sockets[NUM_SOCKETS]; #if ESP_THREAD_SAFE @@ -1392,8 +1364,6 @@ lwip_sendto(int s, const void *data, size_t size, int flags, #endif /* LWIP_TCP */ } - esp32_tx_flow_ctrl(); - if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) { /* sockaddr does not match socket type (IPv4/IPv6) */ sock_set_errno(sock, err_to_errno(ERR_VAL)); diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 67a62b8227..b970ae5539 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -523,7 +523,6 @@ extern unsigned long os_random(void); #define ESP_IP4_ATON 1 #define ESP_LIGHT_SLEEP 1 - #define TCP_WND_DEFAULT (4*TCP_MSS) #define TCP_SND_BUF_DEFAULT (2*TCP_MSS) @@ -550,8 +549,6 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void); #define CHECKSUM_CHECK_UDP 0 #define CHECKSUM_CHECK_IP 0 -#define HEAP_HIGHWAT 20*1024 - #define LWIP_NETCONN_FULLDUPLEX 1 #define LWIP_NETCONN_SEM_PER_THREAD 1 From edf5b103449e4120c0e1b03e40f0e82f17c38daf Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 1 Nov 2016 15:34:30 +0800 Subject: [PATCH 096/285] esp32: update wifi lib 146f5962 - Make the return value of esp_wifi_internal_tx consistent with LWIP error code so that the up-layer can detect the out-of-memory error and take action accordingly, such do flow control. --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 9d18fd1a8f..b3090d8854 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 9d18fd1a8f7610130e69f8be74ec68f6399221b1 +Subproject commit b3090d885413fb78c86e7b88116cdb5c8c5e9e68 From 8c1d1e19c230b5229fcb1463ceb3cd804728b9a8 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 1 Nov 2016 15:41:10 +0800 Subject: [PATCH 097/285] OpenOCD doc fix, fix gdbstub --- components/esp32/gdbstub.c | 2 +- docs/openocd.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/esp32/gdbstub.c b/components/esp32/gdbstub.c index d75fced4cd..a43793f835 100644 --- a/components/esp32/gdbstub.c +++ b/components/esp32/gdbstub.c @@ -351,7 +351,7 @@ static int gdbReadCommand() { -void gdbstubPanicHandler(XtExcFrame *frame) { +void esp_gdbstub_panic_handler(XtExcFrame *frame) { dumpHwToRegfile(frame); //Make sure txd/rxd are enabled PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); diff --git a/docs/openocd.rst b/docs/openocd.rst index 57ee93db4a..cf1d25e60b 100644 --- a/docs/openocd.rst +++ b/docs/openocd.rst @@ -2,10 +2,10 @@ OpenOCD setup for ESP32 ----------------------- The ESP31 and ESP32 have two powerful Xtensa cores, allowing for a great deal of variety of program architectures. The FreeRTOS -OS that comes with ESP-IDF is capable multi-core pre-emptive multithreading, allowing for an intuitive way of writing software. +OS that comes with ESP-IDF is capable of multi-core pre-emptive multithreading, allowing for an intuitive way of writing software. The downside of the ease of programming is that debugging without the right tools is harder: figuring out a bug that is caused -by two threads, maybe even running simultaneously on two different CPU cures, can take a long time when all you have are printf +by two threads, maybe even running simultaneously on two different CPU cores, can take a long time when all you have are printf statements. A better and in many cases quicker way to debug such problems is by using a debugger, connected to the processors over a debug port. @@ -84,7 +84,7 @@ Connecting a debugger to OpenOCD OpenOCD should now be ready to accept gdb connections. If you have compiled the ESP32 toolchain using Crosstool-NG, or if you have downloaded a precompiled toolchain from the Espressif website, you should already have xtensa-esp32-elf-gdb, a version of gdb that can be used for this. First, make sure the project you want to debug is compiled and flashed -into the ESP32s SPI flash. Then, in a different console than OpenOCD is running in, invoke gdb. For example, for the +into the ESP32's SPI flash. Then, in a different console than OpenOCD is running in, invoke gdb. For example, for the template app, you would do this like such:: cd esp-idf-template From 578458604148beb6d4daedbdbae604d2f1406a7c Mon Sep 17 00:00:00 2001 From: Yinling Date: Tue, 1 Nov 2016 19:30:42 +0800 Subject: [PATCH 098/285] integrate unit test to CI --- .gitlab-ci.yml | 63 ++- .../CIConfigs/IT_Function_TCPIP_01.yml | 8 +- .../CIConfigs/IT_Function_TCPIP_02.yml | 2 +- .../CIConfigs/IT_Function_TCPIP_03.yml | 8 +- .../CIConfigs/IT_Function_TCPIP_04.yml | 2 +- .../CIConfigs/IT_Function_TCPIP_07.yml | 6 +- .../CIConfigs/IT_Function_TCPIP_08.yml | 6 +- .../CIConfigs/IT_Function_TCPIP_09.yml | 2 +- .../CIConfigs/IT_Function_TCPIP_11.yml | 2 +- .../CIConfigs/IT_Function_TCPIP_12.yml | 2 +- .../integration_test/InitialConditionAll.yml | 24 + .../idf_test/integration_test/TestEnvAll.yml | 47 +- components/idf_test/uint_test/TestCaseAll.yml | 1 - .../CIConfigs/UT_Function_SYS_01.yml | 8 + .../InitialConditionAll.yml | 24 + components/idf_test/unit_test/TestCaseAll.yml | 484 ++++++++++++++++++ .../TestCaseScript/IDFUnitTest/UnitTest.py | 47 ++ .../TestCaseScript/IDFUnitTest/__init__.py | 1 + .../{uint_test => unit_test}/TestEnvAll.yml | 47 +- 19 files changed, 699 insertions(+), 85 deletions(-) delete mode 100644 components/idf_test/uint_test/TestCaseAll.yml create mode 100644 components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml rename components/idf_test/{uint_test => unit_test}/InitialConditionAll.yml (99%) create mode 100644 components/idf_test/unit_test/TestCaseAll.yml create mode 100644 components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py create mode 100755 components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py rename components/idf_test/{uint_test => unit_test}/TestEnvAll.yml (95%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aff9bea8d1..e51c9dd020 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,6 @@ stages: - build + - unit_test - test - deploy @@ -75,6 +76,23 @@ build_ssc: - chmod +x gen_misc_ng.sh - ./gen_misc_ng.sh +build_esp_idf_tests: + <<: *build_template + artifacts: + paths: + - ./esp-idf-tests/build/*.bin + - ./esp-idf-tests/build/*.elf + - ./esp-idf-tests/build/*.map + - ./esp-idf-tests/build/bootloader/*.bin + expire_in: 6 mos + + script: + - git clone $GITLAB_SSH_SERVER/idf/esp-idf-tests.git + - cd esp-idf-tests + - git checkout ${CI_BUILD_REF_NAME} || echo "Using default branch..." + - make defconfig + - make + build_examples: <<: *build_template artifacts: @@ -176,7 +194,7 @@ push_master_to_github: APP_NAME: "ssc" LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" # append test level folder to TEST_CASE_FILE_PATH in before_script of test job - TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test" + TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test/integration_test" # jobs MUST set CONFIG_FILE in before_script, and overwrite the variables above if necessary artifacts: @@ -222,13 +240,34 @@ push_master_to_github: # run test - python CIRunner.py -l $LOG_PATH -c $CONFIG_FILE -e $LOCAL_ENV_CONFIG_PATH -t $TEST_CASE_FILE_PATH bin_path $APP_NAME $BIN_PATH +# template for unit test jobs +.unit_test_template: &unit_test_template + <<: *test_template + allow_failure: false + stage: unit_test + + variables: + # jobs MUST set CONFIG_FILE in before_script, and overwrite the variables above if necessary + LOCAL_ENV_CONFIG_PATH: /home/gitlab-runner/LocalConfig/ESP32_IDF + BIN_PATH: "$CI_PROJECT_DIR/esp-idf-tests/build/" + LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" + APP_NAME: "ut" + TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test/unit_test" + +UT_Function_SYS_01: + <<: *test_template + tags: + - ESP32_IDF + - UT_T1_1 + before_script: + - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/UT_Function_SYS_01.yml + IT_Function_SYS_01: <<: *test_template tags: - ESP32_IDF - SSC_T1_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_SYS_01.yml IT_Function_WIFI_01: @@ -238,7 +277,6 @@ IT_Function_WIFI_01: - SSC_T1_1 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_01.yml IT_Function_WIFI_02: @@ -248,7 +286,6 @@ IT_Function_WIFI_02: - SSC_T1_1 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_02.yml IT_Function_TCPIP_01: @@ -258,7 +295,6 @@ IT_Function_TCPIP_01: - SSC_T1_1 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_01.yml IT_Function_TCPIP_02: @@ -268,7 +304,6 @@ IT_Function_TCPIP_02: - SSC_T1_1 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_02.yml IT_Function_TCPIP_03: @@ -278,7 +313,6 @@ IT_Function_TCPIP_03: - SSC_T1_1 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_03.yml IT_Function_TCPIP_04: @@ -288,7 +322,6 @@ IT_Function_TCPIP_04: - SSC_T1_1 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_04.yml IT_Function_TCPIP_05: @@ -298,7 +331,6 @@ IT_Function_TCPIP_05: - SSC_T1_1 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_05.yml IT_Function_TCPIP_06: @@ -307,7 +339,6 @@ IT_Function_TCPIP_06: - ESP32_IDF - SSC_T1_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_06.yml IT_Function_WIFI_03: @@ -316,7 +347,6 @@ IT_Function_WIFI_03: - ESP32_IDF - SSC_T3_PhyMode before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_03.yml IT_Function_WIFI_04: @@ -325,7 +355,6 @@ IT_Function_WIFI_04: - ESP32_IDF - SSC_T1_APC before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_04.yml IT_Function_WIFI_05: @@ -334,7 +363,6 @@ IT_Function_WIFI_05: - ESP32_IDF - SSC_T1_WEP before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_05.yml IT_Function_WIFI_06: @@ -343,7 +371,6 @@ IT_Function_WIFI_06: - ESP32_IDF - SSC_T2_PhyMode before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_WIFI_06.yml IT_Function_TCPIP_07: @@ -354,7 +381,6 @@ IT_Function_TCPIP_07: - SSC_T1_2 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_07.yml IT_Function_TCPIP_08: @@ -363,7 +389,6 @@ IT_Function_TCPIP_08: - ESP32_IDF - SSC_T1_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_08.yml IT_Function_TCPIP_09: @@ -372,7 +397,6 @@ IT_Function_TCPIP_09: - ESP32_IDF - SSC_T1_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_09.yml IT_Function_TCPIP_10: @@ -383,7 +407,6 @@ IT_Function_TCPIP_10: - SSC_T1_2 - SSC_T2_1 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_10.yml IT_Function_TCPIP_11: @@ -391,8 +414,8 @@ IT_Function_TCPIP_11: tags: - ESP32_IDF - SSC_T1_1 + - SSC_T1_2 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_11.yml IT_Function_TCPIP_12: @@ -400,7 +423,5 @@ IT_Function_TCPIP_12: tags: - ESP32_IDF - SSC_T1_1 - - SSC_T1_2 before_script: - - TEST_CASE_FILE_PATH=$TEST_CASE_FILE_PATH/integration_test - CONFIG_FILE=$TEST_CASE_FILE_PATH/CIConfigs/IT_Function_TCPIP_12.yml diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml index 25a3ccda99..e86fac28ae 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_01.yml @@ -4,7 +4,7 @@ Filter: - Add: ID: [^TCPIP_DHCP_0302, TCPIP_DHCP_0302, TCPIP_DHCP_0301, TCPIP_TCP_0403, TCPIP_TCP_0402, TCPIP_TCP_0401, TCPIP_TCP_0407, TCPIP_TCP_0406, ^TCPIP_TCP_0411, TCPIP_TCP_0404, - TCPIP_TCP_0408, TCPIP_TCP_0110, TCPIP_TCP_0115, TCPIP_IP_0101, TCPIP_IP_0102, - ^TCPIP_IGMP_0102, ^TCPIP_IGMP_0101, ^TCPIP_IGMP_0104, TCPIP_IGMP_0104, TCPIP_IGMP_0103, - TCPIP_IGMP_0102, TCPIP_IGMP_0101, TCPIP_UDP_0108, TCPIP_UDP_0106, TCPIP_UDP_0107, - TCPIP_UDP_0105, TCPIP_UDP_0101, TCPIP_IGMP_0204, TCPIP_IGMP_0201, TCPIP_IGMP_0202] + TCPIP_TCP_0408, TCPIP_TCP_0110, ^TCPIP_TCP_0111, TCPIP_TCP_0115, TCPIP_IP_0101, + TCPIP_IP_0102, ^TCPIP_IGMP_0102, ^TCPIP_IGMP_0101, ^TCPIP_IGMP_0104, TCPIP_IGMP_0104, + TCPIP_IGMP_0103, TCPIP_IGMP_0102, TCPIP_IGMP_0101, TCPIP_UDP_0108, TCPIP_UDP_0106, + TCPIP_UDP_0107, TCPIP_UDP_0105, TCPIP_UDP_0101, TCPIP_IGMP_0204, TCPIP_IGMP_0201] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml index 73618e0d70..a746cdd913 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_02.yml @@ -2,7 +2,7 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [TCPIP_IGMP_0203, ^TCPIP_TCP_0403, ^TCPIP_TCP_0408, TCPIP_UDP_0201, TCPIP_UDP_0202, + ID: [TCPIP_IGMP_0202, TCPIP_IGMP_0203, ^TCPIP_TCP_0403, ^TCPIP_TCP_0408, TCPIP_UDP_0201, ^TCPIP_DHCP_0301, ^TCPIP_TCP_0101, ^TCPIP_TCP_0103, ^TCPIP_TCP_0105, ^TCPIP_TCP_0104, ^TCPIP_TCP_0107, ^TCPIP_TCP_0106, ^TCPIP_DHCP_0210, ^TCPIP_DHCP_0211, ^TCPIP_DHCP_0212, ^TCPIP_TCP_0404, TCPIP_TCP_0212, TCPIP_TCP_0210, ^TCPIP_TCP_0406, ^TCPIP_TCP_0407, diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml index b326ed721c..f5f0abe5db 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_03.yml @@ -4,7 +4,7 @@ Filter: - Add: ID: [^TCPIP_IP_0102, ^TCPIP_UDP_0105, ^TCPIP_UDP_0107, ^TCPIP_UDP_0106, ^TCPIP_UDP_0101, TCPIP_TCP_0202, ^TCPIP_UDP_0108, ^TCPIP_IGMP_0201, ^TCPIP_IGMP_0203, ^TCPIP_IGMP_0202, - ^TCPIP_IGMP_0103, TCPIP_UDP_0114, TCPIP_UDP_0113, TCPIP_UDP_0112, TCPIP_DHCP_0205, - TCPIP_DHCP_0202, TCPIP_DHCP_0203, ^TCPIP_TCP_0102, TCPIP_TCP_0106, TCPIP_TCP_0107, - TCPIP_TCP_0104, TCPIP_TCP_0105, TCPIP_TCP_0102, TCPIP_TCP_0103, TCPIP_TCP_0101, - ^TCPIP_TCP_0116, ^TCPIP_TCP_0114, ^TCPIP_TCP_0115, ^TCPIP_TCP_0112, ^TCPIP_TCP_0113] + ^TCPIP_IGMP_0103, TCPIP_UDP_0114, TCPIP_UDP_0113, TCPIP_UDP_0112, TCPIP_UDP_0202, + TCPIP_DHCP_0205, TCPIP_DHCP_0202, TCPIP_DHCP_0203, ^TCPIP_TCP_0102, TCPIP_TCP_0106, + TCPIP_TCP_0107, TCPIP_TCP_0104, TCPIP_TCP_0105, TCPIP_TCP_0102, TCPIP_TCP_0103, + TCPIP_TCP_0101, ^TCPIP_TCP_0116, ^TCPIP_TCP_0114, ^TCPIP_TCP_0115, ^TCPIP_TCP_0112] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml index 314de4a7e0..b59e8c60cd 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_04.yml @@ -2,7 +2,7 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [^TCPIP_TCP_0110, ^TCPIP_TCP_0111, TCPIP_DHCP_0209, ^TCPIP_DHCP_0209, ^TCPIP_DHCP_0207, + ID: [^TCPIP_TCP_0113, ^TCPIP_TCP_0110, TCPIP_DHCP_0209, ^TCPIP_DHCP_0209, ^TCPIP_DHCP_0207, ^TCPIP_DHCP_0206, ^TCPIP_DHCP_0205, ^TCPIP_DHCP_0204, ^TCPIP_DHCP_0203, ^TCPIP_DHCP_0202, ^TCPIP_DHCP_0201, TCPIP_TCP_0204, TCPIP_TCP_0207, TCPIP_TCP_0206, TCPIP_TCP_0201, ^TCPIP_DHCP_0101, TCPIP_TCP_0203, ^TCPIP_DHCP_0103, ^TCPIP_DHCP_0208, TCPIP_TCP_0208, diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml index 9b3d943fed..839ac972f4 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_07.yml @@ -2,9 +2,9 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC2, SSC1] Filter: - Add: - ID: [TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, + ID: [TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, + TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_ICMP_0101, TCPIP_DNS_0102, TCPIP_DNS_0102, TCPIP_DNS_0102, TCPIP_DNS_0102, TCPIP_DNS_0102, TCPIP_DNS_0101, TCPIP_DNS_0101, TCPIP_DNS_0101, TCPIP_DNS_0101, TCPIP_DNS_0101, ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, ^TCPIP_ICMP_0101, - TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109, - TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104] + TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109, TCPIP_UDP_0109] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml index a2f8f0df07..b318b09377 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_08.yml @@ -2,9 +2,9 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, + ID: [TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, TCPIP_UDP_0104, + TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0102, TCPIP_UDP_0103, TCPIP_UDP_0103, TCPIP_UDP_0103, TCPIP_UDP_0103, TCPIP_UDP_0103, ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, ^TCPIP_UDP_0307, ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, ^TCPIP_UDP_0306, - ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, - ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304] + ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305, ^TCPIP_UDP_0305] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml index 146b98cf7d..50b50a3eb6 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_09.yml @@ -2,7 +2,7 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, + ID: [^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0304, ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, ^TCPIP_UDP_0302, ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, ^TCPIP_UDP_0301, ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, ^TCPIP_UDP_0104, diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml index 86690db67c..be615d0878 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_11.yml @@ -7,4 +7,4 @@ Filter: TCPIP_UDP_0307, TCPIP_UDP_0307, TCPIP_UDP_0307, TCPIP_UDP_0307, TCPIP_UDP_0307, TCPIP_UDP_0301, TCPIP_UDP_0301, TCPIP_UDP_0301, TCPIP_UDP_0301, TCPIP_UDP_0301, TCPIP_UDP_0302, TCPIP_UDP_0302, TCPIP_UDP_0302, TCPIP_UDP_0302, TCPIP_UDP_0302, - TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303, TCPIP_UDP_0303] + TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103] diff --git a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml index 4e0495e44d..73b0187eed 100644 --- a/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml +++ b/components/idf_test/integration_test/CIConfigs/IT_Function_TCPIP_12.yml @@ -2,5 +2,5 @@ Config: {execute count: 1, execute order: in order} DUT: [SSC1] Filter: - Add: - ID: [TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103, TCPIP_DNS_0103, + ID: [^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0303, ^TCPIP_UDP_0110, ^TCPIP_UDP_0110, ^TCPIP_UDP_0110, ^TCPIP_UDP_0110, ^TCPIP_UDP_0110] diff --git a/components/idf_test/integration_test/InitialConditionAll.yml b/components/idf_test/integration_test/InitialConditionAll.yml index ba06af9f87..3821894552 100644 --- a/components/idf_test/integration_test/InitialConditionAll.yml +++ b/components/idf_test/integration_test/InitialConditionAll.yml @@ -2933,3 +2933,27 @@ initial condition: start: 87.0 tag: T3_PHY1 test script: InitCondBase +- check cmd set: + - '' + - - UT UT1 - + - [R UT1 C Tests C Failures C Ignored] + force restore cmd set: + - '' + - - FREBOOT UT1 + - [''] + - - UT UT1 - + - [R UT1 C Tests C Failures C Ignored] + initial condition detail: At UT menu page + restore cmd set: + - '' + - - FREBOOT UT1 + - [''] + - - UT UT1 - + - [R UT1 C Tests C Failures C Ignored] + restore post cmd set: + - '' + - - DELAY 0.1 + - [''] + script path: InitCondBase.py + tag: UTINIT1 + test script: InitCondBase diff --git a/components/idf_test/integration_test/TestEnvAll.yml b/components/idf_test/integration_test/TestEnvAll.yml index 2e59961d97..6b21760150 100644 --- a/components/idf_test/integration_test/TestEnvAll.yml +++ b/components/idf_test/integration_test/TestEnvAll.yml @@ -141,6 +141,29 @@ test environment: PC wired NIC should set static IP address within the same subnet with AP. Must use onboard wired NIC.', test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T1_Sleep1, + test environment detail: 'AP support DTIM placed with AT target. + + SSC target connect with Raspberry Pi by UART. + + Multimeter connect with Raspberry Pi via GPIB. + + Series multimeter between GND and VCC of SSC1. + + SSC1''s light sleep wakeup pin and wakeup indication connect with Raspberry Pi''s + GPIO. + + SSC1''s XPD connect with RSTB.', test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T1_Sleep2, + test environment detail: 'AP support DTIM placed with AT target. + + SSC target connect with Raspberry Pi by UART. + + Multimeter connect with Raspberry Pi via GPIB. + + Series multimeter between GND and VCC of SSC1. + + SSC1''s RSTB pin connect with Raspberry Pi''s GPIO.', test script: EnvBase} - {PC OS: '', Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T1_TempBox, test environment detail: '1 SSC target connect with PC by UART. @@ -191,28 +214,6 @@ test environment: test environment detail: '2 SSC target connect with PC by UART. Put them to Shield box.', test script: EnvBase} -- {PC OS: '', Special: Y, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_Sleep1, - test environment detail: 'AP support DTIM placed with AT target. - - 2 SSC target connect with PC by UART. - - Multimeter connect with PC via GPIB. - - Series multimeter between GND and VCC of SSC1. - - SSC1''s light sleep wakeup pin and wakeup indication connect with AT2''s GPIO. - - SSC1''s XPD connect with RSTB.', test script: EnvBase} -- {PC OS: '', Special: Y, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_Sleep2, - test environment detail: 'AP support DTIM placed with AT target. - - 2 SSC target connect with PC by UART. - - Multimeter connect with PC via GPIB. - - Series multimeter between GND and VCC of SSC1. - - SSC1''s RSTB pin connect with AT2''s GPIO.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_SmartConfig, test environment detail: '2 SSC target connect with PC by UART. @@ -263,6 +264,8 @@ test environment: SSC2', additional param list: '', basic param list: '', script path: EnvBase.py, tag: UART_T1_2, test environment detail: '[TBD] ESP_8266通过UART_0通过USB, UART_1 TXD 通过 TTLcable 连到PC', test script: EnvBase} +- {PC OS: '', Special: N, Target Count: 1.0, script path: EnvBase.py, tag: UT_T1_1, + test environment detail: Environment for running ESP32 unit tests, test script: EnvBase} - {PC OS: linux, Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: WebServer_T1_1, test environment detail: 'Web Server target connect with PC by UART. diff --git a/components/idf_test/uint_test/TestCaseAll.yml b/components/idf_test/uint_test/TestCaseAll.yml deleted file mode 100644 index 2b2c65e0bd..0000000000 --- a/components/idf_test/uint_test/TestCaseAll.yml +++ /dev/null @@ -1 +0,0 @@ -test cases: [] diff --git a/components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml b/components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml new file mode 100644 index 0000000000..86e191cedf --- /dev/null +++ b/components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml @@ -0,0 +1,8 @@ +Config: {execute count: 1, execute order: in order} +DUT: [UT1] +Filter: +- Add: + ID: [SYS_OS_0101, SYS_OS_0102, SYS_MISC_0103, SYS_MISC_0102, SYS_MISC_0105, SYS_MISC_0104, + SYS_MISC_0107, SYS_MISC_0106, SYS_MISC_0109, SYS_MISC_0108, SYS_MISC_0112, SYS_MISC_0113, + SYS_MISC_0110, SYS_MISC_0111, SYS_MISC_0115, SYS_LIB_0103, SYS_LIB_0102, SYS_LIB_0101, + SYS_LIB_0106, SYS_LIB_0105, SYS_LIB_0104] diff --git a/components/idf_test/uint_test/InitialConditionAll.yml b/components/idf_test/unit_test/InitialConditionAll.yml similarity index 99% rename from components/idf_test/uint_test/InitialConditionAll.yml rename to components/idf_test/unit_test/InitialConditionAll.yml index ba06af9f87..3821894552 100644 --- a/components/idf_test/uint_test/InitialConditionAll.yml +++ b/components/idf_test/unit_test/InitialConditionAll.yml @@ -2933,3 +2933,27 @@ initial condition: start: 87.0 tag: T3_PHY1 test script: InitCondBase +- check cmd set: + - '' + - - UT UT1 - + - [R UT1 C Tests C Failures C Ignored] + force restore cmd set: + - '' + - - FREBOOT UT1 + - [''] + - - UT UT1 - + - [R UT1 C Tests C Failures C Ignored] + initial condition detail: At UT menu page + restore cmd set: + - '' + - - FREBOOT UT1 + - [''] + - - UT UT1 - + - [R UT1 C Tests C Failures C Ignored] + restore post cmd set: + - '' + - - DELAY 0.1 + - [''] + script path: InitCondBase.py + tag: UTINIT1 + test script: InitCondBase diff --git a/components/idf_test/unit_test/TestCaseAll.yml b/components/idf_test/unit_test/TestCaseAll.yml new file mode 100644 index 0000000000..8732296bfc --- /dev/null +++ b/components/idf_test/unit_test/TestCaseAll.yml @@ -0,0 +1,484 @@ +test cases: +- CI ready: 'Yes' + ID: SYS_LIB_0101 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "12" + - [dummy] + comment: check if ROM is used for functions + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run lib test + sub module: Std Lib + summary: lib unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: lib + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_LIB_0102 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "13" + - [dummy] + comment: test time functions + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run lib test + sub module: Std Lib + summary: lib unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: lib + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_LIB_0103 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "14" + - [dummy] + comment: test sscanf function + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run lib test + sub module: Std Lib + summary: lib unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: lib + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_LIB_0104 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "15" + - [dummy] + comment: test sprintf function + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run lib test + sub module: Std Lib + summary: lib unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: lib + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_LIB_0105 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "16" + - [dummy] + comment: test atoX functions + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run lib test + sub module: Std Lib + summary: lib unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: lib + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_LIB_0106 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "17" + - [dummy] + comment: test ctype functions + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run lib test + sub module: Std Lib + summary: lib unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: lib + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0102 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "21" + - [dummy] + comment: mbedtls MPI self-tests + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run bignum test + sub module: Misc + summary: bignum unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: bignum + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0103 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "25" + - [dummy] + comment: test AES thread safety + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run hwcrypto test + sub module: Misc + summary: hwcrypto unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: hwcrypto + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0104 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "26" + - [dummy] + comment: test AES acceleration + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run hwcrypto test + sub module: Misc + summary: hwcrypto unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: hwcrypto + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0105 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "27" + - [dummy] + comment: test SHA thread safety + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run hwcrypto test + sub module: Misc + summary: hwcrypto unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: hwcrypto + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0106 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "33" + - [dummy] + comment: context switch saves FP registers + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run fp test + sub module: Misc + summary: fp unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: fp + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0107 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "34" + - [dummy] + comment: test FP sqrt + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run fp test + sub module: Misc + summary: fp unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: fp + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0108 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "35" + - [dummy] + comment: test FP div + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run fp test + sub module: Misc + summary: fp unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: fp + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0109 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "36" + - [dummy] + comment: test FP mul + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run fp test + sub module: Misc + summary: fp unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: fp + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0110 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "37" + - [dummy] + comment: test FP add + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run fp test + sub module: Misc + summary: fp unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: fp + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_MISC_0111 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "3" + - [dummy] + comment: Test JPEG decompression library + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run JPEG decompression test + sub module: Misc + summary: JPEG decompression library unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: tjpgd + version: v1 (2016-10-31) +- CI ready: 'Yes' + ID: SYS_MISC_0112 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "22" + - [dummy] + comment: mbedtls AES self-tests + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run mbedtls AES self-tests + sub module: Misc + summary: mbedtls AES unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: mbedtls AES + version: v1 (2016-10-31) +- CI ready: 'Yes' + ID: SYS_MISC_0113 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "17" + - [dummy] + comment: mbedtls SHA self-tests + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run mbedtls SHA self-tests + sub module: Misc + summary: mbedtls SHA unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: mbedtls SHA + version: v1 (2016-10-31) +- CI ready: 'Yes' + ID: SYS_MISC_0115 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "28" + - [dummy] + comment: test SHA acceleration + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run SHA acceleration test + sub module: Misc + summary: SHA acceleration unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: SHA acceleration + version: v1 (2016-10-31) +- CI ready: 'Yes' + ID: SYS_OS_0101 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "31" + - [dummy] + comment: FreeRTOS Event Groups + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run freertos test + sub module: OS + summary: freertos unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: freertos + version: v1 (2016-10-26) +- CI ready: 'Yes' + ID: SYS_OS_0102 + SDK: ESP32_IDF + Test App: testje + auto test: 'Yes' + category: Function + cmd set: + - IDFUnitTest/UnitTest + - - test_case = "2" + - [dummy] + comment: Freertos TLS delete cb + execution time: 0 + expected result: 1. set succeed + initial condition: UTINIT1 + level: Unit + module: System + steps: 1. run Freertos TLS delete cb test + sub module: OS + summary: Freertos TLS delete cb unit test + test environment: UT_T1_1 + test point 1: basic function + test point 2: Freertos TLS delete cb + version: v1 (2016-10-31) diff --git a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py new file mode 100644 index 0000000000..6e7cac4109 --- /dev/null +++ b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py @@ -0,0 +1,47 @@ +import re +import time + +from TCAction import PerformanceTCBase +from TCAction import TCActionBase +from NativeLog import NativeLog + +class UnitTest(PerformanceTCBase.PerformanceTCBase): + def __init__(self, name, test_env, cmd_set, timeout=30, log_path=TCActionBase.LOG_PATH): + PerformanceTCBase.PerformanceTCBase.__init__(self, name, test_env, cmd_set=cmd_set, + timeout=timeout, log_path=log_path) + + self.test_case = None + self.test_timeout = 6 + + # load param from excel + for i in range(1, len(cmd_set)): + if cmd_set[i][0] != "dummy": + cmd_string = "self." + cmd_set[i][0] + exec cmd_string + self.result_cntx = TCActionBase.ResultCheckContext(self, test_env, self.tc_name) + pass + + def send_commands(self): + self.flush_data("UT1") + + try: + self.serial_write_line("UT1", self.test_case) + time.sleep(self.test_timeout) #wait for test to run before reading result + data = self.serial_read_data("UT1") + if re.search('[^0] Tests 0 F', data): #check that number of tests run != 0 and number of tests failed == 0 + self.set_result("Success") + else: + self.set_result("Fail") + + except StandardError,e: + NativeLog.add_exception_log(e) + + def execute(self): + TCActionBase.TCActionBase.execute(self) + self.send_commands() + +def main(): + pass + +if __name__ == '__main__': + pass diff --git a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py new file mode 100755 index 0000000000..876a5d4023 --- /dev/null +++ b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/__init__.py @@ -0,0 +1 @@ +__all__ = ["UnitTest"] diff --git a/components/idf_test/uint_test/TestEnvAll.yml b/components/idf_test/unit_test/TestEnvAll.yml similarity index 95% rename from components/idf_test/uint_test/TestEnvAll.yml rename to components/idf_test/unit_test/TestEnvAll.yml index 2e59961d97..6b21760150 100644 --- a/components/idf_test/uint_test/TestEnvAll.yml +++ b/components/idf_test/unit_test/TestEnvAll.yml @@ -141,6 +141,29 @@ test environment: PC wired NIC should set static IP address within the same subnet with AP. Must use onboard wired NIC.', test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T1_Sleep1, + test environment detail: 'AP support DTIM placed with AT target. + + SSC target connect with Raspberry Pi by UART. + + Multimeter connect with Raspberry Pi via GPIB. + + Series multimeter between GND and VCC of SSC1. + + SSC1''s light sleep wakeup pin and wakeup indication connect with Raspberry Pi''s + GPIO. + + SSC1''s XPD connect with RSTB.', test script: EnvBase} +- {PC OS: '', Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T1_Sleep2, + test environment detail: 'AP support DTIM placed with AT target. + + SSC target connect with Raspberry Pi by UART. + + Multimeter connect with Raspberry Pi via GPIB. + + Series multimeter between GND and VCC of SSC1. + + SSC1''s RSTB pin connect with Raspberry Pi''s GPIO.', test script: EnvBase} - {PC OS: '', Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: SSC_T1_TempBox, test environment detail: '1 SSC target connect with PC by UART. @@ -191,28 +214,6 @@ test environment: test environment detail: '2 SSC target connect with PC by UART. Put them to Shield box.', test script: EnvBase} -- {PC OS: '', Special: Y, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_Sleep1, - test environment detail: 'AP support DTIM placed with AT target. - - 2 SSC target connect with PC by UART. - - Multimeter connect with PC via GPIB. - - Series multimeter between GND and VCC of SSC1. - - SSC1''s light sleep wakeup pin and wakeup indication connect with AT2''s GPIO. - - SSC1''s XPD connect with RSTB.', test script: EnvBase} -- {PC OS: '', Special: Y, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_Sleep2, - test environment detail: 'AP support DTIM placed with AT target. - - 2 SSC target connect with PC by UART. - - Multimeter connect with PC via GPIB. - - Series multimeter between GND and VCC of SSC1. - - SSC1''s RSTB pin connect with AT2''s GPIO.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_SmartConfig, test environment detail: '2 SSC target connect with PC by UART. @@ -263,6 +264,8 @@ test environment: SSC2', additional param list: '', basic param list: '', script path: EnvBase.py, tag: UART_T1_2, test environment detail: '[TBD] ESP_8266通过UART_0通过USB, UART_1 TXD 通过 TTLcable 连到PC', test script: EnvBase} +- {PC OS: '', Special: N, Target Count: 1.0, script path: EnvBase.py, tag: UT_T1_1, + test environment detail: Environment for running ESP32 unit tests, test script: EnvBase} - {PC OS: linux, Special: Y, Target Count: 1.0, script path: EnvBase.py, tag: WebServer_T1_1, test environment detail: 'Web Server target connect with PC by UART. From 1d3626c119d2acbb164571ce01dc5326f418172f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 1 Nov 2016 20:08:29 +0800 Subject: [PATCH 099/285] docs: fix typos, build docs with gitlab CI --- .gitlab-ci.yml | 14 ++++++++++++++ docs/eclipse-setup.rst | 2 +- docs/index.rst | 5 +---- docs/requirements.txt | 7 ++++++- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aff9bea8d1..25e4d4f177 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -92,6 +92,20 @@ build_examples: - cd build_examples - ${IDF_PATH}/make/build_examples.sh +build_docs: + stage: build + image: espressif/esp32-ci-env + tags: + - build_docs + script: + - cd docs + - make html + artifacts: + paths: + - docs/_build/html + expire_in: 6 mos + + test_nvs_on_host: stage: test image: espressif/esp32-ci-env diff --git a/docs/eclipse-setup.rst b/docs/eclipse-setup.rst index 32d60a17a0..fbad93be6c 100644 --- a/docs/eclipse-setup.rst +++ b/docs/eclipse-setup.rst @@ -1,4 +1,4 @@ -Build and Falsh with Eclipse IDE +Build and Flash with Eclipse IDE ******************************** Installing Eclipse IDE diff --git a/docs/index.rst b/docs/index.rst index da03346e71..c973950615 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,10 +3,7 @@ ESP32 Programming Guide .. caution:: - This DRAF version of documentation developed within `ESP-IDF 1.0 Release plan `_. - It is scheduled for merging with `espressif/esp-idf `_ repository at the release date. - Before merging it may be incomplete, or not fully in sync with espressif/esp-idf. - Please mind your step! + Until ESP-IDF release 1.0, this documentation is a draft. It is incomplete and may have mistakes. Please mind your step! Contents: diff --git a/docs/requirements.txt b/docs/requirements.txt index 188f51e62d..debed28677 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,6 @@ -breathe \ No newline at end of file +# This is a list of python packages used to generate documentation. This file is used with pip: +# pip install requirements.txt +# +sphinx +sphinx-rtd-theme +breathe From c67ac340c71b2b623ca12a2ee3d85f6f6e6ca96c Mon Sep 17 00:00:00 2001 From: Yinling Date: Tue, 1 Nov 2016 20:33:23 +0800 Subject: [PATCH 100/285] use correct template for unit test jobs --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e51c9dd020..ae93421599 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -255,7 +255,7 @@ push_master_to_github: TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test/unit_test" UT_Function_SYS_01: - <<: *test_template + <<: *unit_test_template tags: - ESP32_IDF - UT_T1_1 From 4f71d741ec79c1a155d4130a3936f2971dd6caf9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 1 Nov 2016 20:58:47 +0800 Subject: [PATCH 101/285] docs: deploy built docs --- .gitlab-ci.yml | 27 ++++++++++++++++++++++++++- docs/conf.py | 22 ++++++++++++++++++---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 25e4d4f177..931009f4b9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -103,7 +103,7 @@ build_docs: artifacts: paths: - docs/_build/html - expire_in: 6 mos + expire_in: 1 mos test_nvs_on_host: @@ -173,6 +173,31 @@ push_master_to_github: - git push --follow-tags github HEAD:master +deploy_docs: + before_script: + - echo "Not setting up GitLab key, not fetching submodules" + stage: deploy + only: + - master + - triggers + tags: + - deploy + image: espressif/esp32-ci-env + script: + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo -n $DOCS_DEPLOY_KEY > ~/.ssh/id_rsa_base64 + - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - echo -e "Host $DOCS_SERVER\n\tStrictHostKeyChecking no\n\tUser $DOCS_SERVER_USER\n" >> ~/.ssh/config + - export GIT_VER=$(git describe --always) + - cd docs/_build/ + - mv html $GIT_VER + - tar czvf $GIT_VER.tar.gz $GIT_VER + - scp $GIT_VER.tar.gz $DOCS_SERVER:$DOCS_PATH + - ssh $DOCS_SERVER -x "cd $DOCS_PATH && tar xzvf $GIT_VER.tar.gz && rm -f latest && ln -s $GIT_VER latest" + + # AUTO GENERATED PART START, DO NOT MODIFY CONTENT BELOW # template for test jobs .test_template: &test_template diff --git a/docs/conf.py b/docs/conf.py index 8a8821fb0d..8a4a275c8a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,9 +26,20 @@ import os # added by krzychb, 24-Oct-2016 # -from subprocess import call +from subprocess import call, Popen, PIPE +import shlex + call('doxygen') +# -- Function to get output of a command ---------------------------------- +def run_cmd_get_output(cmd): + process = Popen(shlex.split(cmd), stdout=PIPE) + (output, err) = process.communicate() + exit_code = process.wait() + if exit_code != 0: + raise RuntimeError('command line program has failed') + return output + # -- General configuration ------------------------------------------------ @@ -64,10 +75,13 @@ copyright = u'2016, Espressif' # |version| and |release|, also used in various other places throughout the # built documents. # -# The short X.Y version. -version = '1.0' +# This is supposed to be "the short X.Y version", but it's the only version +# visible when you open index.html. +# Display full version to make things less confusing. +# If needed, nearest tag is returned by 'git describe --abbrev=0'. +version = run_cmd_get_output('git describe') # The full version, including alpha/beta/rc tags. -release = '1.0' +release = run_cmd_get_output('git describe') # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From e1f0c98ef531ed803bc5ad9100c0a971f8c272b2 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 1 Nov 2016 11:48:32 +0800 Subject: [PATCH 102/285] Modify gpio.h and ledc.h --- components/driver/include/driver/gpio.h | 436 ++++++++++++------------ components/driver/include/driver/ledc.h | 330 +++++++++--------- 2 files changed, 388 insertions(+), 378 deletions(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 001be2a39d..7106489d6c 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -27,43 +27,43 @@ extern "C" { #endif -#define GPIO_SEL_0 (BIT(0)) /* Pin 0 selected */ -#define GPIO_SEL_1 (BIT(1)) /* Pin 1 selected */ -#define GPIO_SEL_2 (BIT(2)) /* Pin 2 selected */ -#define GPIO_SEL_3 (BIT(3)) /* Pin 3 selected */ -#define GPIO_SEL_4 (BIT(4)) /* Pin 4 selected */ -#define GPIO_SEL_5 (BIT(5)) /* Pin 5 selected */ -#define GPIO_SEL_6 (BIT(6)) /* Pin 6 selected */ -#define GPIO_SEL_7 (BIT(7)) /* Pin 7 selected */ -#define GPIO_SEL_8 (BIT(8)) /* Pin 8 selected */ -#define GPIO_SEL_9 (BIT(9)) /* Pin 9 selected */ -#define GPIO_SEL_10 (BIT(10)) /* Pin 10 selected */ -#define GPIO_SEL_11 (BIT(11)) /* Pin 11 selected */ -#define GPIO_SEL_12 (BIT(12)) /* Pin 12 selected */ -#define GPIO_SEL_13 (BIT(13)) /* Pin 13 selected */ -#define GPIO_SEL_14 (BIT(14)) /* Pin 14 selected */ -#define GPIO_SEL_15 (BIT(15)) /* Pin 15 selected */ -#define GPIO_SEL_16 (BIT(16)) /* Pin 16 selected */ -#define GPIO_SEL_17 (BIT(17)) /* Pin 17 selected */ -#define GPIO_SEL_18 (BIT(18)) /* Pin 18 selected */ -#define GPIO_SEL_19 (BIT(19)) /* Pin 19 selected */ +#define GPIO_SEL_0 (BIT(0)) /*!< Pin 0 selected */ +#define GPIO_SEL_1 (BIT(1)) /*!< Pin 1 selected */ +#define GPIO_SEL_2 (BIT(2)) /*!< Pin 2 selected */ +#define GPIO_SEL_3 (BIT(3)) /*!< Pin 3 selected */ +#define GPIO_SEL_4 (BIT(4)) /*!< Pin 4 selected */ +#define GPIO_SEL_5 (BIT(5)) /*!< Pin 5 selected */ +#define GPIO_SEL_6 (BIT(6)) /*!< Pin 6 selected */ +#define GPIO_SEL_7 (BIT(7)) /*!< Pin 7 selected */ +#define GPIO_SEL_8 (BIT(8)) /*!< Pin 8 selected */ +#define GPIO_SEL_9 (BIT(9)) /*!< Pin 9 selected */ +#define GPIO_SEL_10 (BIT(10)) /*!< Pin 10 selected */ +#define GPIO_SEL_11 (BIT(11)) /*!< Pin 11 selected */ +#define GPIO_SEL_12 (BIT(12)) /*!< Pin 12 selected */ +#define GPIO_SEL_13 (BIT(13)) /*!< Pin 13 selected */ +#define GPIO_SEL_14 (BIT(14)) /*!< Pin 14 selected */ +#define GPIO_SEL_15 (BIT(15)) /*!< Pin 15 selected */ +#define GPIO_SEL_16 (BIT(16)) /*!< Pin 16 selected */ +#define GPIO_SEL_17 (BIT(17)) /*!< Pin 17 selected */ +#define GPIO_SEL_18 (BIT(18)) /*!< Pin 18 selected */ +#define GPIO_SEL_19 (BIT(19)) /*!< Pin 19 selected */ -#define GPIO_SEL_21 (BIT(21)) /* Pin 21 selected */ -#define GPIO_SEL_22 (BIT(22)) /* Pin 22 selected */ -#define GPIO_SEL_23 (BIT(23)) /* Pin 23 selected */ +#define GPIO_SEL_21 (BIT(21)) /*!< Pin 21 selected */ +#define GPIO_SEL_22 (BIT(22)) /*!< Pin 22 selected */ +#define GPIO_SEL_23 (BIT(23)) /*!< Pin 23 selected */ -#define GPIO_SEL_25 (BIT(25)) /* Pin 25 selected */ -#define GPIO_SEL_26 (BIT(26)) /* Pin 26 selected */ -#define GPIO_SEL_27 (BIT(27)) /* Pin 27 selected */ +#define GPIO_SEL_25 (BIT(25)) /*!< Pin 25 selected */ +#define GPIO_SEL_26 (BIT(26)) /*!< Pin 26 selected */ +#define GPIO_SEL_27 (BIT(27)) /*!< Pin 27 selected */ -#define GPIO_SEL_32 ((uint64_t)(((uint64_t)1)<<32)) /* Pin 32 selected */ -#define GPIO_SEL_33 ((uint64_t)(((uint64_t)1)<<33)) /* Pin 33 selected */ -#define GPIO_SEL_34 ((uint64_t)(((uint64_t)1)<<34)) /* Pin 34 selected */ -#define GPIO_SEL_35 ((uint64_t)(((uint64_t)1)<<35)) /* Pin 35 selected */ -#define GPIO_SEL_36 ((uint64_t)(((uint64_t)1)<<36)) /* Pin 36 selected */ -#define GPIO_SEL_37 ((uint64_t)(((uint64_t)1)<<37)) /* Pin 37 selected */ -#define GPIO_SEL_38 ((uint64_t)(((uint64_t)1)<<38)) /* Pin 38 selected */ -#define GPIO_SEL_39 ((uint64_t)(((uint64_t)1)<<39)) /* Pin 39 selected */ +#define GPIO_SEL_32 ((uint64_t)(((uint64_t)1)<<32)) /*!< Pin 32 selected */ +#define GPIO_SEL_33 ((uint64_t)(((uint64_t)1)<<33)) /*!< Pin 33 selected */ +#define GPIO_SEL_34 ((uint64_t)(((uint64_t)1)<<34)) /*!< Pin 34 selected */ +#define GPIO_SEL_35 ((uint64_t)(((uint64_t)1)<<35)) /*!< Pin 35 selected */ +#define GPIO_SEL_36 ((uint64_t)(((uint64_t)1)<<36)) /*!< Pin 36 selected */ +#define GPIO_SEL_37 ((uint64_t)(((uint64_t)1)<<37)) /*!< Pin 37 selected */ +#define GPIO_SEL_38 ((uint64_t)(((uint64_t)1)<<38)) /*!< Pin 38 selected */ +#define GPIO_SEL_39 ((uint64_t)(((uint64_t)1)<<39)) /*!< Pin 39 selected */ #define GPIO_PIN_REG_0 PERIPHS_IO_MUX_GPIO0_U #define GPIO_PIN_REG_1 PERIPHS_IO_MUX_U0TXD_U @@ -117,47 +117,47 @@ extern const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT]; #define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((GPIO_IS_VALID_GPIO(gpio_num)) && (gpio_num < 34)) //to decide whether it can be a valid GPIO number of output mode typedef enum { - GPIO_NUM_0 = 0, - GPIO_NUM_1 = 1, - GPIO_NUM_2 = 2, - GPIO_NUM_3 = 3, - GPIO_NUM_4 = 4, - GPIO_NUM_5 = 5, - GPIO_NUM_6 = 6, - GPIO_NUM_7 = 7, - GPIO_NUM_8 = 8, - GPIO_NUM_9 = 9, - GPIO_NUM_10 = 10, - GPIO_NUM_11 = 11, - GPIO_NUM_12 = 12, - GPIO_NUM_13 = 13, - GPIO_NUM_14 = 14, - GPIO_NUM_15 = 15, - GPIO_NUM_16 = 16, - GPIO_NUM_17 = 17, - GPIO_NUM_18 = 18, - GPIO_NUM_19 = 19, + GPIO_NUM_0 = 0, /*!< GPIO0, input and output */ + GPIO_NUM_1 = 1, /*!< GPIO1, input and output */ + GPIO_NUM_2 = 2, /*!< GPIO2, input and output */ + GPIO_NUM_3 = 3, /*!< GPIO3, input and output */ + GPIO_NUM_4 = 4, /*!< GPIO4, input and output */ + GPIO_NUM_5 = 5, /*!< GPIO5, input and output */ + GPIO_NUM_6 = 6, /*!< GPIO6, input and output */ + GPIO_NUM_7 = 7, /*!< GPIO7, input and output */ + GPIO_NUM_8 = 8, /*!< GPIO8, input and output */ + GPIO_NUM_9 = 9, /*!< GPIO9, input and output */ + GPIO_NUM_10 = 10, /*!< GPIO10, input and output */ + GPIO_NUM_11 = 11, /*!< GPIO11, input and output */ + GPIO_NUM_12 = 12, /*!< GPIO12, input and output */ + GPIO_NUM_13 = 13, /*!< GPIO13, input and output */ + GPIO_NUM_14 = 14, /*!< GPIO14, input and output */ + GPIO_NUM_15 = 15, /*!< GPIO15, input and output */ + GPIO_NUM_16 = 16, /*!< GPIO16, input and output */ + GPIO_NUM_17 = 17, /*!< GPIO17, input and output */ + GPIO_NUM_18 = 18, /*!< GPIO18, input and output */ + GPIO_NUM_19 = 19, /*!< GPIO19, input and output */ - GPIO_NUM_21 = 21, - GPIO_NUM_22 = 22, - GPIO_NUM_23 = 23, + GPIO_NUM_21 = 21, /*!< GPIO21, input and output */ + GPIO_NUM_22 = 22, /*!< GPIO22, input and output */ + GPIO_NUM_23 = 23, /*!< GPIO23, input and output */ - GPIO_NUM_25 = 25, - GPIO_NUM_26 = 26, - GPIO_NUM_27 = 27, + GPIO_NUM_25 = 25, /*!< GPIO25, input and output */ + GPIO_NUM_26 = 26, /*!< GPIO26, input and output */ + GPIO_NUM_27 = 27, /*!< GPIO27, input and output */ - GPIO_NUM_32 = 32, - GPIO_NUM_33 = 33, - GPIO_NUM_34 = 34, /*input mode only */ - GPIO_NUM_35 = 35, /*input mode only */ - GPIO_NUM_36 = 36, /*input mode only */ - GPIO_NUM_37 = 37, /*input mode only */ - GPIO_NUM_38 = 38, /*input mode only */ - GPIO_NUM_39 = 39, /*input mode only */ + GPIO_NUM_32 = 32, /*!< GPIO32, input and output */ + GPIO_NUM_33 = 33, /*!< GPIO32, input and output */ + GPIO_NUM_34 = 34, /*!< GPIO34, input mode only */ + GPIO_NUM_35 = 35, /*!< GPIO35, input mode only */ + GPIO_NUM_36 = 36, /*!< GPIO36, input mode only */ + GPIO_NUM_37 = 37, /*!< GPIO37, input mode only */ + GPIO_NUM_38 = 38, /*!< GPIO38, input mode only */ + GPIO_NUM_39 = 39, /*!< GPIO39, input mode only */ } gpio_num_t; typedef enum { - GPIO_INTR_DISABLE = 0, /*!< disable GPIO interrupt */ + GPIO_INTR_DISABLE = 0, /*!< Disable GPIO interrupt */ GPIO_INTR_POSEDGE = 1, /*!< GPIO interrupt type : rising edge */ GPIO_INTR_NEGEDGE = 2, /*!< GPIO interrupt type : falling edge */ GPIO_INTR_ANYEDGE = 3, /*!< GPIO interrupt type : both rising and falling edge */ @@ -175,13 +175,13 @@ typedef enum { } gpio_mode_t; typedef enum { - GPIO_PULLUP_DISABLE = 0x0, /*!< disable GPIO pull-up resistor */ - GPIO_PULLUP_ENABLE = 0x1, /*!< enable GPIO pull-up resistor */ + GPIO_PULLUP_DISABLE = 0x0, /*!< Disable GPIO pull-up resistor */ + GPIO_PULLUP_ENABLE = 0x1, /*!< Enable GPIO pull-up resistor */ } gpio_pullup_t; typedef enum { - GPIO_PULLDOWN_DISABLE = 0x0, /*!< disable GPIO pull-down resistor */ - GPIO_PULLDOWN_ENABLE = 0x1, /*!< enable GPIO pull-down resistor */ + GPIO_PULLDOWN_DISABLE = 0x0, /*!< Disable GPIO pull-down resistor */ + GPIO_PULLDOWN_ENABLE = 0x1, /*!< Enable GPIO pull-down resistor */ } gpio_pulldown_t; typedef struct { @@ -192,12 +192,6 @@ typedef struct { gpio_int_type_t intr_type; /*!< GPIO interrupt type */ } gpio_config_t; -typedef enum { - GPIO_LOW_LEVEL = 0, - GPIO_HIGH_LEVEL = 1, - GPIO_LEVEL_ERR, -} gpio_level_t; - typedef enum { GPIO_PULLUP_ONLY, /*!< Pad pull up */ GPIO_PULLDOWN_ONLY, /*!< Pad pull down */ @@ -207,254 +201,250 @@ typedef enum { typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num); -/** \defgroup Driver_APIs Driver APIs - * @brief Driver APIs - */ - -/** @addtogroup Driver_APIs - * @{ - */ - -/** \defgroup GPIO_Driver_APIs GPIO Driver APIs - * @brief GPIO APIs - */ - -/** @addtogroup GPIO_Driver_APIs - * @{ - */ - /** - * @brief GPIO common configuration + * @brief GPIO common configuration * - * Use this Function ,Configure GPIO's Mode,pull-up,PullDown,IntrType + * Configure GPIO's Mode,pull-up,PullDown,IntrType * - * @param[in] pGPIOConfig - * pGPIOConfig.pin_bit_mask : Configure GPIO pins bits,set this parameter with bit mask. - * If you want to configure GPIO34 and GPIO16, pin_bit_mask=GPIO_Pin_16|GPIO_Pin_34; - * pGPIOConfig.mode : Configure GPIO mode,such as output ,input... - * pGPIOConfig.pull_up_en : Enable or Disable pull-up - * pGPIOConfig.pull_down_en : Enable or Disable pull-down - * pGPIOConfig.intr_type : Configure GPIO interrupt trigger type - * @return ESP_OK: success ; - * ESP_ERR_INVALID_ARG: parameter error - * ESP_FAIL : GPIO error + * @param pGPIOConfig Pointer to GPIO configure struct + * + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error * */ esp_err_t gpio_config(gpio_config_t *pGPIOConfig); /** - * @brief GPIO set interrupt trigger type + * @brief GPIO set interrupt trigger type * - * @param[in] gpio_num : GPIO number. - * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); - * @param[in] intr_type: interrupt type, select from gpio_int_type_t + * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param intr_type Interrupt type, select from gpio_int_type_t * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error * */ esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type); /** - * @brief enable GPIO module interrupt signal + * @brief Enable GPIO module interrupt signal * - * @param[in] gpio_num : GPIO number. - * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error * */ esp_err_t gpio_intr_enable(gpio_num_t gpio_num); /** - * @brief disable GPIO module interrupt signal + * @brief Disable GPIO module interrupt signal * - * @param[in] gpio_num : GPIO number. - * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK success + * - ESP_ERR_INVALID_ARG Parameter error * */ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); /** - * @brief GPIO set output level + * @brief GPIO set output level * - * @param[in] gpio_num : GPIO number. - * If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); - * @param[in] level : Output level. 0: low ; 1: high + * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param level Output level. 0: low ; 1: high * - * @return ESP_OK : success - * ESP_FAIL : GPIO error + * @return + * - ESP_OK Success + * - GPIO_IS_VALID_GPIO GPIO number error * */ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); /** - * @brief GPIO get input level + * @brief GPIO get input level * - * @param[in] gpio_num : GPIO number. - * If you want to get level of pin GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to get level of pin GPIO16, gpio_num should be GPIO_NUM_16 (16); * - * @return 0 : the GPIO input level is 0 - * 1 : the GPIO input level is 1 + * @return + * - 0 the GPIO input level is 0 + * - 1 the GPIO input level is 1 * */ int gpio_get_level(gpio_num_t gpio_num); /** - * @brief GPIO set direction + * @brief GPIO set direction * * Configure GPIO direction,such as output_only,input_only,output_and_input * - * @param[in] gpio_num : Configure GPIO pins number,it should be GPIO number. - * If you want to set direction of GPIO16, gpio_num should be GPIO_NUM_16 (16); - * @param[in] mode : Configure GPIO direction,such as output_only,input_only,... + * @param gpio_num Configure GPIO pins number, it should be GPIO number. If you want to set direction of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param mode GPIO direction * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG : fail - * ESP_FAIL : GPIO error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG GPIO error * */ esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode); /** - * @brief GPIO set pull + * @brief GPIO set pull * * User this Function,configure GPIO pull mode,such as pull-up,pull-down * - * @param[in] gpio_num : Configure GPIO pins number,it should be GPIO number. - * If you want to set pull up or down mode for GPIO16,gpio_num should be GPIO_NUM_16 (16); - * @param[in] pull : Configure GPIO pull up/down mode,such as pullup_only,pulldown_only,pullup_and_pulldown,... + * @param gpio_num GPIO number. If you want to set pull up or down mode for GPIO16,gpio_num should be GPIO_NUM_16 (16); + * @param pull GPIO pull up/down mode. * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG : fail - * ESP_FAIL : GPIO error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG : Parameter error * */ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull); /** - * @brief enable GPIO wake-up function. + * @brief enable GPIO wake-up function. * - * @param gpio_num : GPIO number. + * @param gpio_num GPIO number. * - * @param intr_type : only GPIO_INTR_LOLEVEL\GPIO_INTR_HILEVEL can be used + * @param intr_type GPIO wake-up type. Only GPIO_INTR_LOLEVEL\GPIO_INTR_HILEVEL can be used * - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type); /** - * @brief disable GPIO wake-up function. + * @brief Disable GPIO wake-up function. * - * @param gpio_num: GPIO number + * @param gpio_num GPIO number * - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); /** * @brief register GPIO interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. + * @note * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. - * TODO: to move INUM options to menu_config - * @param gpio_intr_num : GPIO interrupt number,check the info in soc.h, and please see the core-isa.h for more details - * @param (*fn)(void* ) : interrupt handler function. - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * @param arg : parameter for handler function * - * @return ESP_OK : success ; - * ESP_FAIL: gpio error + * @param gpio_intr_num GPIO interrupt number,check the info in soc.h, and please see the core-isa.h for more details + * @param fn Interrupt handler function. + * + * @note + * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". + * + * @param arg Parameter for handler function + * + * @return + * - ESP_OK Success ; + * - ESP_ERR_INVALID_ARG GPIO error */ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg); /** * *************** ATTENTION ********************/ /** - * - * Each GPIO has its own separate configuration register, so we do not use - * a lock to serialize access to them. This works under the assumption that - * no situation will occur where two tasks try to configure the same GPIO - * pin simultaneously. It is up to the application developer to guarantee this. + *@attention + * Each GPIO has its own separate configuration register, so we do not use + * a lock to serialize access to them. This works under the assumption that + * no situation will occur where two tasks try to configure the same GPIO + * pin simultaneously. It is up to the application developer to guarantee this. */ - -/*----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ */ -/* gpio_config_t io_conf; +/** + *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + * @code{c} + * gpio_config_t io_conf; * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt * io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode * io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19 * io_conf.pull_down_en = 0; //disable pull-down mode * io_conf.pull_up_en = 0; //disable pull-up mode * gpio_config(&io_conf); //configure GPIO with the given settings + * @endcode **/ -/*----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ */ -/* io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt + +/** + *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + * @code{c} + * io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt * io_conf.mode = GPIO_MODE_INPUT; //set as input * io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5 * io_conf.pull_down_en = 0; //disable pull-down mode * io_conf.pull_up_en = 1; //enable pull-up mode * gpio_config(&io_conf); //configure GPIO with the given settings - *----------EXAMPLE TO SET ISR HANDLER ----------------------*/ -/* gpio_isr_register(18,gpio_intr_test,NULL); //hook the isr handler for GPIO interrupt - * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * //NOTE1:user should arrange the INUMs that used, better not to use a same INUM for different interrupt. - * //NOTE2:do not pick the INUM that already occupied by the system. - * //NOTE3:refer to soc.h to check which INUMs that can be used. - *-------------EXAMPLE OF HANDLER FUNCTION-------------------*/ -/*#include "esp_attr.h" + * @endcode + */ +/** + *----------EXAMPLE TO SET ISR HANDLER ---------------------- + * @code{c} + * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. + * gpio_isr_register(18,gpio_intr_test,NULL); //hook the isr handler for GPIO interrupt + * @endcode + * @note + * 1. user should arrange the INUMs that used, better not to use a same INUM for different interrupt. + * 2. do not pick the INUM that already occupied by the system. + * 3. refer to soc.h to check which INUMs that can be used. + */ +/** + *-------------EXAMPLE OF HANDLER FUNCTION-------------------* + * @code{c} + * #include "esp_attr.h" * void IRAM_ATTR gpio_intr_test(void* arg) - *{ - * //GPIO intr process - * ets_printf("in gpio_intr\n"); - * uint32_t gpio_num = 0; - * uint32_t gpio_intr_status = READ_PERI_REG(GPIO_STATUS_REG); //read status to get interrupt status for GPIO0-31 - * uint32_t gpio_intr_status_h = READ_PERI_REG(GPIO_STATUS1_REG);//read status1 to get interrupt status for GPIO32-39 - * SET_PERI_REG_MASK(GPIO_STATUS_W1TC_REG, gpio_intr_status); //Clear intr for gpio0-gpio31 - * SET_PERI_REG_MASK(GPIO_STATUS1_W1TC_REG, gpio_intr_status_h); //Clear intr for gpio32-39 - * do { - * if(gpio_num < 32) { - * if(gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 - * ets_printf("Intr GPIO%d ,val: %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } else { - * if(gpio_intr_status_h & BIT(gpio_num - 32)) { - * ets_printf("Intr GPIO%d, val : %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } - * } while(++gpio_num < GPIO_PIN_COUNT); - *} - *----EXAMPLE OF I2C CONFIG AND PICK SIGNAL FOR IO MATRIX---*/ -/* gpio_config_t io_conf; - * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - * io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; //set as output mode - * io_conf.pin_bit_mask = GPIO_SEL_21 | GPIO_SEL_22; //bit mask of the pins that you want to set,e.g.GPIO21/22 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 1; //enable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * gpio_matrix_out(21, EXT_I2C_SCL_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_out(22, EXT_I2C_SDA_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_in( 22, EXT_I2C_SDA_I_IDX, 0); //set input signal for io_matrix + * { + * //GPIO intr process + * ets_printf("in gpio_intr\n"); + * uint32_t gpio_num = 0; + * uint32_t gpio_intr_status = READ_PERI_REG(GPIO_STATUS_REG); //read status to get interrupt status for GPIO0-31 + * uint32_t gpio_intr_status_h = READ_PERI_REG(GPIO_STATUS1_REG);//read status1 to get interrupt status for GPIO32-39 + * SET_PERI_REG_MASK(GPIO_STATUS_W1TC_REG, gpio_intr_status); //Clear intr for gpio0-gpio31 + * SET_PERI_REG_MASK(GPIO_STATUS1_W1TC_REG, gpio_intr_status_h); //Clear intr for gpio32-39 + * do { + * if(gpio_num < 32) { + * if(gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 + * ets_printf("Intr GPIO%d ,val: %d\n",gpio_num,gpio_get_level(gpio_num)); + * //This is an isr handler, you should post an event to process it in RTOS queue. + * } + * } else { + * if(gpio_intr_status_h & BIT(gpio_num - 32)) { + * ets_printf("Intr GPIO%d, val : %d\n",gpio_num,gpio_get_level(gpio_num)); + * //This is an isr handler, you should post an event to process it in RTOS queue. + * } + * } + * } while(++gpio_num < GPIO_PIN_COUNT); + * } + * @endcode + */ + +/** + *----EXAMPLE OF I2C CONFIG AND PICK SIGNAL FOR IO MATRIX---* + * @code{c} + * gpio_config_t io_conf; + * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt + * io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; //set as output mode + * io_conf.pin_bit_mask = GPIO_SEL_21 | GPIO_SEL_22; //bit mask of the pins that you want to set,e.g.GPIO21/22 + * io_conf.pull_down_en = 0; //disable pull-down mode + * io_conf.pull_up_en = 1; //enable pull-up mode + * gpio_config(&io_conf); //configure GPIO with the given settings + * gpio_matrix_out(21, EXT_I2C_SCL_O_IDX, 0, 0); //set output signal for io_matrix + * gpio_matrix_out(22, EXT_I2C_SDA_O_IDX, 0, 0); //set output signal for io_matrix + * gpio_matrix_in( 22, EXT_I2C_SDA_I_IDX, 0); //set input signal for io_matrix + * @endcode * */ -/** - * @} - */ - -/** - * @} - */ - #ifdef __cplusplus } #endif diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 79a6c7f9f3..3ab0ebff1e 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -30,68 +30,68 @@ extern "C" { #define LEDC_REF_CLK_HZ (1*1000000) typedef enum { - LEDC_HIGH_SPEED_MODE = 0, /*LEDC high speed speed_mode */ + LEDC_HIGH_SPEED_MODE = 0, /*!< LEDC high speed speed_mode */ //in this version, we only support high speed speed_mode. We will access low speed speed_mode later - //LEDC_LOW_SPEED_MODE, /*LEDC low speed speed_mode */ - LEDC_SPEED_MODE_MAX, + //LEDC_LOW_SPEED_MODE, /*!< LEDC low speed speed_mode */ + LEDC_SPEED_MODE_MAX, /*!< LEDC speed limit */ } ledc_mode_t; typedef enum { - LEDC_INTR_DISABLE = 0, /*Disable LEDC interrupt */ - LEDC_INTR_FADE_END, /*Enable LEDC interrupt */ + LEDC_INTR_DISABLE = 0, /*!< Disable LEDC interrupt */ + LEDC_INTR_FADE_END, /*!< Enable LEDC interrupt */ } ledc_intr_type_t; typedef enum { - LEDC_DUTY_DIR_DECREASE = 0, /*LEDC duty decrease direction */ - LEDC_DUTY_DIR_INCREASE = 1, /*LEDC duty increase direction */ + LEDC_DUTY_DIR_DECREASE = 0, /*!< LEDC duty decrease direction */ + LEDC_DUTY_DIR_INCREASE = 1, /*!< LEDC duty increase direction */ } ledc_duty_direction_t; typedef enum { - LEDC_REF_TICK = 0, /*LEDC timer clock divided from reference tick(1Mhz) */ - LEDC_APB_CLK, /*LEDC timer clock divided from APB clock(80Mhz)*/ + LEDC_REF_TICK = 0, /*!< LEDC timer clock divided from reference tick(1Mhz) */ + LEDC_APB_CLK, /*!< LEDC timer clock divided from APB clock(80Mhz)*/ } ledc_clk_src_t; typedef enum { - LEDC_TIMER_0 = 0, /*LEDC source timer TIMER0 */ - LEDC_TIMER_1, /*LEDC source timer TIMER1 */ - LEDC_TIMER_2, /*LEDC source timer TIMER2 */ - LEDC_TIMER_3, /*LEDC source timer TIMER3 */ + LEDC_TIMER_0 = 0, /*!< LEDC source timer TIMER0 */ + LEDC_TIMER_1, /*!< LEDC source timer TIMER1 */ + LEDC_TIMER_2, /*!< LEDC source timer TIMER2 */ + LEDC_TIMER_3, /*!< LEDC source timer TIMER3 */ } ledc_timer_t; typedef enum { - LEDC_CHANNEL_0 = 0, /*LEDC channel 0 */ - LEDC_CHANNEL_1, /*LEDC channel 1 */ - LEDC_CHANNEL_2, /*LEDC channel 2 */ - LEDC_CHANNEL_3, /*LEDC channel 3 */ - LEDC_CHANNEL_4, /*LEDC channel 4 */ - LEDC_CHANNEL_5, /*LEDC channel 5 */ - LEDC_CHANNEL_6, /*LEDC channel 6 */ - LEDC_CHANNEL_7, /*LEDC channel 7 */ + LEDC_CHANNEL_0 = 0, /*!< LEDC channel 0 */ + LEDC_CHANNEL_1, /*!< LEDC channel 1 */ + LEDC_CHANNEL_2, /*!< LEDC channel 2 */ + LEDC_CHANNEL_3, /*!< LEDC channel 3 */ + LEDC_CHANNEL_4, /*!< LEDC channel 4 */ + LEDC_CHANNEL_5, /*!< LEDC channel 5 */ + LEDC_CHANNEL_6, /*!< LEDC channel 6 */ + LEDC_CHANNEL_7, /*!< LEDC channel 7 */ } ledc_channel_t; typedef enum { - LEDC_TIMER_10_BIT = 10, /*LEDC PWM depth 10Bit */ - LEDC_TIMER_11_BIT = 11, /*LEDC PWM depth 11Bit */ - LEDC_TIMER_12_BIT = 12, /*LEDC PWM depth 12Bit */ - LEDC_TIMER_13_BIT = 13, /*LEDC PWM depth 13Bit */ - LEDC_TIMER_14_BIT = 14, /*LEDC PWM depth 14Bit */ - LEDC_TIMER_15_BIT = 15, /*LEDC PWM depth 15Bit */ + LEDC_TIMER_10_BIT = 10, /*!< LEDC PWM depth 10Bit */ + LEDC_TIMER_11_BIT = 11, /*!< LEDC PWM depth 11Bit */ + LEDC_TIMER_12_BIT = 12, /*!< LEDC PWM depth 12Bit */ + LEDC_TIMER_13_BIT = 13, /*!< LEDC PWM depth 13Bit */ + LEDC_TIMER_14_BIT = 14, /*!< LEDC PWM depth 14Bit */ + LEDC_TIMER_15_BIT = 15, /*!< LEDC PWM depth 15Bit */ } ledc_timer_bit_t; typedef struct { - int gpio_num; /*the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16*/ - ledc_mode_t speed_mode; /*LEDC speed speed_mode, high-speed mode or low-speed mode*/ - ledc_channel_t channel; /*LEDC channel(0 - 7)*/ - ledc_intr_type_t intr_type; /*configure interrupt, Fade interrupt enable or Fade interrupt disable*/ - ledc_timer_t timer_sel; /*Select the timer source of channel (0 - 3)*/ - uint32_t duty; /*LEDC channel duty, the duty range is [0, (2**bit_num) - 1], */ + int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16*/ + ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode*/ + ledc_channel_t channel; /*!< LEDC channel(0 - 7)*/ + ledc_intr_type_t intr_type; /*!< configure interrupt, Fade interrupt enable or Fade interrupt disable*/ + ledc_timer_t timer_sel; /*!< Select the timer source of channel (0 - 3)*/ + uint32_t duty; /*!< LEDC channel duty, the duty range is [0, (2**bit_num) - 1], */ } ledc_channel_config_t; typedef struct { - ledc_mode_t speed_mode; /*LEDC speed speed_mode, high-speed mode or low-speed mode*/ - ledc_timer_bit_t bit_num; /*LEDC channel duty depth*/ - ledc_timer_t timer_num; /*The timer source of channel (0 - 3)*/ - uint32_t freq_hz; /*LEDC timer frequency(Hz)*/ + ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode*/ + ledc_timer_bit_t bit_num; /*!< LEDC channel duty depth*/ + ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3)*/ + uint32_t freq_hz; /*!< LEDC timer frequency(Hz)*/ } ledc_timer_config_t; @@ -100,15 +100,10 @@ typedef struct { * * User this Function, configure LEDC channel with the given channel/output gpio_num/interrupt/source timer/frequency(Hz)/LEDC depth * - * @param[in] ledc_channel_config_t - * ledc_channel_config_t.speed_mode : LEDC speed speed_mode - * ledc_channel_config_t.gpio_num : LEDC output gpio_num, if you want to use gpio16, ledc_channel_config_t.gpio_num = 16 - * ledc_channel_config_t.channel : LEDC channel(0 - 7) - * ledc_channel_config_t.intr_type : configure interrupt, Fade interrupt enable or Fade interrupt disable - * ledc_channel_config_t.timer_sel : Select the timer source of channel (0 - 3), high speed channel must bind with high speed timer. - * ledc_channel_config_t.duty : LEDC channel duty, the duty range is [0, (2**timer_bit_num) - 1], - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error + * @param ledc_conf Pointer of LEDC channel configure struct + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error * */ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf); @@ -118,14 +113,13 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf); * * User this Function, configure LEDC timer with the given source timer/frequency(Hz)/bit_num * - * @param[in] ledc_timer_config_t - * ledc_timer_config_t.speed_mode : LEDC speed speed_mode - * ledc_timer_config_t.timer_num : Select the timer source of channel (0 - 3) - * ledc_timer_config_t.freq_hz : LEDC channel frequency(Hz), - * ledc_timer_config_t.bit_num : LEDC channel duty depth - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error - * ESP_FAIL: Can not find a proper pre-divider number base on the given frequency and the current bit_num. + * @param timer_conf Pointer of LEDC timer configure struct + * + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL Can not find a proper pre-divider number base on the given frequency and the current bit_num. * */ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); @@ -136,12 +130,13 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf); * Call this function to activate the LEDC updated parameters. * After ledc_set_duty, ledc_set_fade, we need to call this function to update the settings. * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] channel : LEDC channel(0-7), select from ledc_channel_t + * @param channel LEDC channel(0-7), select from ledc_channel_t * - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error * */ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); @@ -151,12 +146,13 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); * * Disable LEDC output, and set idle level * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] channel : LEDC channel(0-7), select from ledc_channel_t + * @param channel LEDC channel(0-7), select from ledc_channel_t * - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level); @@ -165,27 +161,29 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl * * Set LEDC frequency(Hz) * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] timer_num : LEDC timer index(0-3), select from ledc_timer_t + * @param timer_num LEDC timer index(0-3), select from ledc_timer_t * - * @param[in] freq_hz : set the LEDC frequency + * @param freq_hz Set the LEDC frequency * - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error - * ESP_FAIL: Can not find a proper pre-divider number base on the given frequency and the current bit_num. + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_FAIL Can not find a proper pre-divider number base on the given frequency and the current bit_num. */ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz); /** * @brief LEDC get channel frequency(Hz) * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] timer_num : LEDC timer index(0-3), select from ledc_timer_t + * @param timer_num LEDC timer index(0-3), select from ledc_timer_t * - * @return 0 : error - * others : current LEDC frequency + * @return + * - 0 error + * - Others Current LEDC frequency * */ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); @@ -195,27 +193,29 @@ uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num); * * Set LEDC duty, After the function calls the ledc_update_duty function, the function can take effect. * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] channel : LEDC channel(0-7), select from ledc_channel_t + * @param channel LEDC channel(0-7), select from ledc_channel_t * - * @param[in] duty : set the LEDC duty, the duty range is [0, (2**bit_num) - 1] + * @param duty Set the LEDC duty, the duty range is [0, (2**bit_num) - 1] * - * @return ESP_OK: success - * ESP_ERR_INVALID_ARG: parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty); /** * @brief LEDC get duty * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] channel : LEDC channel(0-7), select from ledc_channel_t + * @param channel LEDC channel(0-7), select from ledc_channel_t * * - * @return -1: parameter error - * other value: current LEDC duty + * @return + * - (-1) parameter error + * - Others Current LEDC duty * */ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel); @@ -225,22 +225,23 @@ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel); * * Set LEDC gradient, After the function calls the ledc_update_duty function, the function can take effect. * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] channel : LEDC channel(0-7), select from ledc_channel_t + * @param channel LEDC channel(0-7), select from ledc_channel_t * - * @param[in] duty : set the start of the gradient duty, the duty range is [0, (2**bit_num) - 1] + * @param duty Set the start of the gradient duty, the duty range is [0, (2**bit_num) - 1] * - * @param[in] gradule_direction : set the direction of the gradient + * @param gradule_direction Set the direction of the gradient * - * @param[in] step_num : set the number of the gradient + * @param step_num Set the number of the gradient * - * @param[in] duty_cyle_num : set how many LEDC tick each time the gradient lasts + * @param duty_cyle_num Set how many LEDC tick each time the gradient lasts * - * @param[in] duty_scale : set gradient change amplitude + * @param duty_scale Set gradient change amplitude * - * @return ESP_OK : success - * ESP_ERR_INVALID_ARG : parameter error + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error */ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t gradule_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale); @@ -248,34 +249,37 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, /** * @brief register LEDC interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * TODO: to move INUM options to menu_config - * @param[in] uint32_t ledc_intr_num : LEDC interrupt number, check the info in soc.h, and please see the core-isa.h for more details - * @param[in] void (* fn)(void* ) : interrupt handler function. - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * @param[in] void * arg : parameter for handler function + * @note + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. + * @param ledc_intr_num LEDC interrupt number, check the info in soc.h, and please see the core-isa.h for more details + * @param fn Interrupt handler function. + * @note + * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". + * @param arg Parameter for handler function * - * @return ESP_OK : success ; - * ESP_ERR_INVALID_ARG : function ptr error. + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Function pointer error. */ esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg); /** * @brief configure LEDC settings * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] timer_sel : timer index(0-3), there are 4 timers in LEDC module + * @param timer_sel Timer index(0-3), there are 4 timers in LEDC module * - * @param[in] div_num : timer clock divide number, the timer clock is divided from the selected clock source + * @param div_num Timer clock divide number, the timer clock is divided from the selected clock source * - * @param[in] bit_num : the count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) + * @param bit_num The count number of one period, counter range is 0 ~ ((2 ** bit_num) - 1) * - * @param[in] clk_src : select LEDC source clock. + * @param clk_src Select LEDC source clock. * - * @return -1: parameter error - * other value: current LEDC duty + * @return + * - (-1) Parameter error + * - Other Current LEDC duty * */ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src); @@ -283,13 +287,14 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_ /** * @brief reset LEDC timer * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] timer_sel : LEDC timer index(0-3), select from ledc_timer_t + * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * * - * @return ESP_ERR_INVALID_ARG: parameter error - * ESP_OK: success + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success * */ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); @@ -297,13 +302,14 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel); /** * @brief pause LEDC timer counter * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] timer_sel : LEDC timer index(0-3), select from ledc_timer_t + * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * * - * @return ESP_ERR_INVALID_ARG: parameter error - * ESP_OK: success + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success * */ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel); @@ -311,13 +317,14 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel); /** * @brief pause LEDC timer resume * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] timer_sel : LEDC timer index(0-3), select from ledc_timer_t + * @param timer_sel LEDC timer index(0-3), select from ledc_timer_t * * - * @return ESP_ERR_INVALID_ARG: parameter error - * ESP_OK: success + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success * */ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); @@ -325,15 +332,16 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); /** * @brief bind LEDC channel with the selected timer * - * @param[in] speed_mode : select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version + * @param speed_mode Select the LEDC speed_mode, high-speed mode and low-speed mode, now we only support high-speed mode. We will access low-speed mode in next version * - * @param[in] channel : LEDC channel index(0-7), select from ledc_channel_t + * @param channel LEDC channel index(0-7), select from ledc_channel_t * - * @param[in] timer_idx : LEDC timer index(0-3), select from ledc_timer_t + * @param timer_idx LEDC timer index(0-3), select from ledc_timer_t * * - * @return ESP_ERR_INVALID_ARG: parameter error - * ESP_OK: success + * @return + * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_OK Success * */ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); @@ -342,44 +350,56 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * * * ----------------EXAMPLE OF LEDC SETTING --------------------- - * //1. enable LEDC - * periph_module_enable(PERIPH_LEDC_MODULE); //enable LEDC module, or you can not set any register of it. + * @code{c} + * //1. enable LEDC + * //enable LEDC module, or you can not set any register of it. + * periph_module_enable(PERIPH_LEDC_MODULE); + * @endcode * - * //2. set LEDC timer - * ledc_timer_config_t timer_conf = { - * .bit_num = LEDC_TIMER_12_BIT, //set timer counter bit number - * .freq_hz = 1000, //set frequency of pwm, here, 1000Hz - * .speed_mode = LEDC_HIGH_SPEED_MODE //timer mode, - * .timer_num = LEDC_TIMER_0, //timer number - * }; - * ledc_timer_config(&timer_conf); //setup timer. + * @code{c} + * //2. set LEDC timer + * ledc_timer_config_t timer_conf = { + * .bit_num = LEDC_TIMER_12_BIT, //set timer counter bit number + * .freq_hz = 1000, //set frequency of pwm, here, 1000Hz + * .speed_mode = LEDC_HIGH_SPEED_MODE, //timer mode, + * .timer_num = LEDC_TIMER_0, //timer number + * }; + * ledc_timer_config(&timer_conf); //setup timer. + * @endcode * - * //3. set LEDC channel - * ledc_channel_config_t ledc_conf = { - * .channel = LEDC_CHANNEL_0; //set LEDC channel 0 - * .duty = 1000; //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) - * .gpio_num = 16; //GPIO number - * .intr_type = LEDC_INTR_FADE_END; //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. - * .speed_mode = LEDC_HIGH_SPEED_MODE; //set LEDC mode, from ledc_mode_t - * .timer_sel = LEDC_TIMER_0; //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same - * } - * ledc_channel_config(&ledc_conf); //setup the configuration + * @code{c} + * //3. set LEDC channel + * ledc_channel_config_t ledc_conf = { + * .channel = LEDC_CHANNEL_0; //set LEDC channel 0 + * .duty = 1000; //set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1) + * .gpio_num = 16; //GPIO number + * .intr_type = LEDC_INTR_FADE_END; //GPIO INTR TYPE, as an example, we enable fade_end interrupt here. + * .speed_mode = LEDC_HIGH_SPEED_MODE; //set LEDC mode, from ledc_mode_t + * .timer_sel = LEDC_TIMER_0; //set LEDC timer source, if different channel use one timer, the frequency and bit_num of these channels should be the same + * } + * ledc_channel_config(&ledc_conf); //setup the configuration * * ----------------EXAMPLE OF SETTING DUTY --- ----------------- - * uint32_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) - * uint32_t duty = 2000; //duty range is 0 ~ ((2**bit_num)-1) - * LEDC_set_duty(LEDC_HIGH_SPEED_MODE, ledc_channel, duty); //set speed mode, channel, and duty. - * ledc_update_duty(LEDC_HIGH_SPEED_MODE, ledc_channel); //after set duty, we need to call ledc_update_duty to update the settings. - * + * @code{c} + * uint32_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) + * uint32_t duty = 2000; //duty range is 0 ~ ((2**bit_num)-1) + * LEDC_set_duty(LEDC_HIGH_SPEED_MODE, ledc_channel, duty); //set speed mode, channel, and duty. + * ledc_update_duty(LEDC_HIGH_SPEED_MODE, ledc_channel); //after set duty, we need to call ledc_update_duty to update the settings. + * @endcode * * ----------------EXAMPLE OF LEDC INTERRUPT ------------------ - * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. - * ledc_isr_register(18, ledc_isr_handler, NULL); //hook the isr handler for LEDC interrupt - * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * //NOTE1:user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. - * //NOTE2:do not pick the INUM that already occupied by the system. - * //NOTE3:refer to soc.h to check which INUMs that can be used. + * @code{c} + * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. + * ledc_isr_register(18, ledc_isr_handler, NULL); //hook the isr handler for LEDC interrupt + * @endcode + * @note + * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. + * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. + * 3. do not pick the INUM that already occupied by the system. + * 4. refer to soc.h to check which INUMs that can be used. + * * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- + * @code{c} * #include "esp_attr.h" * void IRAM_ATTR ledc_isr_handler(void* arg) //we should add 'IRAM_ATTR' attribution when we declare the isr function * { @@ -391,7 +411,7 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * * LEDC.int_clr.val = intr_st; //clear LEDC interrupt status. * } - * + * @endcode * *--------------------------END OF EXAMPLE -------------------------- */ From f10cc7dc8eb09bcdd3075b7f8ef9303e3dc3da28 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 25 Oct 2016 14:55:35 +1100 Subject: [PATCH 103/285] bootloader: Refactor secure boot digest generation --- .../bootloader/src/main/bootloader_config.h | 4 +- .../bootloader/src/main/bootloader_start.c | 15 +- components/bootloader/src/main/secure_boot.c | 135 +++++++++++++----- 3 files changed, 114 insertions(+), 40 deletions(-) diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 8a837693c2..1655280dc7 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -36,7 +36,6 @@ extern "C" #define RTC_DATA_LOW 0x50000000 #define RTC_DATA_HIGH 0x50002000 - #define PART_TYPE_APP 0x00 #define PART_SUBTYPE_FACTORY 0x00 #define PART_SUBTYPE_OTA_FLAG 0x10 @@ -66,8 +65,7 @@ void boot_cache_redirect( uint32_t pos, size_t size ); uint32_t get_bin_len(uint32_t pos); bool flash_encrypt(bootloader_state_t *bs); -bool secure_boot(void); - +bool secure_boot_generate_bootloader_digest(void); #ifdef __cplusplus } diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 5b1e152070..a4b27702c4 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -256,6 +256,7 @@ void bootloader_main() bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; esp_ota_select_entry_t sa,sb; + memset(&bs, 0, sizeof(bs)); ESP_LOGI(TAG, "compile time " __TIME__ ); @@ -329,16 +330,20 @@ void bootloader_main() } ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); + if(fhdr.secure_boot_flag == 0x01) { - /* protect the 2nd_boot */ - if(false == secure_boot()){ - ESP_LOGE(TAG, "secure boot failed"); - return; + /* Generate secure digest from this bootloader to protect future + modifications */ + if (secure_boot_generate_bootloader_digest() == false){ + ESP_LOGE(TAG, "Bootloader digest generation failed. SECURE BOOT IS NOT ENABLED."); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ } } if(fhdr.encrypt_flag == 0x01) { - /* encrypt flash */ + /* encrypt flash */ if (false == flash_encrypt(&bs)) { ESP_LOGE(TAG, "flash encrypt failed"); return; diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 3dfdbd1412..9247f2cbd9 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -40,7 +40,7 @@ static const char* TAG = "secure_boot"; * * @inputs: bool */ -bool secure_boot_generate(uint32_t bin_len){ +static bool secure_boot_generate(uint32_t bin_len){ SpiFlashOpResult spiRet; uint16_t i; uint32_t buf[32]; @@ -87,41 +87,112 @@ bool secure_boot_generate(uint32_t bin_len){ return true; } +/* Burn values written to the efuse write registers */ +static inline void burn_efuses() +{ + REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ + REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ + REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ + REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ +} /** - * @function : secure_boot - * @description: protect boot code in flash + * @function : secure_boot_generate_bootloader_digest * - * @inputs: bool + * @description: Called if the secure boot flag is set on the + * bootloader image in flash. If secure boot is not yet enabled for + * bootloader, this will generate the secure boot digest and enable + * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. + * + * This function does not verify secure boot of the bootloader (the + * ROM bootloader does this.) + * + * @return true if secure boot is enabled (either was already enabled, + * or is freshly enabled as a result of calling this function.) */ -bool secure_boot(void){ - uint32_t bin_len = 0; - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) - { - ESP_LOGD(TAG, "already secure boot !"); - return true; - } else { - boot_cache_redirect( 0, 64*1024); - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if (bin_len == 0) { - ESP_LOGE(TAG, "boot len is error"); - return false; - } - if (false == secure_boot_generate(bin_len)){ - ESP_LOGE(TAG, "secure boot generate failed"); - return false; - } - } +bool secure_boot_generate_bootloader_digest(void) { + uint32_t bin_len = 0; + if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) + { + ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); + return true; + } - REG_SET_BIT(EFUSE_BLK0_WDATA6_REG, EFUSE_RD_ABS_DONE_0); - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - ESP_LOGW(TAG, "burn abstract_done_0"); - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ - ESP_LOGI(TAG, "read EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); - return true; + boot_cache_redirect( 0, 64*1024); + bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); + if (bin_len == 0) { + ESP_LOGE(TAG, "Invalid bootloader image length zero."); + return false; + } + if (bin_len > 0x100000) { + ESP_LOGE(TAG, "Invalid bootloader image length %x", bin_len); + return false; + } + uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG); + bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2; + bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2; + if (efuse_key_read_protected == false + && efuse_key_write_protected == false + && REG_READ(EFUSE_BLK2_RDATA0_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA1_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA2_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA3_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA4_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA5_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA6_REG) == 0 + && REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) { + ESP_LOGI(TAG, "Generating new secure boot key..."); + /* reuse the secure boot IV generation function to generate + the key, as this generator uses the hardware RNG. */ + uint32_t buf[32]; + ets_secure_boot_rd_iv(buf); + for (int i = 0; i < 8; i++) { + ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); + REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); + } + bzero(buf, sizeof(buf)); + burn_efuses(); + ESP_LOGI(TAG, "Read & write protecting new key..."); + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2); + burn_efuses(); + efuse_key_read_protected = true; + efuse_key_write_protected = true; + + } else { + ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2"); + } + + ESP_LOGI(TAG, "Generating secure boot digest..."); + if (false == secure_boot_generate(bin_len)){ + ESP_LOGE(TAG, "secure boot generation failed"); + return false; + } + ESP_LOGI(TAG, "Digest generation complete."); + + if (!efuse_key_read_protected) { + ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); + return false; + } + if (!efuse_key_write_protected) { + ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse."); + return false; + } + + ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); + ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); + REG_WRITE(EFUSE_BLK0_WDATA6_REG, + EFUSE_RD_ABS_DONE_0 | EFUSE_RD_DISABLE_JTAG); + burn_efuses(); + uint32_t after = REG_READ(EFUSE_BLK0_RDATA6_REG); + ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after); + if (after & EFUSE_RD_ABS_DONE_0) { + ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); + return true; + } else { + ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); + return false; + } } From cf732bb663a4c8690107e354d958042ef3ea5bfb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 31 Oct 2016 16:27:26 +1100 Subject: [PATCH 104/285] esp32: efuse_reg add bit values for read & write disable flags --- components/esp32/include/soc/efuse_reg.h | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/components/esp32/include/soc/efuse_reg.h b/components/esp32/include/soc/efuse_reg.h index a0f0a07da6..291e3984ee 100644 --- a/components/esp32/include/soc/efuse_reg.h +++ b/components/esp32/include/soc/efuse_reg.h @@ -29,6 +29,16 @@ #define EFUSE_RD_EFUSE_RD_DIS_M ((EFUSE_RD_EFUSE_RD_DIS_V)<<(EFUSE_RD_EFUSE_RD_DIS_S)) #define EFUSE_RD_EFUSE_RD_DIS_V 0xF #define EFUSE_RD_EFUSE_RD_DIS_S 16 + +/* Read disable bits for efuse blocks 1-3 */ +#define EFUSE_RD_DIS_BLK1 (1<<16) +#define EFUSE_RD_DIS_BLK2 (1<<17) +#define EFUSE_RD_DIS_BLK3 (1<<18) +/* Read disable FLASH_CRYPT_CONFIG, CODING_SCHEME & KEY_STATUS + in efuse block 0 +*/ +#define EFUSE_RD_DIS_BLK0_PARTIAL (1<<19) + /* EFUSE_RD_EFUSE_WR_DIS : RO ;bitpos:[15:0] ;default: 16'b0 ; */ /*description: read for efuse_wr_disable*/ #define EFUSE_RD_EFUSE_WR_DIS 0x0000FFFF @@ -36,6 +46,22 @@ #define EFUSE_RD_EFUSE_WR_DIS_V 0xFFFF #define EFUSE_RD_EFUSE_WR_DIS_S 0 +/* Write disable bits */ +#define EFUSE_WR_DIS_RD_DIS (1<<0) /*< disable writing read disable reg */ +#define EFUSE_WR_DIS_WR_DIS (1<<1) /*< disable writing write disable reg */ +#define EFUSE_WR_DIS_FLASH_CRYPT_CNT (1<<2) +#define EFUSE_WR_DIS_MAC_SPI_CONFIG_HD (1<<3) /*< disable writing MAC & SPI config hd efuses */ +#define EFUSE_WR_DIS_XPD_SDIO (1<<5) /*< disable writing SDIO config efuses */ +#define EFUSE_WR_DIS_SPI_PAD_CONFIG (1<<6) /*< disable writing SPI_PAD_CONFIG efuses */ +#define EFUSE_WR_DIS_BLK1 (1<<7) /*< disable writing BLK1 efuses */ +#define EFUSE_WR_DIS_BLK2 (1<<8) /*< disable writing BLK2 efuses */ +#define EFUSE_WR_DIS_BLK3 (1<<9) /*< disable writing BLK3 efuses */ +#define EFUSE_WR_DIS_FLASH_CRYPT_CODING_SCHEME (1<<10) /*< disable writing FLASH_CRYPT_CONFIG and CODING_SCHEME efuses */ +#define EFUSE_WR_DIS_ABS_DONE_0 (1<<12) /*< disable writing ABS_DONE_0 efuse */ +#define EFUSE_WR_DIS_ABS_DONE_1 (1<<13) /*< disable writing ABS_DONE_1 efuse */ +#define EFUSE_WR_DIS_JTAG_DISABLE (1<<14) /*< disable writing JTAG_DISABLE efuse */ +#define EFUSE_WR_DIS_CONSOLE_DL_DISABLE (1<<15) /*< disable writing CONSOLE_DEBUG_DISABLE, DISABLE_DL_ENCRYPT, DISABLE_DL_DECRYPT and DISABLE_DL_CACHE efuses */ + #define EFUSE_BLK0_RDATA1_REG (DR_REG_EFUSE_BASE + 0x004) /* EFUSE_RD_WIFI_MAC_CRC_LOW : RO ;bitpos:[31:0] ;default: 32'b0 ; */ /*description: read for low 32bit WIFI_MAC_Address*/ From 4ba1b73eba78893d2117a94a6e65c6ad63e6705a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Nov 2016 10:50:16 +1100 Subject: [PATCH 105/285] build system: Add espefuse/espsecure support for secure boot --- components/bootloader/Kconfig.projbuild | 67 ++++++++++++++++++-- components/bootloader/Makefile.projbuild | 57 ++++++++++++++++- components/bootloader/src/main/secure_boot.c | 2 + components/esptool_py/Makefile.projbuild | 17 ++++- components/esptool_py/esptool | 2 +- make/project.mk | 23 +++++-- 6 files changed, 147 insertions(+), 21 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 394bcc1bd1..d6736b33db 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -20,12 +20,65 @@ config LOG_BOOTLOADER_LEVEL_VERBOSE endchoice config LOG_BOOTLOADER_LEVEL - int - default 0 if LOG_BOOTLOADER_LEVEL_NONE - default 1 if LOG_BOOTLOADER_LEVEL_ERROR - default 2 if LOG_BOOTLOADER_LEVEL_WARN - default 3 if LOG_BOOTLOADER_LEVEL_INFO - default 4 if LOG_BOOTLOADER_LEVEL_DEBUG - default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE + int + default 0 if LOG_BOOTLOADER_LEVEL_NONE + default 1 if LOG_BOOTLOADER_LEVEL_ERROR + default 2 if LOG_BOOTLOADER_LEVEL_WARN + default 3 if LOG_BOOTLOADER_LEVEL_INFO + default 4 if LOG_BOOTLOADER_LEVEL_DEBUG + default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE + +choice SECURE_BOOTLOADER + bool "Secure bootloader" + default SECURE_BOOTLOADER_DISABLED + help + Build a bootloader with the secure boot flag enabled. + + Secure bootloader can be one-time-flash (chip will only ever + boot that particular bootloader), or a digest key can be used + to allow the secure bootloader to be re-flashed with + modifications. Secure boot also permanently disables JTAG. + + See docs/security/secure-boot.rst for details. + +config SECURE_BOOTLOADER_DISABLED + bool "Disabled" + +config SECURE_BOOTLOADER_ONE_TIME_FLASH + bool "One-time flash" + help + On first boot, the bootloader will generate a key which is not readable externally or by software. A digest is generated from the bootloader image itself. This digest will be verified on each subsequent boot. + + Enabling this option means that the bootloader cannot be changed after the first time it is booted. + +config SECURE_BOOTLOADER_REFLASHABLE + bool "Reflashable" + help + Generate the bootloader digest key on the computer instead of inside + the chip. Allows the secure bootloader to be re-flashed by using the + same key. + + This option is less secure than one-time flash, because a leak of the digest key allows reflashing of any device that uses it. + +endchoice + +config SECURE_BOOTLOADER_KEY_FILE + string "Secure bootloader digest key file" + depends on SECURE_BOOTLOADER_REFLASHABLE + default secure_boot_key.bin + help + Path to the key file for a reflashable secure boot digest. + File must contain 32 randomly generated bytes. + + Path is evaluated relative to the project directory. + + You can generate a new key by running the following command: + espsecure.py generate_key secure_boot_key.bin + + See docs/security/secure-boot.rst for details. + +config SECURE_BOOTLOADER_ENABLED + bool + default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE endmenu diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 50c95f9fec..8edabdb6ef 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,6 +15,8 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig +SECURE_BOOT_KEYFILE=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) + # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ @@ -31,18 +33,67 @@ bootloader-clean: clean: bootloader-clean +ifdef CONFIG_SECURE_BOOTLOADER_DISABLED +# If secure boot disabled, bootloader flashing is integrated +# with 'make flash' and no warnings are printed. + bootloader: $(BOOTLOADER_BIN) + @echo "$(SEPARATOR)" @echo "Bootloader built. Default flash command is:" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" -all_binaries: $(BOOTLOADER_BIN) - ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) -# bootloader-flash calls flash in the bootloader dummy project bootloader-flash: $(BOOTLOADER_BIN) $(BOOTLOADER_MAKE) flash +else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH +# One time flashing requires user to run esptool.py command themselves, +# and warning is printed about inability to reflash. + +bootloader: $(BOOTLOADER_BIN) + @echo $(SEPARATOR) + @echo "Bootloader built. One-time flash command is:" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo $(SEPARATOR) + @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" + +else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE +# Reflashable secure bootloader (recommended for testing only) +# generates a digest binary (bootloader + digest) and prints +# instructions for reflashing. User must run commands manually. + +BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin + +bootloader: $(BOOTLOADER_DIGEST_BIN) + @echo $(SEPARATOR) + @echo "Bootloader built and secure digest generated. First time flash command is:" + @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOT_KEYFILE)" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo $(SEPARATOR) + @echo "To reflash the bootloader after initial flash:" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)" + @echo $(SEPARATOR) + @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." + @echo "* NOT RECOMMENDED TO RE-FLASH the same bootloader-reflash-digest.bin to multiple production devices" + +$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) + @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" + $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOT_KEYFILE) -o $@ $< + +$(SECURE_BOOT_KEYFILE): + @echo $(SEPARATOR) + @echo "Need to generate secure boot digest key first. Run following command:" + @echo "$(ESPSECUREPY) generate_key $@" + @echo "Keep key file safe after generating." + @exit 1 + +else +$(error Bad sdkconfig - one of CONFIG_SECURE_BOOTLOADER_DISABLED, CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH, CONFIG_SECURE_BOOTLOADER_REFLASHABLE must be set) +endif + +all_binaries: $(BOOTLOADER_BIN) + # synchronise the project level config to the bootloader's # config $(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR) diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 9247f2cbd9..070fbf24d3 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -148,7 +148,9 @@ bool secure_boot_generate_bootloader_digest(void) { /* reuse the secure boot IV generation function to generate the key, as this generator uses the hardware RNG. */ uint32_t buf[32]; + ets_secure_boot_start(); ets_secure_boot_rd_iv(buf); + ets_secure_boot_finish(); for (int i = 0; i < 8; i++) { ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 69c01e1e7f..dfb9dfc4c2 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -11,23 +11,34 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) # two commands that can be used from other components # to invoke esptool.py (with or without serial port args) # -# NB: esptool.py lives in the sdk/bin directory not the component directory ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +# Supporting esptool command line tools +ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py +ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py + ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) -# the no-stub argument is temporary until esptool.py fully supports compressed uploads +ESPTOOL_ELF2IMAGE_OPTIONS := + +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ESPTOOL_ELF2IMAGE_OPTIONS += "--set-secure-boot-flag" +endif + ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< + $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" +endif $(Q) $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 5c6962e894..95dae1651e 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 5c6962e894e0a118c9a4b5760876433493449260 +Subproject commit 95dae1651e5aea1adb2b6018b23f65a305f67387 diff --git a/make/project.mk b/make/project.mk index 67e2e92bdb..17fb83e0d8 100644 --- a/make/project.mk +++ b/make/project.mk @@ -11,13 +11,13 @@ # .PHONY: build-components menuconfig defconfig all build clean all_binaries -all: all_binaries # other components will add dependencies to 'all_binaries' - @echo "To flash all build output, run 'make flash' or:" - @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) - -# (the reason all_binaries is used instead of 'all' is so that the flash target -# can build everything without triggering the per-component "to flash..." -# output targets.) +all: all_binaries +# see below for recipe of 'all' target +# +# # other components will add dependencies to 'all_binaries'. The +# reason all_binaries is used instead of 'all' is so that the flash +# target can build everything without triggering the per-component "to +# flash..." output targets.) help: @echo "Welcome to Espressif IDF build system. Some useful make targets:" @@ -135,6 +135,15 @@ export PROJECT_PATH #Include functionality common to both project & component -include $(IDF_PATH)/make/common.mk +all: +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" + @echo "To flash app & partition table, run 'make flash' or:" +else + @echo "To flash all build output, run 'make flash' or:" +endif + @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) + # Set default LDFLAGS LDFLAGS ?= -nostdlib \ From 04beb8baba3c087622558851285ccd038f8b59d0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Nov 2016 17:41:27 +1100 Subject: [PATCH 106/285] Add documentation for bootloader secure boot stage --- components/bootloader/Kconfig.projbuild | 4 +- components/bootloader/Makefile.projbuild | 2 +- docs/security/secure-boot.rst | 146 +++++++++++++++++++++++ 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 docs/security/secure-boot.rst diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index d6736b33db..55c2eebd14 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -63,11 +63,11 @@ config SECURE_BOOTLOADER_REFLASHABLE endchoice config SECURE_BOOTLOADER_KEY_FILE - string "Secure bootloader digest key file" + string "Secure bootloader key file" depends on SECURE_BOOTLOADER_REFLASHABLE default secure_boot_key.bin help - Path to the key file for a reflashable secure boot digest. + Path to the key file for a reflashable secure bootloader digest. File must contain 32 randomly generated bytes. Path is evaluated relative to the project directory. diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 8edabdb6ef..b9dab3e093 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -75,7 +75,7 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)" @echo $(SEPARATOR) @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." - @echo "* NOT RECOMMENDED TO RE-FLASH the same bootloader-reflash-digest.bin to multiple production devices" + @echo "* Not recommended to re-use the same secure boot keyfile on multiple production devices." $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst new file mode 100644 index 0000000000..5e33367b49 --- /dev/null +++ b/docs/security/secure-boot.rst @@ -0,0 +1,146 @@ +Secure Boot +=========== + +Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset. + +Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However for maximum protection we recommend using both features together. + +Background +---------- + +- Most data is stored in flash. Flash access does not need to protected for secure boot to function, because critical data is stored in Efuses internal to the chip. + +- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. + +- To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. + +Secure Boot Process Overview +---------------------------- + +This is a high level overview of the secure boot process. Step by step instructions are supplied under `How To Enable Secure Boot`. Further in-depth details are supplied under `Technical Details`: + +1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". + +2. Bootloader Config includes the path to a ECDSA public key. This "image public key" is compiled into the software bootloader. + +2. The software bootloader image is built by esp-idf with the public key embedded, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. + +3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: + - Hardware secure boot support generates a device secure bootloader key (stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - The secure digest is flashed at offset 0x0 in the flash. + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot this bootloader image.) + - Bootloader also disables JTAG via efuse. + +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. + +5. When running in secure boot mode, the software bootloader uses the image public key (embedded in the bootloader itself) to verify all subsequent partition tables and app images before they are booted. + +Keys +---- + +The following keys are used by the secure boot process: + +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from hardware, the user does not need to supply it. The Efuse holding this key must be read & write protected (preventing software access) before secure boot is enabled. + +- "image public key" is a ECDSA public key (curve TBD) in format TBD. This public key is flashed into the software bootloader and used to verify the remaining parts of the flash (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. + +- "image private key" is a ECDSA private key, a matching pair with "image public key". This private key is used to sign partition tables and app images for the secure boot process. **This private key must be securely kept private** as anyone who has this key can authenticate to the secure boot process. It is acceptable to use the same private key across multiple production devices. + +How To Enable Secure Boot +------------------------- + +1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (For the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) + +2. Select names for public & private image key files "Image Public Key File" and "Image Private Key File". These options will appear after secure boot is enabled. The files can be anywhere on your system. Relative paths are evaluated from the project directory. The files don't need to exist yet. + +3. Set other config options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration + +4. Run ``make generate_image_keypair`` to generate an image public key and a matching image private key. + + **IMPORTANT** The key pair is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + +5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of `make` will include a prompt for a flashing command, using `esptool.py write_flash`. + +6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not automated) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. + +7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the private key you generated in step 4. + + *NOTE*: `make flash` doesn't flash the bootloader if secure boot is enabled. + +8. Reset the ESP32 and it will boot the software bootloader you flashed. The software bootloader will enable secure boot on the chip, and then it verifies the app image signature and boots the app. You should watch the serial console output from the ESP32 to verify that secure boot is enabled and no errors have occured due to the build configuration. + +**IMPORTANT** Secure boot won't ever be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. + +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the image public key). + +Re-Flashable Software Bootloader +-------------------------------- + +The "Secure Boot: One-Time Flash" is the recommended software bootloader configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. + +However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. + +*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. + +1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable". + +2. Select a name for the "Secure bootloader key file". The file can be anywhere on your system, and does not have to exist yet. A path is evaluated relative to the project directory. The file doesn't have to exist yet. + +3. The first time you run ``make bootloader``, the system will prompt you with a ``espsecure.py generate_key`` command that can be used to generate the secure bootloader key. + + **IMPORTANT** The new key is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the secure bootloader key will be weak. + +4. Run ``make bootloader`` again. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the secure bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the secure bootloader key file). + +5. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. + + +Technical Details +----------------- + +The following sections contain low-level descriptions of various technical functions: + +Hardware Secure Boot Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Secure Boot support hardware can perform three basic operations: + +1. Generate a random sequence of bytes from a hardware random number generator. + +2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read from hardware if Efuse ABS_DONE_0 is *not* burned (ie still 0), to prevent new digests from being calculated on the device after secure boot is enabled. + +3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. + +Secure Bootloader Digest Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Starting with an "image" of binary data as input, this algorithm generates a digest as output. + +For a Python version of this algorithm, see the `espsecure.py` tool in the components/esptool_py directory. + +Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions. + +1. Prefix the image with a 128 byte randomly generated IV. +2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) +3. For each 16 byte block of the input image: + - Reverse the byte order of the block (^) + - Use the AES256 algorithm in ECB mode to encrypt the block. + - Reverse the byte order of the 16 bytes of ciphertext output. (^) + - Append to the overall ciphertext output. +4. Byte-swap each 4 byte word of the ciphertext (^) +5. Calculate SHA-512 of the ciphertext. + +Output digest is 192 bytes of data: The 128 byte IV, followed by the 64 byte SHA-512 digest. + +Image Signing Algorithm +~~~~~~~~~~~~~~~~~~~~~~~ + +Deterministic ECDSA as specified by `RFC6979`. + +Curve is TBD. +Key format is TBD. +Output format is TBD. + + +.. _esp-idf boot process: ../boot-process.rst +.. _RFC6979: https://tools.ietf.org/html/rfc6979 From d0fac3c39566d819022b569f2f2f26b3552c2166 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 2 Nov 2016 10:26:02 +0800 Subject: [PATCH 107/285] Language tweaking --- docs/openocd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/openocd.rst b/docs/openocd.rst index cf1d25e60b..9e2aeff459 100644 --- a/docs/openocd.rst +++ b/docs/openocd.rst @@ -1,7 +1,7 @@ OpenOCD setup for ESP32 ----------------------- -The ESP31 and ESP32 have two powerful Xtensa cores, allowing for a great deal of variety of program architectures. The FreeRTOS +The ESP31 and ESP32 have two powerful Xtensa cores, allowing for a great variety of program architectures. The FreeRTOS OS that comes with ESP-IDF is capable of multi-core pre-emptive multithreading, allowing for an intuitive way of writing software. The downside of the ease of programming is that debugging without the right tools is harder: figuring out a bug that is caused From aceb6517c05e807c7f9ad27d31e9b8aa12568172 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Nov 2016 10:41:58 +1100 Subject: [PATCH 108/285] Refactor existing bootloader common functionality into bootloader_support component --- components/bootloader/Makefile.projbuild | 6 +- components/bootloader/src/Makefile | 2 +- .../bootloader/src/main/bootloader_config.h | 5 - .../bootloader/src/main/bootloader_start.c | 276 ++++++++---------- .../bootloader/src/main/flash_encrypt.c | 158 +++++----- components/bootloader/src/main/secure_boot.c | 72 +++-- components/bootloader_support/README.rst | 9 + components/bootloader_support/component.mk | 11 + .../include/esp_image_format.h | 133 +++++++++ .../include/esp_secureboot.h | 51 ++++ .../include_priv/bootloader_flash.h | 69 +++++ .../bootloader_support/src/bootloader_flash.c | 122 ++++++++ .../bootloader_support/src/esp_image_format.c | 155 ++++++++++ .../bootloader_support/src/secureboot.c | 7 + .../esp32/include/esp_flash_data_types.h | 48 --- components/esp32/include/rom/secure_boot.h | 2 +- components/log/include/esp_log.h | 4 + make/project.mk | 2 +- 18 files changed, 813 insertions(+), 319 deletions(-) create mode 100644 components/bootloader_support/README.rst create mode 100755 components/bootloader_support/component.mk create mode 100644 components/bootloader_support/include/esp_image_format.h create mode 100644 components/bootloader_support/include/esp_secureboot.h create mode 100644 components/bootloader_support/include_priv/bootloader_flash.h create mode 100644 components/bootloader_support/src/bootloader_flash.c create mode 100644 components/bootloader_support/src/esp_image_format.c create mode 100644 components/bootloader_support/src/secureboot.c diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index b9dab3e093..3799e913c8 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -20,7 +20,7 @@ SECURE_BOOT_KEYFILE=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ - BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ + BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) IS_BOOTLOADER_BUILD=1 .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) @@ -89,7 +89,9 @@ $(SECURE_BOOT_KEYFILE): @exit 1 else -$(error Bad sdkconfig - one of CONFIG_SECURE_BOOTLOADER_DISABLED, CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH, CONFIG_SECURE_BOOTLOADER_REFLASHABLE must be set) +bootloader: + @echo "Invalid bootloader target: bad sdkconfig?" + @exit 1 endif all_binaries: $(BOOTLOADER_BIN) diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index add9c15d61..278e0e9afb 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader log spi_flash +COMPONENTS := esptool_py bootloader bootloader_support log spi_flash # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 1655280dc7..0823581824 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -25,8 +25,6 @@ extern "C" #define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 -#define MEM_CACHE(offset) (uint8_t *)(0x3f400000 + (offset)) -#define CACHE_READ_32(offset) ((uint32_t *)(0x3f400000 + (offset))) #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 #define DROM_LOW 0x3F400000 @@ -61,9 +59,6 @@ typedef struct { uint32_t selected_subtype; } bootloader_state_t; -void boot_cache_redirect( uint32_t pos, size_t size ); -uint32_t get_bin_len(uint32_t pos); - bool flash_encrypt(bootloader_state_t *bs); bool secure_boot_generate_bootloader_digest(void); diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index a4b27702c4..3bc2696a4b 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -33,6 +33,8 @@ #include "soc/timer_group_reg.h" #include "sdkconfig.h" +#include "esp_image_format.h" +#include "bootloader_flash.h" #include "bootloader_config.h" @@ -49,7 +51,7 @@ flash cache is down and the app CPU is in reset. We do have a stack, so we can d extern void Cache_Flush(int); void bootloader_main(); -void unpack_load_app(const esp_partition_pos_t *app_node); +static void unpack_load_app(const esp_partition_pos_t *app_node); void print_flash_info(const esp_image_header_t* pfhdr); void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, @@ -94,53 +96,6 @@ void IRAM_ATTR call_start_cpu0() bootloader_main(); } -/** - * @function : get_bin_len - * @description: get bin's length - * - * @inputs: pos bin locate address in flash - * @return: uint32 length of bin,if bin MAGIC error return 0 - */ - -uint32_t get_bin_len(uint32_t pos) -{ - uint32_t len = 8 + 16; - uint8_t i; - ESP_LOGD(TAG, "pos %d %x",pos,*(uint8_t *)pos); - if(0xE9 != *(uint8_t *)pos) { - return 0; - } - for (i = 0; i < *(uint8_t *)(pos + 1); i++) { - len += *(uint32_t *)(pos + len + 4) + 8; - } - if (len % 16 != 0) { - len = (len / 16 + 1) * 16; - } else { - len += 16; - } - ESP_LOGD(TAG, "bin length = %d", len); - return len; -} - -/** - * @function : boot_cache_redirect - * @description: Configure several pages in flash map so that `size` bytes - * starting at `pos` are mapped to 0x3f400000. - * This sets up mapping only for PRO CPU. - * - * @inputs: pos address in flash - * size size of the area to map, in bytes - */ -void boot_cache_redirect( uint32_t pos, size_t size ) -{ - uint32_t pos_aligned = pos & 0xffff0000; - uint32_t count = (size + 0xffff) / 0x10000; - Cache_Read_Disable( 0 ); - Cache_Flush( 0 ); - ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", pos_aligned, count ); - cache_flash_mmu_set( 0, 0, 0x3f400000, pos_aligned, 64, count ); - Cache_Read_Enable( 0 ); -} /** * @function : load_partition_table @@ -154,79 +109,86 @@ void boot_cache_redirect( uint32_t pos, size_t size ) */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { - esp_partition_info_t partition; - uint32_t end = addr + 0x1000; - int index = 0; + const esp_partition_info_t *partitions; + const int PARTITION_TABLE_SIZE = 0x1000; + const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); char *partition_usage; ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); - while (addr < end) { - ESP_LOGD(TAG, "load partition table entry from %x(%08x)", addr, MEM_CACHE(addr)); - memcpy(&partition, MEM_CACHE(addr), sizeof(partition)); - ESP_LOGD(TAG, "type=%x subtype=%x", partition.type, partition.subtype); + partitions = bootloader_mmap(addr, 0x1000); + if (!partitions) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); + return false; + } + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", addr, (intptr_t)partitions); + + for(int i = 0; i < MAX_PARTITIONS; i++) { + const esp_partition_info_t *partition = &partitions[i]; + ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); + ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); partition_usage = "unknown"; - if (partition.magic == ESP_PARTITION_MAGIC) { /* valid partition definition */ - switch(partition.type) { - case PART_TYPE_APP: /* app partition */ - switch(partition.subtype) { - case PART_SUBTYPE_FACTORY: /* factory binary */ - bs->factory = partition.pos; - partition_usage = "factory app"; - break; - case PART_SUBTYPE_TEST: /* test binary */ - bs->test = partition.pos; - partition_usage = "test app"; - break; - default: - /* OTA binary */ - if ((partition.subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { - bs->ota[partition.subtype & PART_SUBTYPE_OTA_MASK] = partition.pos; - ++bs->app_count; - partition_usage = "OTA app"; - } - else { - partition_usage = "Unknown app"; - } - break; + if (partition->magic != ESP_PARTITION_MAGIC) { + /* invalid partition definition indicates end-of-table */ + break; + } + + /* valid partition table */ + switch(partition->type) { + case PART_TYPE_APP: /* app partition */ + switch(partition->subtype) { + case PART_SUBTYPE_FACTORY: /* factory binary */ + bs->factory = partition->pos; + partition_usage = "factory app"; + break; + case PART_SUBTYPE_TEST: /* test binary */ + bs->test = partition->pos; + partition_usage = "test app"; + break; + default: + /* OTA binary */ + if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { + bs->ota[partition->subtype & PART_SUBTYPE_OTA_MASK] = partition->pos; + ++bs->app_count; + partition_usage = "OTA app"; } - break; /* PART_TYPE_APP */ - case PART_TYPE_DATA: /* data partition */ - switch(partition.subtype) { - case PART_SUBTYPE_DATA_OTA: /* ota data */ - bs->ota_info = partition.pos; - partition_usage = "OTA data"; - break; - case PART_SUBTYPE_DATA_RF: - partition_usage = "RF data"; - break; - case PART_SUBTYPE_DATA_WIFI: - partition_usage = "WiFi data"; - break; - default: - partition_usage = "Unknown data"; - break; + else { + partition_usage = "Unknown app"; } - break; /* PARTITION_USAGE_DATA */ - default: /* other partition type */ break; } - } - /* invalid partition magic number */ - else { - break; /* todo: validate md5 */ + break; /* PART_TYPE_APP */ + case PART_TYPE_DATA: /* data partition */ + switch(partition->subtype) { + case PART_SUBTYPE_DATA_OTA: /* ota data */ + bs->ota_info = partition->pos; + partition_usage = "OTA data"; + break; + case PART_SUBTYPE_DATA_RF: + partition_usage = "RF data"; + break; + case PART_SUBTYPE_DATA_WIFI: + partition_usage = "WiFi data"; + break; + default: + partition_usage = "Unknown data"; + break; + } + break; /* PARTITION_USAGE_DATA */ + default: /* other partition type */ + break; } /* print partition type info */ - ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", index, partition.label, partition_usage, - partition.type, partition.subtype, - partition.pos.offset, partition.pos.size); - index++; - addr += sizeof(partition); + ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", i, partition->label, partition_usage, + partition->type, partition->subtype, + partition->pos.offset, partition->pos.size); } + bootloader_unmap(partitions); + ESP_LOGI(TAG,"End of partition table"); return true; } @@ -254,8 +216,9 @@ void bootloader_main() esp_image_header_t fhdr; bootloader_state_t bs; - SpiFlashOpResult spiRet1,spiRet2; + SpiFlashOpResult spiRet1,spiRet2; esp_ota_select_entry_t sa,sb; + const esp_ota_select_entry_t *ota_select_map; memset(&bs, 0, sizeof(bs)); @@ -264,10 +227,11 @@ void bootloader_main() REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN ); REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); - /*register first sector in drom0 page 0 */ - boot_cache_redirect( 0, 0x5000 ); - memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(esp_image_header_t) ); + if(esp_image_load_header(0x1000, &fhdr) != ESP_OK) { + ESP_LOGE(TAG, "failed to load bootloader header!"); + return; + } print_flash_info(&fhdr); @@ -282,9 +246,19 @@ void bootloader_main() if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition //ESP_LOGE("OTA info sector handling is not implemented"); - boot_cache_redirect(bs.ota_info.offset, bs.ota_info.size ); - memcpy(&sa,MEM_CACHE(bs.ota_info.offset & 0x0000ffff),sizeof(sa)); - memcpy(&sb,MEM_CACHE((bs.ota_info.offset + 0x1000)&0x0000ffff) ,sizeof(sb)); + if (bs.ota_info.size < 2 * sizeof(esp_ota_select_entry_t)) { + ESP_LOGE(TAG, "ERROR: ota_info partition size %d is too small (minimum %d bytes)", bs.ota_info.size, sizeof(esp_ota_select_entry_t)); + return; + } + ota_select_map = bootloader_mmap(bs.ota_info.offset, bs.ota_info.size); + if (!ota_select_map) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs.ota_info.offset, bs.ota_info.size); + return; + } + sa = ota_select_map[0]; + sb = ota_select_map[1]; + bootloader_unmap(ota_select_map); + if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { // init status flash load_part_pos = bs.ota[0]; @@ -355,14 +329,14 @@ void bootloader_main() } -void unpack_load_app(const esp_partition_pos_t* partition) +static void unpack_load_app(const esp_partition_pos_t* partition) { - boot_cache_redirect(partition->offset, partition->size); - - uint32_t pos = 0; esp_image_header_t image_header; - memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header)); - pos += sizeof(image_header); + + if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { + ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); + return; + } uint32_t drom_addr = 0; uint32_t drom_load_addr = 0; @@ -376,19 +350,22 @@ void unpack_load_app(const esp_partition_pos_t* partition) bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic, - image_header.blocks, + image_header.segment_count, image_header.spi_mode, image_header.spi_size, (unsigned)image_header.entry_addr); - for (uint32_t section_index = 0; - section_index < image_header.blocks; - ++section_index) { - esp_image_section_header_t section_header = {0}; - memcpy(§ion_header, MEM_CACHE(pos), sizeof(section_header)); - pos += sizeof(section_header); + for (int segment = 0; segment < image_header.segment_count; segment++) { + esp_image_segment_header_t segment_header; + uint32_t data_offs; + if(esp_image_load_segment_header(segment, partition->offset, + &image_header, &segment_header, + &data_offs) != ESP_OK) { + ESP_LOGE(TAG, "failed to load segment header #%d", segment); + return; + } - const uint32_t address = section_header.load_addr; + const uint32_t address = segment_header.load_addr; bool load = true; bool map = false; if (address == 0x00000000) { // padding, ignore block @@ -400,47 +377,50 @@ void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= DROM_LOW && address < DROM_HIGH) { - ESP_LOGD(TAG, "found drom section, map from %08x to %08x", pos, - section_header.load_addr); - drom_addr = partition->offset + pos - sizeof(section_header); - drom_load_addr = section_header.load_addr; - drom_size = section_header.data_len + sizeof(section_header); + ESP_LOGD(TAG, "found drom section, map from %08x to %08x", data_offs, + segment_header.load_addr); + drom_addr = data_offs; + drom_load_addr = segment_header.load_addr; + drom_size = segment_header.data_len + sizeof(segment_header); load = false; map = true; } if (address >= IROM_LOW && address < IROM_HIGH) { - ESP_LOGD(TAG, "found irom section, map from %08x to %08x", pos, - section_header.load_addr); - irom_addr = partition->offset + pos - sizeof(section_header); - irom_load_addr = section_header.load_addr; - irom_size = section_header.data_len + sizeof(section_header); + ESP_LOGD(TAG, "found irom section, map from %08x to %08x", data_offs, + segment_header.load_addr); + irom_addr = data_offs; + irom_load_addr = segment_header.load_addr; + irom_size = segment_header.data_len + sizeof(segment_header); load = false; map = true; } if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", pos); + ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", data_offs); load = false; } if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) { - ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", pos); + ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", data_offs); load = false; } - ESP_LOGI(TAG, "section %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", section_index, pos, - section_header.load_addr, section_header.data_len, section_header.data_len, (load)?"load":(map)?"map":""); + ESP_LOGI(TAG, "segment %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", segment, data_offs - sizeof(esp_image_segment_header_t), + segment_header.load_addr, segment_header.data_len, segment_header.data_len, (load)?"load":(map)?"map":""); - if (!load) { - pos += section_header.data_len; - continue; + if (load) { + const void *data = bootloader_mmap(data_offs, segment_header.data_len); + if(!data) { + ESP_LOGE(TAG, "bootloader_mmap(0x%xc, 0x%x) failed", + data_offs, segment_header.data_len); + return; + } + memcpy((void *)segment_header.load_addr, data, segment_header.data_len); + bootloader_unmap(data); } - - memcpy((void*) section_header.load_addr, MEM_CACHE(pos), section_header.data_len); - pos += section_header.data_len; } - + set_cache_and_start_app(drom_addr, drom_load_addr, drom_size, @@ -526,7 +506,7 @@ void print_flash_info(const esp_image_header_t* phdr) #if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) ESP_LOGD(TAG, "magic %02x", phdr->magic ); - ESP_LOGD(TAG, "blocks %02x", phdr->blocks ); + ESP_LOGD(TAG, "segments %02x", phdr->segment_count ); ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); diff --git a/components/bootloader/src/main/flash_encrypt.c b/components/bootloader/src/main/flash_encrypt.c index 2fb57a987d..2b1479097d 100644 --- a/components/bootloader/src/main/flash_encrypt.c +++ b/components/bootloader/src/main/flash_encrypt.c @@ -17,6 +17,7 @@ #include "esp_types.h" #include "esp_attr.h" #include "esp_log.h" +#include "esp_err.h" #include "rom/cache.h" #include "rom/ets_sys.h" @@ -30,6 +31,7 @@ #include "sdkconfig.h" #include "bootloader_config.h" +#include "esp_image_format.h" static const char* TAG = "flash_encrypt"; @@ -90,103 +92,97 @@ bool flash_encrypt_write(uint32_t pos, uint32_t len) Cache_Read_Enable(0); return true; } + + /** * @function : flash_encrypt * @description: encrypt 2nd boot ,partition table ,factory bin ��test bin (if use)��ota bin * ��OTA info sector. * * @inputs: bs bootloader state structure used to save the data - * + * * @return: return true, if the encrypt flash success - * + * */ bool flash_encrypt(bootloader_state_t *bs) { - uint32_t bin_len = 0; - uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT); - uint8_t count = bitcount(flash_crypt_cnt); - int i = 0; - ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count); + esp_err_t err; + uint32_t image_len = 0; + uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT); + uint8_t count = bitcount(flash_crypt_cnt); + ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count); - if ((count % 2) == 0) { - boot_cache_redirect( 0, 64*1024); - /* encrypt iv and abstruct */ - if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt iv and abstract error"); - return false; - } + if ((count % 2) == 0) { + /* encrypt iv and abstract */ + if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt iv and abstract error"); + return false; + } + + /* encrypt bootloader image */ + err = esp_image_basic_verify(0x1000, &image_len); + if(err == ESP_OK && image_len != 0) { + if (false == flash_encrypt_write(0x1000, image_len)) { + ESP_LOGE(TAG, "encrypt 2nd boot error"); + return false; + } + } else { + ESP_LOGE(TAG, "2nd boot len error"); + return false; + } - /* encrypt write boot bin*/ - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if(bin_len != 0) { - if (false == flash_encrypt_write(0x1000, bin_len)) { - ESP_LOGE(TAG, "encrypt 2nd boot error"); - return false; - } - } else { - ESP_LOGE(TAG, "2nd boot len error"); - return false; - } /* encrypt partition table */ - if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt partition table error"); - return false; - } + if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt partition table error"); + return false; + } /* encrypt write factory bin */ - if(bs->factory.offset != 0x00) { - ESP_LOGD(TAG, "have factory bin"); - boot_cache_redirect(bs->factory.offset, bs->factory.size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->factory.offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->factory.offset, bin_len)) { - ESP_LOGE(TAG, "encrypt factory bin error"); - return false; - } - } - } + if(bs->factory.offset != 0 && bs->factory.size != 0) { + ESP_LOGD(TAG, "have factory bin"); + if (false == flash_encrypt_write(bs->factory.offset, bs->factory.size)) { + ESP_LOGE(TAG, "encrypt factory bin error"); + return false; + } + } + /* encrypt write test bin */ - if(bs->test.offset != 0x00) { - ESP_LOGD(TAG, "have test bin"); - boot_cache_redirect(bs->test.offset, bs->test.size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->test.offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->test.offset, bin_len)) { - ESP_LOGE(TAG, "encrypt test bin error"); - return false; - } - } - } + if(bs->test.offset != 0 && bs->test.size != 0) { + ESP_LOGD(TAG, "have test bin"); + if (false == flash_encrypt_write(bs->test.offset, bs->test.size)) { + ESP_LOGE(TAG, "encrypt test bin error"); + return false; + } + } + /* encrypt write ota bin */ - for (i = 0;i<16;i++) { - if(bs->ota[i].offset != 0x00) { - ESP_LOGD(TAG, "have ota[%d] bin",i); - boot_cache_redirect(bs->ota[i].offset, bs->ota[i].size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->ota[i].offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->ota[i].offset, bin_len)) { - ESP_LOGE(TAG, "encrypt ota bin error"); - return false; - } - } - } - } + for (int i = 0; i < 16; i++) { + if(bs->ota[i].offset != 0 && bs->ota[i].size != 0) { + ESP_LOGD(TAG, "have ota[%d] bin",i); + if (false == flash_encrypt_write(bs->ota[i].offset, bs->ota[i].size)) { + ESP_LOGE(TAG, "encrypt ota bin error"); + return false; + } + } + } + /* encrypt write ota info bin */ - if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt ota info error"); - return false; - } - REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04); - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - ESP_LOGW(TAG, "burn flash_crypt_cnt"); - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ - return true; - } else { - ESP_LOGI(TAG, "flash already encrypted."); - return true; - } + if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt ota info error"); + return false; + } + + REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04); + REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ + REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ + ESP_LOGW(TAG, "burn flash_crypt_cnt"); + REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ + REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ + return true; + } else { + ESP_LOGI(TAG, "flash already encrypted."); + return true; + } } diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 070fbf24d3..2b1b8573fc 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -31,6 +31,8 @@ #include "sdkconfig.h" #include "bootloader_config.h" +#include "bootloader_flash.h" +#include "esp_image_format.h" static const char* TAG = "secure_boot"; @@ -40,43 +42,52 @@ static const char* TAG = "secure_boot"; * * @inputs: bool */ -static bool secure_boot_generate(uint32_t bin_len){ +static bool secure_boot_generate(uint32_t image_len){ SpiFlashOpResult spiRet; - uint16_t i; uint32_t buf[32]; - if (bin_len % 128 != 0) { - bin_len = (bin_len / 128 + 1) * 128; - } + const void *image; + + if (image_len % 128 != 0) { + image_len = (image_len / 128 + 1) * 128; + } ets_secure_boot_start(); ets_secure_boot_rd_iv(buf); ets_secure_boot_hash(NULL); Cache_Read_Disable(0); - /* iv stored in sec 0 */ + /* iv stored in sec 0 */ spiRet = SPIEraseSector(0); if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ - spiRet = SPIWrite(0, buf, 128); - if (spiRet != SPI_FLASH_RESULT_OK) { ESP_LOGE(TAG, SPI_ERROR_LOG); return false; } - ESP_LOGD(TAG, "write iv to flash."); Cache_Read_Enable(0); - /* read 4K code image from flash, for test */ - for (i = 0; i < bin_len; i+=128) { - ets_secure_boot_hash((uint32_t *)(0x3f400000 + 0x1000 + i)); + + /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ + ESP_LOGD(TAG, "write iv to flash."); + spiRet = SPIWrite(0, buf, 128); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, SPI_ERROR_LOG); + return false; } + /* generate digest from image contents */ + image = bootloader_mmap(0x1000, image_len); + if (!image) { + ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len); + return false; + } + for (int i = 0; i < image_len; i+=128) { + ets_secure_boot_hash(image + i/sizeof(void *)); + } + bootloader_unmap(image); + ets_secure_boot_obtain(); ets_secure_boot_rd_abstract(buf); ets_secure_boot_finish(); - Cache_Read_Disable(0); - /* write abstract to flash, 0x0080, 64 bytes (512 bits) */ + + ESP_LOGD(TAG, "write abstract to flash."); spiRet = SPIWrite(0x80, buf, 64); if (spiRet != SPI_FLASH_RESULT_OK) { ESP_LOGE(TAG, SPI_ERROR_LOG); @@ -99,9 +110,9 @@ static inline void burn_efuses() } /** - * @function : secure_boot_generate_bootloader_digest + * @brief Enable secure boot if it is not already enabled. * - * @description: Called if the secure boot flag is set on the + * Called if the secure boot flag is set on the * bootloader image in flash. If secure boot is not yet enabled for * bootloader, this will generate the secure boot digest and enable * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. @@ -110,24 +121,21 @@ static inline void burn_efuses() * ROM bootloader does this.) * * @return true if secure boot is enabled (either was already enabled, - * or is freshly enabled as a result of calling this function.) + * or is freshly enabled as a result of calling this function.) false + * implies an error occured (possibly secure boot is part-enabled.) */ bool secure_boot_generate_bootloader_digest(void) { - uint32_t bin_len = 0; + esp_err_t err; + uint32_t image_len = 0; if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) { ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); return true; } - boot_cache_redirect( 0, 64*1024); - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if (bin_len == 0) { - ESP_LOGE(TAG, "Invalid bootloader image length zero."); - return false; - } - if (bin_len > 0x100000) { - ESP_LOGE(TAG, "Invalid bootloader image length %x", bin_len); + err = esp_image_basic_verify(0x1000, &image_len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err); return false; } @@ -168,7 +176,7 @@ bool secure_boot_generate_bootloader_digest(void) { } ESP_LOGI(TAG, "Generating secure boot digest..."); - if (false == secure_boot_generate(bin_len)){ + if (false == secure_boot_generate(image_len)){ ESP_LOGE(TAG, "secure boot generation failed"); return false; } diff --git a/components/bootloader_support/README.rst b/components/bootloader_support/README.rst new file mode 100644 index 0000000000..54ac861c9b --- /dev/null +++ b/components/bootloader_support/README.rst @@ -0,0 +1,9 @@ +Bootloader Support Component +============================ + +Overview +-------- + +"Bootloader support" contains APIs which are used by the bootloader but are also needed for the main app. + +Code in this component needs to be aware of being executed in a bootloader environment (no RTOS available, BOOTLOADER_BUILD macro set) or in an esp-idf app environment (RTOS running, need locking support.) diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk new file mode 100755 index 0000000000..2988fe287e --- /dev/null +++ b/components/bootloader_support/component.mk @@ -0,0 +1,11 @@ +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := include_priv + +ifdef IS_BOOTLOADER_BUILD +# share "private" headers with the bootloader component +COMPONENT_ADD_INCLUDEDIRS += include_priv +endif + +COMPONENT_SRCDIRS := src + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h new file mode 100644 index 0000000000..a8b1739d73 --- /dev/null +++ b/components/bootloader_support/include/esp_image_format.h @@ -0,0 +1,133 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP32_IMAGE_FORMAT_H +#define __ESP32_IMAGE_FORMAT_H + +#include +#include + +#define ESP_ERR_IMAGE_BASE 0x2000 +#define ESP_ERR_IMAGE_FLASH_FAIL (ESP_ERR_IMAGE_BASE + 1) +#define ESP_ERR_IMAGE_INVALID (ESP_ERR_IMAGE_BASE + 2) + +/* Support for app/bootloader image parsing + Can be compiled as part of app or bootloader code. +*/ + +/* SPI flash mode, used in esp_image_header_t */ +typedef enum { + ESP_IMAGE_SPI_MODE_QIO, + ESP_IMAGE_SPI_MODE_QOUT, + ESP_IMAGE_SPI_MODE_DIO, + ESP_IMAGE_SPI_MODE_DOUT, + ESP_IMAGE_SPI_MODE_FAST_READ, + ESP_IMAGE_SPI_MODE_SLOW_READ +} esp_image_spi_mode_t; + +/* SPI flash clock frequency */ +enum { + ESP_IMAGE_SPI_SPEED_40M, + ESP_IMAGE_SPI_SPEED_26M, + ESP_IMAGE_SPI_SPEED_20M, + ESP_IMAGE_SPI_SPEED_80M = 0xF +} esp_image_spi_freq_t; + +/* Supported SPI flash sizes */ +typedef enum { + ESP_IMAGE_FLASH_SIZE_1MB = 0, + ESP_IMAGE_FLASH_SIZE_2MB, + ESP_IMAGE_FLASH_SIZE_4MB, + ESP_IMAGE_FLASH_SIZE_8MB, + ESP_IMAGE_FLASH_SIZE_16MB, + ESP_IMAGE_FLASH_SIZE_MAX +} esp_image_flash_size_t; + +#define ESP_IMAGE_HEADER_MAGIC 0xE9 + +/* Main header of binary image */ +typedef struct { + uint8_t magic; + uint8_t segment_count; + uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint32_t entry_addr; + uint8_t encrypt_flag; /* encrypt flag */ + uint8_t secure_boot_flag; /* secure boot flag */ + uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ +} esp_image_header_t; + +/* Header of binary image segment */ +typedef struct { + uint32_t load_addr; + uint32_t data_len; +} esp_image_segment_header_t; + + +/** + * @brief Read an ESP image header from flash. + * + * @param src_addr Address in flash to load image header. Must be 4 byte aligned. + * @param[out] image_header Pointer to an esp_image_header_t struture to be filled with data. If the function fails, contents are undefined. + * + * @return ESP_OK if image header was loaded, ESP_ERR_IMAGE_FLASH_FAIL + * if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header + * appears invalid. + */ +esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header); + +/** + * @brief Read the segment header and data offset of a segment in the image. + * + * @param index Index of the segment to load information for. + * @param src_addr Base address in flash of the image. + * @param[in] image_header Pointer to the flash image header, already loaded by @ref esp_image_load_header(). + * @param[out] segment_header Pointer to a segment header structure to be filled with data. If the function fails, contents are undefined. + * @param[out] segment_data_offset Pointer to the data offset of the segment. + * + * @return ESP_OK if segment_header & segment_data_offset were loaded successfully, ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header appears invalid, ESP_ERR_INVALID_ARG if the index is invalid. + */ +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset); + + +/** + * @brief Return length of an image in flash. Non-cryptographically validates image integrity in the process. + * + * If the image has a secure boot signature appended, the signature is not checked and this length is not included in the result. + * + * Image validation checks: + * - Magic byte + * - No single section longer than 16MB + * - Total image no longer than 16MB + * - 8 bit image checksum is valid + * + * @param src_addr Offset of the start of the image in flash. Must be 4 byte aligned. + * @param[out] length Length of the image, set to a value if the image is valid. Can be null. + * + * @return ESP_OK if image is valid, ESP_FAIL or ESP_ERR_IMAGE_INVALID on errors. + * + */ +esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *length); + + +typedef struct { + uint32_t drom_addr; + uint32_t drom_load_addr; + uint32_t drom_size; + uint32_t irom_addr; + uint32_t irom_load_addr; + uint32_t irom_size; +} esp_image_flash_mapping_t; + +#endif diff --git a/components/bootloader_support/include/esp_secureboot.h b/components/bootloader_support/include/esp_secureboot.h new file mode 100644 index 0000000000..b0097df8a6 --- /dev/null +++ b/components/bootloader_support/include/esp_secureboot.h @@ -0,0 +1,51 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP32_SECUREBOOT_H +#define __ESP32_SECUREBOOT_H + +#include +#include + +/* Support functions for secure boot features. + + Can be compiled as part of app or bootloader code. +*/ + +/** @brief Is secure boot currently enabled in hardware? + * + * Secure boot is enabled if the ABS_DONE_0 efuse is blown. This means + * that the ROM bootloader code will only boot a verified secure + * bootloader digest from now on. + * + * @return true if secure boot is enabled. + */ +bool esp_secure_boot_enabled(void); + + +/** @brief Enable secure boot if it isw not already enabled. + * + * @important If this function succeeds, secure boot is permanentl + * enabled on the chip via efuse. + * + * This function is intended to be called from bootloader code. + * + * @return ESP_ERR_INVALID_STATE if efuse state doesn't allow + * secure boot to be enabled cleanly. ESP_OK if secure boot + * is enabled on this chip from now on. + */ +esp_err_t esp_secure_boot_enable(void); + + + +#endif diff --git a/components/bootloader_support/include_priv/bootloader_flash.h b/components/bootloader_support/include_priv/bootloader_flash.h new file mode 100644 index 0000000000..d70ec22d56 --- /dev/null +++ b/components/bootloader_support/include_priv/bootloader_flash.h @@ -0,0 +1,69 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __BOOTLOADER_FLASH_H +#define __BOOTLOADER_FLASH_H + +#include +#include +#include +#include + +/* Provide a Flash API for bootloader_support code, + that can be used from bootloader or app code. + + This header is available to source code in the bootloader & + bootloader_support components only. +*/ + +/** + * @brief Map a region of flash to data memory + * + * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_unmapped before another region is mapped. + * + * @important In app code, these functions are not thread safe. + * + * Call bootloader_unmap once for each successful call to bootloader_mmap. + * + * In esp-idf app, this function maps directly to spi_flash_mmap. + * + * @param offset - Starting flash offset to map to memory. + * @param length - Length of data to map. + * + * @return Pointer to mapped data memory (at src_addr), or NULL + * if an allocation error occured. + */ +const void *bootloader_mmap(uint32_t src_addr, uint32_t size); + + +/** + * @brief Unmap a previously mapped region of flash + * + * Call bootloader_unmap once for each successful call to bootloader_mmap. + */ +void bootloader_unmap(const void *mapping); + +/** + * @brief Read data from Flash. + * + * @note Both src and dest have to be 4-byte aligned. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size); + +#endif diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c new file mode 100644 index 0000000000..a50cd157e9 --- /dev/null +++ b/components/bootloader_support/src/bootloader_flash.c @@ -0,0 +1,122 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include + +#include +#include +#include /* including in bootloader for error values */ + +#ifndef BOOTLOADER_BUILD +/* Normal app version maps to esp_spi_flash.h operations... + */ +static const char *TAG = "bootloader_mmap"; + +static spi_flash_mmap_memory_t map; + +const void *bootloader_mmap(uint32_t src_addr, uint32_t size) +{ + if (map) { + ESP_LOGE(TAG, "tried to bootloader_mmap twice"); + return NULL; /* existing mapping in use... */ + } + const void *result = NULL; + esp_err_t err = spi_flash_mmap(src_addr, size, SPI_FLASH_MMAP_DATA, &result, &map); + if (err != ESP_OK) { + result = NULL; + } + return result; +} + +void bootloader_unmap(const void *mapping) +{ + if(mapping && map) { + spi_flash_munmap(map); + } + map = 0; +} + +esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size) +{ + return spi_flash_read(src, dest, size); +} + +#else +/* Bootloader version, uses ROM functions only */ +#include +#include + +static const char *TAG = "bootloader_flash"; + +static bool mapped; + +const void *bootloader_mmap(uint32_t src_addr, uint32_t size) +{ + if (mapped) { + ESP_LOGE(TAG, "tried to bootloader_mmap twice"); + return NULL; /* can't map twice */ + } + + uint32_t src_addr_aligned = src_addr & 0xffff0000; + uint32_t count = (size + 0xffff) / 0x10000; + Cache_Read_Disable(0); + Cache_Flush(0); + ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count ); + cache_flash_mmu_set( 0, 0, 0x3f400000, src_addr_aligned, 64, count ); + Cache_Read_Enable( 0 ); + + mapped = true; + + return (void *)(0x3f400000 + (src_addr - src_addr_aligned)); +} + +void bootloader_unmap(const void *mapping) +{ + if (mapped) { + /* Full MMU reset */ + Cache_Read_Disable(0); + Cache_Flush(0); + mmu_init(0); + mapped = false; + } +} + +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) +{ + if(src_addr & 3) { + ESP_LOGE(TAG, "bootloader_flash_read src_addr not 4-byte aligned"); + return ESP_FAIL; + } + if((intptr_t)dest & 3) { + ESP_LOGE(TAG, "bootloader_flash_read dest not 4-byte aligned"); + return ESP_FAIL; + } + + Cache_Read_Disable(0); + Cache_Flush(0); + SpiFlashOpResult r = SPIRead(src_addr, dest, size); + Cache_Read_Enable(0); + + switch(r) { + case SPI_FLASH_RESULT_OK: + return ESP_OK; + case SPI_FLASH_RESULT_ERR: + return ESP_ERR_FLASH_OP_FAIL; + case SPI_FLASH_RESULT_TIMEOUT: + return ESP_ERR_FLASH_OP_TIMEOUT; + default: + return ESP_FAIL; + } +} + +#endif diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c new file mode 100644 index 0000000000..ad3cd33f13 --- /dev/null +++ b/components/bootloader_support/src/esp_image_format.c @@ -0,0 +1,155 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include + +#include +#include +#include + +const static char *TAG = "esp_image"; + +#define SIXTEEN_MB 0x1000000 +#define ESP_ROM_CHECKSUM_INITIAL 0xEF + +esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header) +{ + esp_err_t err; + ESP_LOGD(TAG, "reading image header @ 0x%x", src_addr); + + err = bootloader_flash_read(src_addr, image_header, sizeof(esp_image_header_t)); + + if (err == ESP_OK) { + if (image_header->magic != ESP_IMAGE_HEADER_MAGIC) { + ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr); + err = ESP_ERR_IMAGE_INVALID; + } + if (image_header->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI mode %d", src_addr, image_header->spi_mode); + } + if (image_header->spi_speed > ESP_IMAGE_SPI_SPEED_80M) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI speed %d", src_addr, image_header->spi_speed); + } + if (image_header->spi_size > ESP_IMAGE_FLASH_SIZE_MAX) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI size %d", src_addr, image_header->spi_size); + } + } + + if (err != ESP_OK) { + bzero(image_header, sizeof(esp_image_header_t)); + } + return err; +} + +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset) +{ + esp_err_t err = ESP_OK; + uint32_t next_addr = src_addr + sizeof(esp_image_header_t); + + if(index >= image_header->segment_count) { + ESP_LOGE(TAG, "index %d higher than segment count %d", index, image_header->segment_count); + return ESP_ERR_INVALID_ARG; + } + + for(int i = 0; i <= index && err == ESP_OK; i++) { + err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t)); + if (err == ESP_OK) { + if ((segment_header->data_len & 3) != 0 + || segment_header->data_len >= SIXTEEN_MB) { + ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); + err = ESP_ERR_IMAGE_INVALID; + } + next_addr += sizeof(esp_image_segment_header_t); + *segment_data_offset = next_addr; + next_addr += segment_header->data_len; + } + } + + if (err != ESP_OK) { + *segment_data_offset = 0; + bzero(segment_header, sizeof(esp_image_segment_header_t)); + } + + return err; +} + +esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) +{ + esp_err_t err; + uint8_t buf[16]; + uint8_t checksum = ESP_ROM_CHECKSUM_INITIAL; + esp_image_header_t image_header; + esp_image_segment_header_t segment_header = { 0 }; + uint32_t segment_data_offs = 0; + const uint8_t *segment_data; + uint32_t end_addr; + uint32_t length; + + if (p_length != NULL) { + *p_length = 0; + } + + err = esp_image_load_header(src_addr, &image_header); + if (err != ESP_OK) { + return err; + } + + ESP_LOGD(TAG, "reading %d image segments", image_header.segment_count); + + /* Checksum each segment's data */ + for (int i = 0; i < image_header.segment_count; i++) { + err = esp_image_load_segment_header(i, src_addr, &image_header, + &segment_header, &segment_data_offs); + if (err != ESP_OK) { + return err; + } + + segment_data = bootloader_mmap(segment_data_offs, segment_header.data_len); + if (segment_data == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", segment_data_offs, segment_header.data_len); + return ESP_FAIL; + } + for(int i = 0; i < segment_header.data_len; i++) { + checksum ^= segment_data[i]; + } + bootloader_unmap(segment_data); + } + + /* End of image, verify checksum */ + end_addr = segment_data_offs + segment_header.data_len; + + if (end_addr < src_addr) { + ESP_LOGE(TAG, "image offset has wrapped"); + return ESP_ERR_IMAGE_INVALID; + } + + length = end_addr - src_addr; + if (length >= SIXTEEN_MB) { + ESP_LOGE(TAG, "invalid total length 0x%x", length); + return ESP_ERR_IMAGE_INVALID; + } + + /* image padded to next full 16 byte block, with checksum byte at very end */ + length += 15 - (length % 16); + bootloader_flash_read(src_addr + length - 16, buf, 16); + if (checksum != buf[15]) { + ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", + checksum, buf[15]); + return ESP_ERR_IMAGE_INVALID; + } + + if (p_length != NULL) { + *p_length = length; + } + return ESP_OK; +} diff --git a/components/bootloader_support/src/secureboot.c b/components/bootloader_support/src/secureboot.c new file mode 100644 index 0000000000..e55c1f33fa --- /dev/null +++ b/components/bootloader_support/src/secureboot.c @@ -0,0 +1,7 @@ +#include +#include + +#include "esp_log.h" + + + diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index 4bf886c842..ce4acb7bc9 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -24,54 +24,6 @@ extern "C" #define ESP_PARTITION_TABLE_ADDR 0x4000 #define ESP_PARTITION_MAGIC 0x50AA -/* SPI flash mode, used in esp_image_header_t */ -typedef enum { - ESP_IMAGE_SPI_MODE_QIO, - ESP_IMAGE_SPI_MODE_QOUT, - ESP_IMAGE_SPI_MODE_DIO, - ESP_IMAGE_SPI_MODE_DOUT, - ESP_IMAGE_SPI_MODE_FAST_READ, - ESP_IMAGE_SPI_MODE_SLOW_READ -} esp_image_spi_mode_t; - -/* SPI flash clock frequency */ -enum { - ESP_IMAGE_SPI_SPEED_40M, - ESP_IMAGE_SPI_SPEED_26M, - ESP_IMAGE_SPI_SPEED_20M, - ESP_IMAGE_SPI_SPEED_80M = 0xF -} esp_image_spi_freq_t; - -/* Supported SPI flash sizes */ -typedef enum { - ESP_IMAGE_FLASH_SIZE_1MB = 0, - ESP_IMAGE_FLASH_SIZE_2MB, - ESP_IMAGE_FLASH_SIZE_4MB, - ESP_IMAGE_FLASH_SIZE_8MB, - ESP_IMAGE_FLASH_SIZE_16MB, - ESP_IMAGE_FLASH_SIZE_MAX -} esp_image_flash_size_t; - -/* Main header of binary image */ -typedef struct { - uint8_t magic; - uint8_t blocks; - uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ - uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ - uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ - uint32_t entry_addr; - uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secure_boot_flag; /* secure boot flag */ - uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ -} esp_image_header_t; - -/* Header of binary image segment */ -typedef struct { - uint32_t load_addr; - uint32_t data_len; -} esp_image_section_header_t; - - /* OTA selection structure (two copies in the OTA data partition.) Size of 32 bytes is friendly to flash encryption */ typedef struct { diff --git a/components/esp32/include/rom/secure_boot.h b/components/esp32/include/rom/secure_boot.h index cfeda08933..bd4f32ed95 100644 --- a/components/esp32/include/rom/secure_boot.h +++ b/components/esp32/include/rom/secure_boot.h @@ -25,7 +25,7 @@ void ets_secure_boot_start(void); void ets_secure_boot_finish(void); -void ets_secure_boot_hash(uint32_t *buf); +void ets_secure_boot_hash(const uint32_t *buf); void ets_secure_boot_obtain(void); diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 6716f6e10f..f4b9aa2885 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -19,6 +19,10 @@ #include #include "sdkconfig.h" +#ifdef BOOTLOADER_BUILD +#include +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/make/project.mk b/make/project.mk index 17fb83e0d8..7e8dc46d54 100644 --- a/make/project.mk +++ b/make/project.mk @@ -100,7 +100,7 @@ COMPONENT_LDFLAGS := # # Debugging this? Replace $(shell with $(error and you'll see the full command as-run. define GetVariable -$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) | sed -En "s/^$(2)=(.+)/\1/p" ) +$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) IS_BOOTLOADER_BUILD=$(IS_BOOTLOADER_BUILD) | sed -En "s/^$(2)=(.+)/\1/p" ) endef COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(addprefix $(comp)/, \ From c534dedf2d2a720fa748a57ce60b823d7a8069fe Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 2 Nov 2016 17:17:28 +0800 Subject: [PATCH 109/285] newlib: implement time syscalls --- components/esp32/Kconfig | 29 +++++ components/esp32/cpu_start.c | 3 +- components/esp32/include/soc/frc_timer_reg.h | 49 ++++++++ components/esp32/include/soc/rtc_cntl_reg.h | 3 + components/esp32/include/soc/soc.h | 9 +- .../newlib/platform_include/esp_newlib.h | 8 +- components/newlib/syscall_table.c | 3 +- components/newlib/time.c | 118 +++++++++++++++++- 8 files changed, 211 insertions(+), 11 deletions(-) create mode 100644 components/esp32/include/soc/frc_timer_reg.h diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index b5a8d2f2dd..928b190321 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -319,6 +319,35 @@ config BROWNOUT_DET_RESETDELAY before trying to restart the chip. You can set the delay here. +choice ESP32_TIME_SYSCALL + prompt "Timers used for gettimeofday function" + default ESP32_TIME_SYSCALL_USE_RTC_FRC1 + help + This setting defines which hardware timers are used to + implement 'gettimeofday' function in C library. + + - If only FRC1 timer is used, gettimeofday will provide time at + microsecond resolution. Time will not be preserved when going + into deep sleep mode. + - If both FRC1 and RTC timers are used, timekeeping will + continue in deep sleep. Time will be reported at 1 microsecond + resolution. + - If only RTC timer is used, timekeeping will continue in + deep sleep, but time will be measured at 6.(6) microsecond + resolution. Also the gettimeofday function itself may take + longer to run. + - If no timers are used, gettimeofday function return -1 and + set errno to ENOSYS. + +config ESP32_TIME_SYSCALL_USE_RTC + bool "RTC" +config ESP32_TIME_SYSCALL_USE_RTC_FRC1 + bool "RTC and FRC1" +config ESP32_TIME_SYSCALL_USE_FRC1 + bool "FRC1" +config ESP32_TIME_SYSCALL_USE_NONE + bool "None" +endchoice endmenu diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 8d56e2c12d..c82c528597 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -169,7 +169,8 @@ void start_cpu0_default(void) #if CONFIG_TASK_WDT esp_task_wdt_init(); #endif - esp_setup_syscalls(); + esp_setup_syscall_table(); + esp_setup_time_syscalls(); esp_vfs_dev_uart_register(); esp_reent_init(_GLOBAL_REENT); const char* default_uart_dev = "/dev/uart/0"; diff --git a/components/esp32/include/soc/frc_timer_reg.h b/components/esp32/include/soc/frc_timer_reg.h new file mode 100644 index 0000000000..24b942c0bb --- /dev/null +++ b/components/esp32/include/soc/frc_timer_reg.h @@ -0,0 +1,49 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SOC_FRC_TIMER_REG_H_ +#define _SOC_FRC_TIMER_REG_H_ + +#include "soc.h" + +/** + * These are the register definitions for "legacy" timers + */ + +#define REG_FRC_TIMER_BASE(i) (DR_REG_FRC_TIMER_BASE + i*0x20) + +#define FRC_TIMER_LOAD_REG(i) (REG_FRC_TIMER_BASE(i) + 0x0) // timer load value (23 bit for i==0, 32 bit for i==1) +#define FRC_TIMER_LOAD_VALUE(i) ((i == 0)?0x007FFFFF:0xffffffff) +#define FRC_TIMER_LOAD_VALUE_S 0 + +#define FRC_TIMER_COUNT_REG(i) (REG_FRC_TIMER_BASE(i) + 0x4) // timer count value (23 bit for i==0, 32 bit for i==1) +#define FRC_TIMER_COUNT ((i == 0)?0x007FFFFF:0xffffffff) +#define FRC_TIMER_COUNT_S 0 + +#define FRC_TIMER_CTRL_REG(i) (REG_FRC_TIMER_BASE(i) + 0x8) +#define FRC_TIMER_INT_ENABLE (BIT(8)) // enable interrupt +#define FRC_TIMER_ENABLE (BIT(7)) // enable timer +#define FRC_TIMER_AUTOLOAD (BIT(6)) // enable autoload +#define FRC_TIMER_PRESCALER 0x00000007 // 0: divide by 1, 2: divide by 16, 4: divide by 256 +#define FRC_TIMER_PRESCALER_S 1 +#define FRC_TIMER_EDGE_INT (BIT(0)) // 0: level, 1: edge + +#define FRC_TIMER_INT_REG(i) (REG_FRC_TIMER_BASE(i) + 0xC) +#define FRC_TIMER_INT_CLR (BIT(0)) // clear interrupt + +#define FRC_TIMER_ALARM_REG(i) (REG_FRC_TIMER_BASE(i) + 0x10) // timer alarm value; register only present for i == 1 +#define FRC_TIMER_ALARM 0xFFFFFFFF +#define FRC_TIMER_ALARM_S 0 + +#endif //_SOC_FRC_TIMER_REG_H_ diff --git a/components/esp32/include/soc/rtc_cntl_reg.h b/components/esp32/include/soc/rtc_cntl_reg.h index bb4e2afced..d99cec1864 100644 --- a/components/esp32/include/soc/rtc_cntl_reg.h +++ b/components/esp32/include/soc/rtc_cntl_reg.h @@ -239,6 +239,9 @@ #define RTC_CNTL_TIME_VALID_V 0x1 #define RTC_CNTL_TIME_VALID_S 30 +/* frequency of RTC slow clock, Hz */ +#define RTC_CTNL_SLOWCLK_FREQ 150000 + #define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10) /* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: RTC timer low 32 bits*/ diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index 4ffdfb069e..8ab9f027c5 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -148,6 +148,7 @@ #define DR_REG_GPIO_SD_BASE 0x3ff44f00 #define DR_REG_FE2_BASE 0x3ff45000 #define DR_REG_FE_BASE 0x3ff46000 +#define DR_REG_FRC_TIMER_BASE 0x3ff47000 #define DR_REG_RTCCNTL_BASE 0x3ff48000 #define DR_REG_RTCIO_BASE 0x3ff48400 #define DR_REG_SARADC_BASE 0x3ff48800 @@ -281,9 +282,9 @@ * 19 2 extern level * 20 2 extern level * 21 2 extern level - * 22 3 extern edge + * 22 3 extern edge FRC1 timer * 23 3 extern level - * 24 4 extern level + * 24 4 extern level TG1_WDT * 25 4 extern level Reserved Reserved * 26 5 extern level Reserved Reserved * 27 3 extern level Reserved Reserved @@ -301,8 +302,10 @@ #define ETS_T0_WDT_INUM 3 #define ETS_WBB_INUM 4 #define ETS_TG0_T1_INUM 10 /**< use edge interrupt*/ +#define ETS_FRC1_INUM 22 +#define ETS_T1_WDT_INUM 24 -//CPU0 Intrrupt number used in ROM, should be cancelled in SDK +//CPU0 Interrupt number used in ROM, should be cancelled in SDK #define ETS_SLC_INUM 1 #define ETS_UART0_INUM 5 #define ETS_UART1_INUM 5 diff --git a/components/newlib/platform_include/esp_newlib.h b/components/newlib/platform_include/esp_newlib.h index 468f2ae34f..eac3544259 100644 --- a/components/newlib/platform_include/esp_newlib.h +++ b/components/newlib/platform_include/esp_newlib.h @@ -31,7 +31,13 @@ void esp_reent_init(struct _reent* r); * Called from the startup code, not intended to be called from application * code. */ -void esp_setup_syscalls(); +void esp_setup_syscall_table(); +/** + * Initialize hardware timer used as time source for newlib time functions. + * + * Called from the startup code, not intended to be called from application. + */ +void esp_setup_time_syscalls(); #endif //__ESP_NEWLIB_H__ diff --git a/components/newlib/syscall_table.c b/components/newlib/syscall_table.c index b6414af554..feed768172 100644 --- a/components/newlib/syscall_table.c +++ b/components/newlib/syscall_table.c @@ -24,6 +24,7 @@ #include #include "rom/libc_stubs.h" #include "esp_vfs.h" +#include "esp_newlib.h" static struct _reent s_reent; @@ -79,7 +80,7 @@ static struct syscall_stub_table s_stub_table = { ._scanf_float = &_scanf_float, }; -void esp_setup_syscalls() +void esp_setup_syscall_table() { syscall_table_ptr_pro = &s_stub_table; syscall_table_ptr_app = &s_stub_table; diff --git a/components/newlib/time.c b/components/newlib/time.c index 021b295451..83645aa4cc 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -14,22 +14,130 @@ #include #include +#include +#include #include #include #include #include #include "esp_attr.h" +#include "soc/soc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/frc_timer_reg.h" +#include "rom/ets_sys.h" +#include "freertos/FreeRTOS.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "sdkconfig.h" +#if defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 ) +#define WITH_RTC 1 +#endif -clock_t _times_r(struct _reent *r, struct tms *ptms) +#if defined( CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 ) || defined( CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 ) +#define WITH_FRC1 1 +#endif + +#ifdef WITH_RTC +static uint64_t get_rtc_time_us() { - __errno_r(r) = ENOSYS; - return (clock_t) -1; + SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M); + while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID_M) == 0) { + ; + } + CLEAR_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE_M); + uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG); + uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG); + uint64_t ticks = (high << 32) | low; + return ticks * 100 / (RTC_CTNL_SLOWCLK_FREQ / 10000); // scale RTC_CTNL_SLOWCLK_FREQ to avoid overflow +} +#endif // WITH_RTC + + +#ifdef WITH_FRC1 +#define FRC1_PRESCALER 16 +#define FRC1_PRESCALER_CTL 2 +#define FRC1_TICK_FREQ (APB_CLK_FREQ / FRC1_PRESCALER) +#define FRC1_TICKS_PER_US (FRC1_TICK_FREQ / 1000000) +#define FRC1_ISR_PERIOD_US (FRC_TIMER_LOAD_VALUE(0) / FRC1_TICKS_PER_US) +// Counter frequency will be APB_CLK_FREQ / 16 = 5 MHz +// 1 tick = 0.2 us +// Timer has 23 bit counter, so interrupt will fire each 1677721.6 microseconds. +// This is not a whole number, so timer will drift by 0.3 ppm due to rounding error. + +static volatile uint64_t s_microseconds = 0; + +static void IRAM_ATTR frc_timer_isr() +{ + WRITE_PERI_REG(FRC_TIMER_INT_REG(0), FRC_TIMER_INT_CLR); + s_microseconds += FRC1_ISR_PERIOD_US; } -// TODO: read time from RTC -int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +#endif // WITH_FRC1 + +void esp_setup_time_syscalls() { +#if defined( WITH_FRC1 ) +#if defined( WITH_RTC ) + // initialize time from RTC clock + s_microseconds = get_rtc_time_us(); +#endif //WITH_RTC + + + // set up timer + WRITE_PERI_REG(FRC_TIMER_CTRL_REG(0), \ + FRC_TIMER_AUTOLOAD | \ + (FRC1_PRESCALER_CTL << FRC_TIMER_PRESCALER_S) | \ + FRC_TIMER_EDGE_INT); + + WRITE_PERI_REG(FRC_TIMER_LOAD_REG(0), FRC_TIMER_LOAD_VALUE(0)); + SET_PERI_REG_MASK(FRC_TIMER_CTRL_REG(0), + FRC_TIMER_ENABLE | \ + FRC_TIMER_INT_ENABLE); + intr_matrix_set(xPortGetCoreID(), ETS_TIMER1_INTR_SOURCE, ETS_FRC1_INUM); + xt_set_interrupt_handler(ETS_FRC1_INUM, &frc_timer_isr, NULL); + xt_ints_on(1 << ETS_FRC1_INUM); +#endif // WITH_FRC1 +} + +clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) +{ + clock_t t = xTaskGetTickCount() * (portTICK_PERIOD_MS * CLK_TCK / 1000); + ptms->tms_cstime = t; + ptms->tms_cutime = 0; + ptms->tms_stime = t; + ptms->tms_utime = 0; + struct timeval tv = {0, 0}; + _gettimeofday_r(r, &tv, NULL); + return (clock_t) tv.tv_sec; +} + +int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +{ + (void) tz; +#ifdef WITH_FRC1 + uint32_t timer_ticks_before = READ_PERI_REG(FRC_TIMER_COUNT_REG(0)); + uint64_t microseconds = s_microseconds; + uint32_t timer_ticks_after = READ_PERI_REG(FRC_TIMER_COUNT_REG(0)); + if (timer_ticks_after > timer_ticks_before) { + // overflow happened at some point between getting + // timer_ticks_before and timer_ticks_after + // microseconds value is ambiguous, get a new one + microseconds = s_microseconds; + } + microseconds += (FRC_TIMER_LOAD_VALUE(0) - timer_ticks_after) / FRC1_TICKS_PER_US; +#elif defined(WITH_RTC) + uint64_t microseconds = get_rtc_time_us(); +#endif + +#if defined( WITH_FRC1 ) || defined( WITH_RTC ) + if (tv) { + tv->tv_sec = microseconds / 1000000; + tv->tv_usec = microseconds % 1000000; + } + return 0; +#else __errno_r(r) = ENOSYS; return -1; +#endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) } From fdff6b2b012ac29f6aa6218ebfcc37b881d8ad9c Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 2 Nov 2016 19:08:55 +0800 Subject: [PATCH 110/285] run unit test case by case name --- .../idf_test/integration_test/TestEnvAll.yml | 6 + .../CIConfigs/UT_Function_SYS_01.yml | 2 +- components/idf_test/unit_test/TestCaseAll.yml | 103 +++++++----------- .../TestCaseScript/IDFUnitTest/UnitTest.py | 14 ++- components/idf_test/unit_test/TestEnvAll.yml | 6 + 5 files changed, 61 insertions(+), 70 deletions(-) diff --git a/components/idf_test/integration_test/TestEnvAll.yml b/components/idf_test/integration_test/TestEnvAll.yml index 6b21760150..b8a2a497c6 100644 --- a/components/idf_test/integration_test/TestEnvAll.yml +++ b/components/idf_test/integration_test/TestEnvAll.yml @@ -202,6 +202,12 @@ test environment: SSC2', additional param list: '', basic param list: '', script path: EnvBase.py, tag: SSC_T2_GPIO3, test environment detail: '[TBD] 2个ESP_8266通过UART连到PC, ESP_8266之间需要测试的Target_GPIO相连', test script: EnvBase} +- {PC OS: '', Special: N, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_JAP, + test environment detail: 'PC has 1 wired NIC connected to APC. + + APC control the power supply of multiple APs. + + 2 SSC target connect with PC by UART.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_PhyMode, test environment detail: '2 SSC target connect with PC by UART. diff --git a/components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml b/components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml index 86e191cedf..c3561aa0c5 100644 --- a/components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml +++ b/components/idf_test/unit_test/CIConfigs/UT_Function_SYS_01.yml @@ -2,7 +2,7 @@ Config: {execute count: 1, execute order: in order} DUT: [UT1] Filter: - Add: - ID: [SYS_OS_0101, SYS_OS_0102, SYS_MISC_0103, SYS_MISC_0102, SYS_MISC_0105, SYS_MISC_0104, + ID: [SYS_OS_0102, SYS_MISC_0103, SYS_MISC_0102, SYS_MISC_0105, SYS_MISC_0104, SYS_MISC_0107, SYS_MISC_0106, SYS_MISC_0109, SYS_MISC_0108, SYS_MISC_0112, SYS_MISC_0113, SYS_MISC_0110, SYS_MISC_0111, SYS_MISC_0115, SYS_LIB_0103, SYS_LIB_0102, SYS_LIB_0101, SYS_LIB_0106, SYS_LIB_0105, SYS_LIB_0104] diff --git a/components/idf_test/unit_test/TestCaseAll.yml b/components/idf_test/unit_test/TestCaseAll.yml index 8732296bfc..6f974e0bf5 100644 --- a/components/idf_test/unit_test/TestCaseAll.yml +++ b/components/idf_test/unit_test/TestCaseAll.yml @@ -2,12 +2,12 @@ test cases: - CI ready: 'Yes' ID: SYS_LIB_0101 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "12" + - - test_case = "check if ROM is used for functions" - [dummy] comment: check if ROM is used for functions execution time: 0 @@ -25,12 +25,12 @@ test cases: - CI ready: 'Yes' ID: SYS_LIB_0102 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "13" + - - test_case = "test time functions" - [dummy] comment: test time functions execution time: 0 @@ -48,12 +48,12 @@ test cases: - CI ready: 'Yes' ID: SYS_LIB_0103 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "14" + - - test_case = "test sscanf function" - [dummy] comment: test sscanf function execution time: 0 @@ -71,12 +71,12 @@ test cases: - CI ready: 'Yes' ID: SYS_LIB_0104 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "15" + - - test_case = "test sprintf function" - [dummy] comment: test sprintf function execution time: 0 @@ -94,12 +94,12 @@ test cases: - CI ready: 'Yes' ID: SYS_LIB_0105 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "16" + - - test_case = "test atoX functions" - [dummy] comment: test atoX functions execution time: 0 @@ -117,12 +117,12 @@ test cases: - CI ready: 'Yes' ID: SYS_LIB_0106 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "17" + - - test_case = "test ctype functions" - [dummy] comment: test ctype functions execution time: 0 @@ -140,12 +140,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0102 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "21" + - - test_case = "mbedtls MPI self-tests" - [dummy] comment: mbedtls MPI self-tests execution time: 0 @@ -163,12 +163,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0103 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "25" + - - test_case = "test AES thread safety" - [dummy] comment: test AES thread safety execution time: 0 @@ -186,12 +186,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0104 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "26" + - - test_case = "test AES acceleration" - [dummy] comment: test AES acceleration execution time: 0 @@ -209,12 +209,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0105 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "27" + - - test_case = "test SHA thread safety" - [dummy] comment: test SHA thread safety execution time: 0 @@ -232,12 +232,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0106 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "33" + - - test_case = "context switch saves FP registers" - [dummy] comment: context switch saves FP registers execution time: 0 @@ -255,12 +255,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0107 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "34" + - - test_case = "test FP sqrt" - [dummy] comment: test FP sqrt execution time: 0 @@ -278,12 +278,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0108 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "35" + - - test_case = "test FP div" - [dummy] comment: test FP div execution time: 0 @@ -301,12 +301,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0109 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "36" + - - test_case = "test FP mul" - [dummy] comment: test FP mul execution time: 0 @@ -324,12 +324,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0110 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "37" + - - test_case = "test FP add" - [dummy] comment: test FP add execution time: 0 @@ -347,12 +347,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0111 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "3" + - - test_case = "Test JPEG decompression library" - [dummy] comment: Test JPEG decompression library execution time: 0 @@ -370,12 +370,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0112 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "22" + - - test_case = "mbedtls AES self-tests" - [dummy] comment: mbedtls AES self-tests execution time: 0 @@ -393,12 +393,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0113 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "17" + - - test_case = "mbedtls SHA self-tests" - [dummy] comment: mbedtls SHA self-tests execution time: 0 @@ -416,12 +416,12 @@ test cases: - CI ready: 'Yes' ID: SYS_MISC_0115 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "28" + - - test_case = "test SHA acceleration" - [dummy] comment: test SHA acceleration execution time: 0 @@ -436,38 +436,15 @@ test cases: test point 1: basic function test point 2: SHA acceleration version: v1 (2016-10-31) -- CI ready: 'Yes' - ID: SYS_OS_0101 - SDK: ESP32_IDF - Test App: testje - auto test: 'Yes' - category: Function - cmd set: - - IDFUnitTest/UnitTest - - - test_case = "31" - - [dummy] - comment: FreeRTOS Event Groups - execution time: 0 - expected result: 1. set succeed - initial condition: UTINIT1 - level: Unit - module: System - steps: 1. run freertos test - sub module: OS - summary: freertos unit test - test environment: UT_T1_1 - test point 1: basic function - test point 2: freertos - version: v1 (2016-10-26) - CI ready: 'Yes' ID: SYS_OS_0102 SDK: ESP32_IDF - Test App: testje + Test App: UT auto test: 'Yes' category: Function cmd set: - IDFUnitTest/UnitTest - - - test_case = "2" + - - test_case = "Freertos TLS delete cb" - [dummy] comment: Freertos TLS delete cb execution time: 0 diff --git a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py index 6e7cac4109..2af4747e5d 100644 --- a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py +++ b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py @@ -11,7 +11,7 @@ class UnitTest(PerformanceTCBase.PerformanceTCBase): timeout=timeout, log_path=log_path) self.test_case = None - self.test_timeout = 6 + self.test_timeout = 20 # load param from excel for i in range(1, len(cmd_set)): @@ -26,12 +26,14 @@ class UnitTest(PerformanceTCBase.PerformanceTCBase): try: self.serial_write_line("UT1", self.test_case) - time.sleep(self.test_timeout) #wait for test to run before reading result - data = self.serial_read_data("UT1") - if re.search('[^0] Tests 0 F', data): #check that number of tests run != 0 and number of tests failed == 0 - self.set_result("Success") + data = "" + for _ in range(self.timeout): + time.sleep(1) #wait for test to run before reading result + data += self.serial_read_data("UT1") + if re.search('[^0] Tests 0 F', data): #check that number of tests run != 0 and number of tests failed == 0 + self.set_result("Success") else: - self.set_result("Fail") + self.set_result("Fail") except StandardError,e: NativeLog.add_exception_log(e) diff --git a/components/idf_test/unit_test/TestEnvAll.yml b/components/idf_test/unit_test/TestEnvAll.yml index 6b21760150..b8a2a497c6 100644 --- a/components/idf_test/unit_test/TestEnvAll.yml +++ b/components/idf_test/unit_test/TestEnvAll.yml @@ -202,6 +202,12 @@ test environment: SSC2', additional param list: '', basic param list: '', script path: EnvBase.py, tag: SSC_T2_GPIO3, test environment detail: '[TBD] 2个ESP_8266通过UART连到PC, ESP_8266之间需要测试的Target_GPIO相连', test script: EnvBase} +- {PC OS: '', Special: N, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_JAP, + test environment detail: 'PC has 1 wired NIC connected to APC. + + APC control the power supply of multiple APs. + + 2 SSC target connect with PC by UART.', test script: EnvBase} - {PC OS: '', Special: N, Target Count: 2.0, script path: EnvBase.py, tag: SSC_T2_PhyMode, test environment detail: '2 SSC target connect with PC by UART. From 2e319705ecfd6e2b3c159a771911ff0968652396 Mon Sep 17 00:00:00 2001 From: Yinling Date: Wed, 2 Nov 2016 19:41:33 +0800 Subject: [PATCH 111/285] forget to break when test succeed --- .../idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py index 2af4747e5d..ab29a97f9b 100644 --- a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py +++ b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py @@ -32,6 +32,7 @@ class UnitTest(PerformanceTCBase.PerformanceTCBase): data += self.serial_read_data("UT1") if re.search('[^0] Tests 0 F', data): #check that number of tests run != 0 and number of tests failed == 0 self.set_result("Success") + break else: self.set_result("Fail") From c12aeb11279b9deecdd43e3259d6e52d66fe3661 Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Wed, 2 Nov 2016 20:12:43 +0800 Subject: [PATCH 112/285] add case select by name mark " before case name --- .../idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py index ab29a97f9b..3ec87d24bc 100644 --- a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py +++ b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py @@ -25,7 +25,8 @@ class UnitTest(PerformanceTCBase.PerformanceTCBase): self.flush_data("UT1") try: - self.serial_write_line("UT1", self.test_case) + # add case select by name mark " before case name + self.serial_write_line("UT1", "/"" + self.test_case) data = "" for _ in range(self.timeout): time.sleep(1) #wait for test to run before reading result From e56b745527a96ab41765e8bbe2316920e2d7b602 Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Wed, 2 Nov 2016 20:21:46 +0800 Subject: [PATCH 113/285] incorrect "/", should be "\" --- .../idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py index 3ec87d24bc..35bb62c6d8 100644 --- a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py +++ b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py @@ -26,7 +26,7 @@ class UnitTest(PerformanceTCBase.PerformanceTCBase): try: # add case select by name mark " before case name - self.serial_write_line("UT1", "/"" + self.test_case) + self.serial_write_line("UT1", "\"" + self.test_case) data = "" for _ in range(self.timeout): time.sleep(1) #wait for test to run before reading result From 47910466ce3b646fcbff677f2a8035f30ae8f183 Mon Sep 17 00:00:00 2001 From: He Yin Ling Date: Wed, 2 Nov 2016 20:54:22 +0800 Subject: [PATCH 114/285] Set result "Succeed" for passed cases --- .../idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py index 35bb62c6d8..bfc8edeaa9 100644 --- a/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py +++ b/components/idf_test/unit_test/TestCaseScript/IDFUnitTest/UnitTest.py @@ -32,7 +32,7 @@ class UnitTest(PerformanceTCBase.PerformanceTCBase): time.sleep(1) #wait for test to run before reading result data += self.serial_read_data("UT1") if re.search('[^0] Tests 0 F', data): #check that number of tests run != 0 and number of tests failed == 0 - self.set_result("Success") + self.set_result("Succeed") break else: self.set_result("Fail") From eb2c633cbf4acabbb5822a294d39458ea6c8c81f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 3 Nov 2016 12:46:46 +0800 Subject: [PATCH 115/285] newlib: implement settimeofday, integrate LwIP SNTP, add SNTP example --- components/esp32/Kconfig | 6 +- components/lwip/Kconfig | 9 + components/lwip/include/lwip/port/lwipopts.h | 17 ++ components/newlib/time.c | 51 +++++- examples/06_sntp/Makefile | 9 + examples/06_sntp/README.md | 41 +++++ examples/06_sntp/main/Kconfig.projbuild | 17 ++ examples/06_sntp/main/component.mk | 10 ++ examples/06_sntp/main/sntp_main.c | 163 +++++++++++++++++++ 9 files changed, 314 insertions(+), 9 deletions(-) create mode 100644 examples/06_sntp/Makefile create mode 100644 examples/06_sntp/README.md create mode 100644 examples/06_sntp/main/Kconfig.projbuild create mode 100644 examples/06_sntp/main/component.mk create mode 100644 examples/06_sntp/main/sntp_main.c diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 928b190321..65cac4ee9f 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -324,7 +324,7 @@ choice ESP32_TIME_SYSCALL default ESP32_TIME_SYSCALL_USE_RTC_FRC1 help This setting defines which hardware timers are used to - implement 'gettimeofday' function in C library. + implement 'gettimeofday' and 'time' functions in C library. - If only FRC1 timer is used, gettimeofday will provide time at microsecond resolution. Time will not be preserved when going @@ -336,8 +336,8 @@ choice ESP32_TIME_SYSCALL deep sleep, but time will be measured at 6.(6) microsecond resolution. Also the gettimeofday function itself may take longer to run. - - If no timers are used, gettimeofday function return -1 and - set errno to ENOSYS. + - If no timers are used, gettimeofday and time functions + return -1 and set errno to ENOSYS. config ESP32_TIME_SYSCALL_USE_RTC bool "RTC" diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 15c94c66ba..801fa0b51c 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -24,6 +24,15 @@ config LWIP_SO_REUSE Enabling this option allows binding to a port which remains in TIME_WAIT. +config LWIP_DHCP_MAX_NTP_SERVERS + int "Maximum number of NTP servers" + default 1 + range 1 16 + help + Set maxumum number of NTP servers used by LwIP SNTP module. + First argument of sntp_setserver/sntp_setservername functions + is limited to this value. + endmenu diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index b970ae5539..f705887508 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -33,6 +33,8 @@ #define __LWIPOPTS_H__ #include +#include +#include #include "esp_task.h" #include "sdkconfig.h" @@ -552,7 +554,22 @@ extern unsigned char misc_prof_get_tcp_snd_buf(void); #define LWIP_NETCONN_FULLDUPLEX 1 #define LWIP_NETCONN_SEM_PER_THREAD 1 +#define LWIP_DHCP_MAX_NTP_SERVERS CONFIG_LWIP_DHCP_MAX_NTP_SERVERS +#define LWIP_TIMEVAL_PRIVATE 0 +#define SNTP_SET_SYSTEM_TIME_US(sec, us) \ + do { \ + struct timeval tv = { .tv_sec = sec, .tv_usec = us }; \ + settimeofday(&tv, NULL); \ + } while (0); + +#define SNTP_GET_SYSTEM_TIME(sec, us) \ + do { \ + struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; \ + gettimeofday(&tv, NULL); \ + (sec) = tv.tv_sec; \ + (us) = tv.tv_usec; \ + } while (0); #define SOC_SEND_LOG //printf diff --git a/components/newlib/time.c b/components/newlib/time.c index 83645aa4cc..5f60e1d7b2 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "esp_attr.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" @@ -54,6 +55,15 @@ static uint64_t get_rtc_time_us() #endif // WITH_RTC +// time from Epoch to the first boot time +#ifdef WITH_RTC +static RTC_DATA_ATTR struct timeval s_boot_time; +#else +static struct timeval s_boot_time; +#endif +static _lock_t s_boot_time_lock; + + #ifdef WITH_FRC1 #define FRC1_PRESCALER 16 #define FRC1_PRESCALER_CTL 2 @@ -83,7 +93,6 @@ void esp_setup_time_syscalls() s_microseconds = get_rtc_time_us(); #endif //WITH_RTC - // set up timer WRITE_PERI_REG(FRC_TIMER_CTRL_REG(0), \ FRC_TIMER_AUTOLOAD | \ @@ -112,12 +121,12 @@ clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) return (clock_t) tv.tv_sec; } -int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +static uint64_t get_time_since_boot() { - (void) tz; + uint64_t microseconds = 0; #ifdef WITH_FRC1 uint32_t timer_ticks_before = READ_PERI_REG(FRC_TIMER_COUNT_REG(0)); - uint64_t microseconds = s_microseconds; + microseconds = s_microseconds; uint32_t timer_ticks_after = READ_PERI_REG(FRC_TIMER_COUNT_REG(0)); if (timer_ticks_after > timer_ticks_before) { // overflow happened at some point between getting @@ -127,13 +136,22 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) } microseconds += (FRC_TIMER_LOAD_VALUE(0) - timer_ticks_after) / FRC1_TICKS_PER_US; #elif defined(WITH_RTC) - uint64_t microseconds = get_rtc_time_us(); + microseconds = get_rtc_time_us(); #endif + return microseconds; +} +int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) +{ + (void) tz; #if defined( WITH_FRC1 ) || defined( WITH_RTC ) + uint64_t microseconds = get_time_since_boot(); if (tv) { - tv->tv_sec = microseconds / 1000000; + _lock_acquire(&s_boot_time_lock); + microseconds += s_boot_time.tv_usec; + tv->tv_sec = s_boot_time.tv_sec + microseconds / 1000000; tv->tv_usec = microseconds % 1000000; + _lock_release(&s_boot_time_lock); } return 0; #else @@ -141,3 +159,24 @@ int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) return -1; #endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) } + +int settimeofday(const struct timeval *tv, const struct timezone *tz) +{ + (void) tz; +#if defined( WITH_FRC1 ) || defined( WITH_RTC ) + if (tv) { + _lock_acquire(&s_boot_time_lock); + uint64_t now = ((uint64_t) tv->tv_sec) * 1000000LL + tv->tv_usec; + uint64_t since_boot = get_time_since_boot(); + uint64_t boot_time = now - since_boot; + + s_boot_time.tv_sec = boot_time / 1000000; + s_boot_time.tv_usec = boot_time % 1000000; + _lock_release(&s_boot_time_lock); + } + return 0; +#else + __errno_r(r) = ENOSYS; + return -1; +#endif +} diff --git a/examples/06_sntp/Makefile b/examples/06_sntp/Makefile new file mode 100644 index 0000000000..e6ef17be14 --- /dev/null +++ b/examples/06_sntp/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := sntp + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/06_sntp/README.md b/examples/06_sntp/README.md new file mode 100644 index 0000000000..c5a153bbb2 --- /dev/null +++ b/examples/06_sntp/README.md @@ -0,0 +1,41 @@ +# Example: using LwIP SNTP module and time functions + +This example demonstrates the use of LwIP SNTP module to obtain time from Internet servers. See the README.md file in the upper level 'examples' directory for more information about examples. + +## Obtaining time using LwIP SNTP module + +When this example boots first time after ESP32 is reset, it connects to WiFi and obtains time using SNTP. +See `initialize_sntp` function for details. + +## Timekeeping + +Once time is synchronized, ESP32 will perform timekeeping using built-in timers. + +- RTC clock is used to maintain accurate time when chip is in deep sleep mode + +- FRC1 timer is used to provide time at microsecond accuracy when ESP32 is running. + +Timekeeping using RTC timer is demonstrated in this example by going into deep sleep mode. After wake up, ESP32 will print current time without connecting to WiFi. + +To use this functionality, make sure "Timers used for gettimeofday function" option in "ESP32-specific config" menu of menuconfig is set to "RTC and FRC1" or "RTC". + +## Working with time + +To get current time, [`gettimeofday`](http://man7.org/linux/man-pages/man2/gettimeofday.2.html) function may be used. Additionally the following [standard C library functions](http://en.cppreference.com/w/cpp/header/ctime) can be used to obtain time and manipulate it: + + gettimeofday + time + asctime + clock + ctime + difftime + gmtime + localtime + mktime + strftime + +To set time, [`settimeofday`](http://man7.org/linux/man-pages/man2/settimeofday.2.html) POSIX function can be used. It is used internally in LwIP SNTP library to set current time when response from NTP server is received. + +## Timezones + +To set local timezone, use [`setenv`](http://man7.org/linux/man-pages/man3/setenv.3.html) and [`tzset`](http://man7.org/linux/man-pages/man3/tzset.3.html) POSIX functions. First, call `setenv` to set `TZ` environment variable to the correct value depending on device location. Format of the time string is described in [libc documentation](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html). Next, call `tzset` to update C library runtime data for the new time zone. Once these steps are done, `localtime` function will return correct local time, taking time zone offset and daylight saving time into account. diff --git a/examples/06_sntp/main/Kconfig.projbuild b/examples/06_sntp/main/Kconfig.projbuild new file mode 100644 index 0000000000..c5d5523a9f --- /dev/null +++ b/examples/06_sntp/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + + Can be left blank if the network has no security set. + +endmenu \ No newline at end of file diff --git a/examples/06_sntp/main/component.mk b/examples/06_sntp/main/component.mk new file mode 100644 index 0000000000..24356f23ed --- /dev/null +++ b/examples/06_sntp/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/examples/06_sntp/main/sntp_main.c b/examples/06_sntp/main/sntp_main.c new file mode 100644 index 0000000000..7f516625e3 --- /dev/null +++ b/examples/06_sntp/main/sntp_main.c @@ -0,0 +1,163 @@ +/* LwIP SNTP example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "esp_attr.h" +#include "esp_deepsleep.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "apps/sntp/sntp.h" + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +static const char *TAG = "example"; + +/* Variable holding number of times ESP32 restarted since first boot. + * It is placed into RTC memory using RTC_DATA_ATTR and + * maintains its value when ESP32 wakes from deep sleep. + */ +RTC_DATA_ATTR static int boot_count = 0; + +static void obtain_time(void); +static void initialize_sntp(void); +static void initialise_wifi(void); +static esp_err_t event_handler(void *ctx, system_event_t *event); + + +void app_main() +{ + ++boot_count; + ESP_LOGI(TAG, "Boot count: %d", boot_count); + + time_t now; + struct tm timeinfo; + time(&now); + localtime_r(&now, &timeinfo); + // Is time set? If not, tm_year will be (1970 - 1900). + if (timeinfo.tm_year < (2016 - 1900)) { + ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP."); + obtain_time(); + // update 'now' variable with current time + time(&now); + } + char strftime_buf[64]; + + // Set timezone to Eastern Standard Time and print local time + setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0", 1); + tzset(); + localtime_r(&now, &timeinfo); + strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); + ESP_LOGI(TAG, "The current date/time in New York is: %s", strftime_buf); + + // Set timezone to China Standard Time + setenv("TZ", "CST-8CDT-9,M4.2.0/2,M9.2.0/3", 1); + tzset(); + localtime_r(&now, &timeinfo); + strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); + ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf); + + const int deep_sleep_sec = 10; + ESP_LOGI(TAG, "Entering deep sleep for %d seconds", deep_sleep_sec); + system_deep_sleep(1000000LL * deep_sleep_sec); +} + +static void obtain_time(void) +{ + nvs_flash_init(); + system_init(); + initialise_wifi(); + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, + false, true, portMAX_DELAY); + initialize_sntp(); + + // wait for time to be set + time_t now = 0; + struct tm timeinfo = { 0 }; + int retry = 0; + const int retry_count = 10; + while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) { + ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); + vTaskDelay(2000 / portTICK_PERIOD_MS); + time(&now); + localtime_r(&now, &timeinfo); + } +} + +static void initialize_sntp(void) +{ + ESP_LOGI(TAG, "Initializing SNTP"); + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, "pool.ntp.org"); + sntp_init(); +} + +static void initialise_wifi(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} From 299655e3be68212bd2317cce9c1d8f11bb6ffbf7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 3 Nov 2016 14:49:05 +0800 Subject: [PATCH 116/285] esp32: add choice for RTC clock source For now only one option is supported: internal RC oscillator --- components/esp32/Kconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 65cac4ee9f..1f04cf4bb7 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -349,5 +349,19 @@ config ESP32_TIME_SYSCALL_USE_NONE bool "None" endchoice +choice ESP32_RTC_CLOCK_SOURCE + prompt "RTC clock source" + default ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC + help + Choose which clock is used as RTC clock source. + The only available option for now is to use internal + 150kHz RC oscillator. + +config ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC + bool "Internal RC" +config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + bool "External 32kHz crystal" + depends on DOCUMENTATION_FOR_RTC_CNTL +endchoice endmenu From bc4f1c90a7cbf8f457df9cc97dc89d2e3e8d5a85 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 3 Nov 2016 17:44:23 +0800 Subject: [PATCH 117/285] conver tabs to spaces in frc_timer_reg.h --- components/esp32/include/soc/frc_timer_reg.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/components/esp32/include/soc/frc_timer_reg.h b/components/esp32/include/soc/frc_timer_reg.h index 24b942c0bb..d76199c4f3 100644 --- a/components/esp32/include/soc/frc_timer_reg.h +++ b/components/esp32/include/soc/frc_timer_reg.h @@ -23,26 +23,26 @@ #define REG_FRC_TIMER_BASE(i) (DR_REG_FRC_TIMER_BASE + i*0x20) -#define FRC_TIMER_LOAD_REG(i) (REG_FRC_TIMER_BASE(i) + 0x0) // timer load value (23 bit for i==0, 32 bit for i==1) +#define FRC_TIMER_LOAD_REG(i) (REG_FRC_TIMER_BASE(i) + 0x0) // timer load value (23 bit for i==0, 32 bit for i==1) #define FRC_TIMER_LOAD_VALUE(i) ((i == 0)?0x007FFFFF:0xffffffff) #define FRC_TIMER_LOAD_VALUE_S 0 -#define FRC_TIMER_COUNT_REG(i) (REG_FRC_TIMER_BASE(i) + 0x4) // timer count value (23 bit for i==0, 32 bit for i==1) +#define FRC_TIMER_COUNT_REG(i) (REG_FRC_TIMER_BASE(i) + 0x4) // timer count value (23 bit for i==0, 32 bit for i==1) #define FRC_TIMER_COUNT ((i == 0)?0x007FFFFF:0xffffffff) #define FRC_TIMER_COUNT_S 0 #define FRC_TIMER_CTRL_REG(i) (REG_FRC_TIMER_BASE(i) + 0x8) -#define FRC_TIMER_INT_ENABLE (BIT(8)) // enable interrupt -#define FRC_TIMER_ENABLE (BIT(7)) // enable timer -#define FRC_TIMER_AUTOLOAD (BIT(6)) // enable autoload -#define FRC_TIMER_PRESCALER 0x00000007 // 0: divide by 1, 2: divide by 16, 4: divide by 256 -#define FRC_TIMER_PRESCALER_S 1 -#define FRC_TIMER_EDGE_INT (BIT(0)) // 0: level, 1: edge +#define FRC_TIMER_INT_ENABLE (BIT(8)) // enable interrupt +#define FRC_TIMER_ENABLE (BIT(7)) // enable timer +#define FRC_TIMER_AUTOLOAD (BIT(6)) // enable autoload +#define FRC_TIMER_PRESCALER 0x00000007 // 0: divide by 1, 2: divide by 16, 4: divide by 256 +#define FRC_TIMER_PRESCALER_S 1 +#define FRC_TIMER_EDGE_INT (BIT(0)) // 0: level, 1: edge #define FRC_TIMER_INT_REG(i) (REG_FRC_TIMER_BASE(i) + 0xC) -#define FRC_TIMER_INT_CLR (BIT(0)) // clear interrupt +#define FRC_TIMER_INT_CLR (BIT(0)) // clear interrupt -#define FRC_TIMER_ALARM_REG(i) (REG_FRC_TIMER_BASE(i) + 0x10) // timer alarm value; register only present for i == 1 +#define FRC_TIMER_ALARM_REG(i) (REG_FRC_TIMER_BASE(i) + 0x10) // timer alarm value; register only present for i == 1 #define FRC_TIMER_ALARM 0xFFFFFFFF #define FRC_TIMER_ALARM_S 0 From 8282c73ac25a3b6e8895719eadab0eec92f1e1b3 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 3 Nov 2016 18:28:36 +0800 Subject: [PATCH 118/285] debug ring buffer error. --- components/driver/include/driver/uart.h | 2 +- components/driver/uart.c | 205 +++++++++++++++++------- components/freertos/ringbuf.c | 29 +++- 3 files changed, 177 insertions(+), 59 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 3ea77b2d02..445d71b685 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -530,7 +530,7 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * @param buf pointer to the buffer. * @param length data length - * @param ticks_to_wait: Timeout, count in RTOS ticks + * @param ticks_to_wait sTimeout, count in RTOS ticks * * @return * - (-1) Error diff --git a/components/driver/uart.c b/components/driver/uart.c index eeb2c64208..d6585405f2 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -37,8 +37,6 @@ const char* UART_TAG = "UART"; #define UART_EMPTY_THRESH_DEFAULT (10) #define UART_FULL_THRESH_DEFAULT (120) #define UART_TOUT_THRESH_DEFAULT (10) -#define UART_TX_TASK_DEPTH_DEFAULT (256*2+64) -#define UART_TX_TASK_PRIO_DEFAULT (10) #define UART_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) #define UART_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) @@ -60,7 +58,6 @@ typedef struct { RingbufHandle_t rx_ring_buf; int tx_buf_size; RingbufHandle_t tx_ring_buf; - TaskHandle_t tx_task_handle; bool buffer_full_flg; bool tx_waiting; int cur_remain; @@ -439,8 +436,17 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_dev_t* uart_reg = UART[uart_num]; uint8_t buf_idx = 0; uint32_t uart_intr_status = UART[uart_num]->int_st.val; - static int rx_fifo_len = 0; + int rx_fifo_len = 0; uart_event_t uart_event; + + static uint8_t * tx_ptr; + static uart_event_t* tx_head; + static int tx_len_tot = 0; + static int brk_flg = 0; + static int tx_brk_len = 0; + static int wait_brk = 0; + + portBASE_TYPE HPTaskAwoken = 0; while(uart_intr_status != 0x0) { buf_idx = 0; @@ -450,14 +456,99 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_ena.txfifo_empty = 0; uart_reg->int_clr.txfifo_empty = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + if(wait_brk) { + return; + } if(p_uart->tx_waiting == true) { p_uart->tx_waiting = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } + else { + int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt; + bool en_tx_flg = false; + if(tx_len_tot == 0) { + size_t size; +// ets_printf("dbg1,tot=0,get 1st head\n"); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + if(tx_head) { //enable empty intr +// tx_ptr = (uint8_t*)tx_head + sizeof(uart_event_t); + tx_ptr = NULL; +// en_tx_flg = true; + tx_len_tot = tx_head->data.size; + if(tx_head->type == UART_DATA_BREAK) { + tx_len_tot = tx_head->data.size; + brk_flg = 1; + tx_brk_len = tx_head->data.brk_len; + } +// ets_printf("ret1,tot: %d\n", tx_len_tot); + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + } + else { + return; + } + } + if(tx_ptr == NULL) { + size_t size; +// ets_printf("dbg2, tx ptr null, get 2nd tx ptr\n"); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + if(tx_ptr) { + tx_head = (void*) tx_ptr; +// ets_printf("get size: %d ; h size: %d\n", size, tx_len_tot); + en_tx_flg = true; + } else { + return; + } + } +// else + if(tx_len_tot > 0 && tx_ptr) { //tx + int send_len = tx_len_tot > tx_fifo_rem ? tx_fifo_rem : tx_len_tot; + for(buf_idx = 0; buf_idx < send_len; buf_idx++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(tx_ptr++) & 0xff); + } + tx_len_tot -= send_len; +// ets_printf("tot: %d\n", tx_len_tot); + if(tx_len_tot == 0) { + if(brk_flg == 1) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_brk_done = 0; + uart_reg->idle_conf.tx_brk_num = tx_brk_len; + uart_reg->conf0.txd_brk = 1; + uart_reg->int_clr.tx_brk_done = 1; + uart_reg->int_ena.tx_brk_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + wait_brk = 1; + } else { + en_tx_flg = true; + } +// ets_printf("ret2\n"); + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); +// xRingbufferPrintInfo(p_uart->tx_ring_buf); + tx_head = NULL; + tx_ptr = NULL; + } else { + en_tx_flg = true; + } + } + if(en_tx_flg) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_clr.txfifo_empty = 1; + uart_reg->int_ena.txfifo_empty = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + } + } } else if((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M)) { if(p_uart->buffer_full_flg == false) { //Get the buffer from the FIFO +// ESP_LOGE(UART_TAG, "FULL\n"); rx_fifo_len = uart_reg->status.rxfifo_cnt; p_uart->data_len = rx_fifo_len; memset(p_uart->data_buf, 0, sizeof(p_uart->data_buf)); @@ -506,12 +597,22 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.frm_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { +// ESP_LOGE(UART_TAG, "UART TX BRK DONE\n"); + ets_printf("tx brk done\n"); UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->conf0.txd_brk = 0; uart_reg->int_ena.tx_brk_done = 0; uart_reg->int_clr.tx_brk_done = 1; + if(brk_flg == 1) { + uart_reg->int_ena.txfifo_empty = 1; + } UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + if(brk_flg == 1) { + brk_flg = 0; + wait_brk = 0; + } else { + xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + } } else if(uart_intr_status & UART_TX_BRK_IDLE_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.tx_brk_idle_done = 0; @@ -638,26 +739,26 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool return original_size; } -static void uart_tx_task(void* arg) -{ - uart_obj_t* p_uart = (uart_obj_t*) arg; - size_t size; - uart_event_t evt; - for(;;) { - char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); - if(data == NULL) { - continue; - } - memcpy(&evt, data, sizeof(evt)); - if(evt.type == UART_DATA) { - uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); - } else if(evt.type == UART_DATA_BREAK) { - uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); - } - vRingbufferReturnItem(p_uart->tx_ring_buf, data); - } - vTaskDelete(NULL); -} +//static void uart_tx_task(void* arg) +//{ +// uart_obj_t* p_uart = (uart_obj_t*) arg; +// size_t size; +// uart_event_t evt; +// for(;;) { +// char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); +// if(data == NULL) { +// continue; +// } +// memcpy(&evt, data, sizeof(evt)); +// if(evt.type == UART_DATA) { +// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); +// } else if(evt.type == UART_DATA_BREAK) { +// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); +// } +// vRingbufferReturnItem(p_uart->tx_ring_buf, data); +// } +// vTaskDelete(NULL); +//} int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { @@ -666,19 +767,18 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) UART_CHECK(src, "buffer null"); if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { - uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); - if(evt == NULL) { - ESP_LOGE(UART_TAG, "UART EVT MALLOC ERROR\n"); - return -1; - } + uart_event_t evt; xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); - evt->type = UART_DATA; - evt->data.size = size; - memcpy(evt->data.data, src, size); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); - free(evt); - evt = NULL; + evt.type = UART_DATA; + evt.data.size = size; + ets_printf("-----1st send-----\n"); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); + ets_printf("====2nd send====\n"); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); + xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[0], SEND DIRECTLY\n"); @@ -698,19 +798,15 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { - uart_event_t *evt = (uart_event_t*) malloc(sizeof(uart_event_t) + size); - if(evt == NULL) { - return -1; - } + uart_event_t evt; xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); - evt->type = UART_DATA_BREAK; - evt->data.size = size; - evt->data.brk_len = brk_len; - memcpy(evt->data.data, src, size); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) evt, sizeof(uart_event_t) + size, portMAX_DELAY); - free(evt); - evt = NULL; + evt.type = UART_DATA_BREAK; + evt.data.size = size; + evt.data.brk_len = brk_len; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[1], SEND DIRECTLY\n"); @@ -782,8 +878,10 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp p_uart_obj[uart_num]->head_ptr = data; p_uart_obj[uart_num]->rd_ptr = data; p_uart_obj[uart_num]->cur_remain = size; +// ets_printf("dbg0\n"); } else { xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); +// ets_printf("dbg1\n"); return copy_len; } } @@ -792,17 +890,20 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp } else { len_tmp = p_uart_obj[uart_num]->cur_remain; } +// ets_printf("dbga\n"); memcpy(buf + copy_len, p_uart_obj[uart_num]->rd_ptr, len_tmp); p_uart_obj[uart_num]->rd_ptr += len_tmp; p_uart_obj[uart_num]->cur_remain -= len_tmp; copy_len += len_tmp; length -= len_tmp; +// ets_printf("dbgb\n"); if(p_uart_obj[uart_num]->cur_remain == 0) { vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); p_uart_obj[uart_num]->head_ptr = NULL; p_uart_obj[uart_num]->rd_ptr = NULL; if(p_uart_obj[uart_num]->buffer_full_flg) { BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); +// ets_printf("dbg2\n"); if(res == pdTRUE) { p_uart_obj[uart_num]->buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); @@ -810,6 +911,7 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp } } } +// ets_printf("dbg3\n"); xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } @@ -944,14 +1046,11 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->rx_buf_type = rx_buf_type; p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, rx_buf_type); if(tx_buffer_size > 0) { - p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT); + p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT);//RINGBUF_TYPE_BYTEBUF);//RINGBUF_TYPE_NOSPLIT); p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; - xTaskCreate(uart_tx_task, "uart_tx_task", UART_TX_TASK_DEPTH_DEFAULT, (void*)p_uart_obj[uart_num], UART_TX_TASK_PRIO_DEFAULT, &p_uart_obj[uart_num]->tx_task_handle); - } else { p_uart_obj[uart_num]->tx_ring_buf = NULL; p_uart_obj[uart_num]->tx_buf_size = 0; - p_uart_obj[uart_num]->tx_task_handle = NULL; } } else { ESP_LOGE(UART_TAG, "UART driver already installed\n"); @@ -986,10 +1085,6 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) uart_disable_tx_intr(uart_num); uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); - if(p_uart_obj[uart_num]->tx_task_handle) { - vTaskDelete(p_uart_obj[uart_num]->tx_task_handle); - p_uart_obj[uart_num]->tx_task_handle = NULL; - } if(p_uart_obj[uart_num]->tx_fifo_sem) { vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); p_uart_obj[uart_num]->tx_fifo_sem = NULL; diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index ce5504596a..560eb5fdd9 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -77,9 +77,13 @@ static int ringbufferFreeMem(ringbuf_t *rb) { int free_size = rb->free_ptr-rb->write_ptr; if (free_size <= 0) free_size += rb->size; + //If we free the last dummy item in the buffer, free_ptr will point to rb->data + //In this case, after we write the last some bytes, the buffer might wrap around if we don't have room for a header anymore. +// if (free_size == 0 && rb->read_ptr == rb->write_ptr) free_size += rb->size; //Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation - //where read_ptr == free_ptr, messing up the next calculation. - return free_size-1; + //where write_ptr == free_ptr, messing up the next calculation. +// return free_size == 0 ? 0 : free_size - 1; + return free_size - 1; } @@ -334,6 +338,10 @@ static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wan //can be increase. //This function by itself is not threadsafe, always call from within a muxed section. static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { + ets_printf("in returnItemToRingbufDefault\n"); + xRingbufferPrintInfo(rb); + + uint8_t *data=(uint8_t*)item; configASSERT(((int)rb->free_ptr&3)==0); configASSERT(data >= rb->data); @@ -350,9 +358,16 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { hdr=(buf_entry_hdr_t *)rb->free_ptr; //basically forward free_ptr until we run into either a block that is still in use or the write pointer. while (((hdr->flags & iflag_free) || (hdr->flags & iflag_dummydata)) && rb->free_ptr != rb->write_ptr) { + if (hdr->flags & iflag_dummydata) { + ets_printf("hrd len: %d; flg: 0x%02x\n",hdr->len,hdr->flags); //Rest is dummy data. Reset to start of ringbuffer. rb->free_ptr=rb->data; + //If the read_ptr is pointing to this dummy item, + //we should also move the read pointer to data, in case we overwrite the read hdr. +// if(rb->read_ptr == (uint8_t*)hdr) { +// rb->read_ptr = rb->data; +// } } else { //Skip past item size_t len=(hdr->len+3)&~3; @@ -363,8 +378,10 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { if ((rb->data+rb->size)-rb->free_ptr < sizeof(buf_entry_hdr_t)) { rb->free_ptr=rb->data; } + if(rb->free_ptr == rb->read_ptr) break; //Next header hdr=(buf_entry_hdr_t *)rb->free_ptr; + } } @@ -386,6 +403,12 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf) configASSERT(rb); ets_printf("Rb size %d free %d rptr %d freeptr %d wptr %d\n", rb->size, ringbufferFreeMem(rb), rb->read_ptr-rb->data, rb->free_ptr-rb->data, rb->write_ptr-rb->data); + buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr; + if(rb->write_ptr == rb->read_ptr) { + ets_printf("write que read\n"); + } else { + ets_printf("hdr len: %d; flg: 0x%08x\n", hdr->len, hdr->flags); + } } @@ -493,7 +516,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, ticks_to_wait = ticks_end - xTaskGetTickCount(); } } while (ringbufferFreeMem(rb) < needed_size && ticks_to_wait>=0); - + //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry From 6602d6b7f2c25ad72da581f038cc27e474ea9d0b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 1 Sep 2016 12:32:05 +0800 Subject: [PATCH 119/285] docs: add style guide This adds initial code style guide. Only section on code formatting is written, other sections to be added later. Also adds scripts to format code using astyle. --- docs/style-guide.rst | 182 ++++++++++++++++++++++++++++++++++++++++ tools/format-minimal.sh | 9 ++ tools/format.sh | 12 +++ 3 files changed, 203 insertions(+) create mode 100644 docs/style-guide.rst create mode 100644 tools/format-minimal.sh create mode 100644 tools/format.sh diff --git a/docs/style-guide.rst b/docs/style-guide.rst new file mode 100644 index 0000000000..971d86bc91 --- /dev/null +++ b/docs/style-guide.rst @@ -0,0 +1,182 @@ +Espressif IoT Development Framework Style Guide +=============================================== + + +About this guide +~~~~~~~~~~~~~~~~ + +Purpose of this style guide is to encourage use of common coding practices within the ESP-IDF. + +Style guide is a set of rules which are aimed to help create readable, maintainable, and robust code. By writing code which looks the same way across the code base we help others read and comprehend the code. By using same conventions for spaces and newlines we reduce chances that future changes will produce huge unreadable diffs. By following common patterns for module structure and by using language features consistently we help others understand code behavior. + +We try to keep rules simple enough, which means that they can not cover all potential cases. In some cases one has to bend these simple rules to achieve readability, maintainability, or robustness. + +When doing modifications to third-party code used in ESP-IDF, follow the way that particular project is written. That will help propose useful changes for merging into upstream project. + +C code formatting +~~~~~~~~~~~~~~~~~ + +Indentation +----------- + +Use 4 spaces for each indentation level. Don't use tabs for indentation. Configure the editor to emit 4 spaces each time you press tab key. + +Vertical space +-------------- + +Place one empty line between functions. Don't begin or end a function with an empty line. +:: + + void function1() + { + do_one_thing(); + do_another_thing(); + // INCORRECT, don't place empty line here + } + // place empty line here + void function2() + { + // INCORRECT, don't use an empty line here + int var = 0; + while (var < SOME_CONSTANT) { + do_stuff(&var); + } + } + +Horizontal space +---------------- + +Always add single space after conditional and loop keywords:: + + if (condition) { // correct + // ... + } + + switch (n) { // correct + case 0: + // ... + } + + for(int i = 0; i < CONST; ++i) { // INCORRECT + // ... + } + +Add single space around binary operators. No space is necessary for unary operators. It is okay to drop space around multiply and divide operators:: + + const int y = y0 + (x - x0) * (y1 - y0) / (x1 - x0); // correct + + const int y = y0 + (x - x0)*(y1 - y0)/(x1 - x0); // also okay + + int y_cur = -y; // correct + ++y_cur; + + const int y = y0+(x-x0)*(y1-y0)/(x1-x0); // INCORRECT + + +No space is necessary around ``.`` and ``->`` operators. + + +Sometimes adding horizontal space within a line can help make code more readable. For example, you can add space to align function arguments:: + + gpio_matrix_in(PIN_CAM_D6, I2S0I_DATA_IN14_IDX, false); + gpio_matrix_in(PIN_CAM_D7, I2S0I_DATA_IN15_IDX, false); + gpio_matrix_in(PIN_CAM_HREF, I2S0I_H_ENABLE_IDX, false); + gpio_matrix_in(PIN_CAM_PCLK, I2S0I_DATA_IN15_IDX, false); + +Note however that if someone goes to add new line with a longer identifier as first argument (e.g. ``PIN_CAM_VSYNC``), it will not fit. So other lines would have to be realigned, adding meaningless changes to the commit. + +Therefore, use horizontal alignment sparingly, especially if you expect new lines to be added to the list later. + +Never use TAB characters for horizontal alignment. + +Never add trailing whitespace at the end of the line. + + +Braces +------ + +- Function definition should have a brace on a separate line:: + + // This is correct: + void function(int arg) + { + + } + + // NOT like this: + void function(int arg) { + + } + +- Within a function, place opening brace on the same line with conditional and loop statements:: + + if (condition) { + do_one(); + } + else if (other_condition) { + do_two(); + } + + +Comments +-------- + +Use ``//`` for single line comments. For multi-line comments it is okay to use either ``//`` on each line or a ``/* */`` block. + +Although not directly related to formatting, here are a few notes about using comments effectively. + +- Don't use single comments to disable some functionality:: + + void init_something() + { + setup_dma(); + // load_resources(); // WHY is this thing commented, asks the reader? + start_timer(); + } + +- If some code is no longer required, remove it completely. If you need it you can always look it up in git history of this file. If you disable some call because of temporary reasons, with an intention to restore it in the future, add explanation on the adjacent line:: + + void init_something() + { + setup_dma(); + // TODO: we should load resources here, but loader is not fully integrated yet. + // load_resources(); + start_timer(); + } + +- Same goes for ``#if 0 ... #endif`` blocks. Remove code block completely if it is not used. Otherwise, add comment explaining why the block is disabled. Don't use ``#if 0 ... #endif`` or comments to store code snippets which you may need in the future. + +- Don't add trivial comments about authorship and change date. You can always look up who modified any given line using git. E.g. this comment adds clutter to the code without adding any useful information:: + + void init_something() + { + setup_dma(); + // XXX add 2016-09-01 + init_dma_list(); + fill_dma_item(0); + // end XXX add + start_timer(); + } + + +Formatting your code +-------------------- + +You can use ``astyle`` program to format your code according to the above recommendations. + +If you are writing a file from scratch, or doing a complete rewrite, feel free to re-format the entire file. If you are changing a small portion of file, don't re-format the code you didn't change. This will help others when they review your changes. + +To re-format a file, run:: + + tools/format.sh components/my_component/file.c + +Structure and naming +~~~~~~~~~~~~~~~~~~~~ + +To be written. + +Language features +~~~~~~~~~~~~~~~~~ + +To be written. + diff --git a/tools/format-minimal.sh b/tools/format-minimal.sh new file mode 100644 index 0000000000..ff829247d3 --- /dev/null +++ b/tools/format-minimal.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Runs astyle with parameters which should be checked in a pre-commit hook +astyle \ + --style=otbs \ + --indent=spaces=4 \ + --convert-tabs \ + --keep-one-line-statements \ + --pad-header \ + "$@" diff --git a/tools/format.sh b/tools/format.sh new file mode 100644 index 0000000000..aadb337aaa --- /dev/null +++ b/tools/format.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Runs astyle with the full set of formatting options +astyle \ + --style=otbs \ + --indent=spaces=4 \ + --convert-tabs \ + --align-pointer=name \ + --align-reference=name \ + --keep-one-line-statements \ + --pad-header \ + --pad-oper \ + "$@" From b717e44eb2bbb51b61b656d4c976c45adcdf22e3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 7 Sep 2016 12:45:08 +0800 Subject: [PATCH 120/285] tools: add code formatter rules for Eclipse Generated with Eclipse Neon, should work in earlier versions as well. This file can be imported - into the workspace via Preferences > C++ > Code Style > Formatter > Import - or into the project via Project Preferences > C++ General > Formatter > Import Configuration options in Eclipse are not exactly the same as in astyle. There may be some discrepancy between the format astyle and Eclipse will produce. If anyone notices that, please let me know. --- tools/eclipse-code-style.xml | 167 +++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 tools/eclipse-code-style.xml diff --git a/tools/eclipse-code-style.xml b/tools/eclipse-code-style.xml new file mode 100644 index 0000000000..f589a00800 --- /dev/null +++ b/tools/eclipse-code-style.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4d3ed9efdee93dd97325a05942c4bc2a5f69d80c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 3 Nov 2016 19:07:41 +0800 Subject: [PATCH 121/285] docs: update style guide --- docs/style-guide.rst | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 971d86bc91..4fa8321762 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -3,7 +3,7 @@ Espressif IoT Development Framework Style Guide About this guide -~~~~~~~~~~~~~~~~ +---------------- Purpose of this style guide is to encourage use of common coding practices within the ESP-IDF. @@ -14,15 +14,15 @@ We try to keep rules simple enough, which means that they can not cover all pote When doing modifications to third-party code used in ESP-IDF, follow the way that particular project is written. That will help propose useful changes for merging into upstream project. C code formatting -~~~~~~~~~~~~~~~~~ +----------------- Indentation ------------ +^^^^^^^^^^^ Use 4 spaces for each indentation level. Don't use tabs for indentation. Configure the editor to emit 4 spaces each time you press tab key. Vertical space --------------- +^^^^^^^^^^^^^^ Place one empty line between functions. Don't begin or end a function with an empty line. :: @@ -44,7 +44,7 @@ Place one empty line between functions. Don't begin or end a function with an em } Horizontal space ----------------- +^^^^^^^^^^^^^^^^ Always add single space after conditional and loop keywords:: @@ -93,7 +93,7 @@ Never add trailing whitespace at the end of the line. Braces ------- +^^^^^^ - Function definition should have a brace on a separate line:: @@ -112,14 +112,13 @@ Braces if (condition) { do_one(); - } - else if (other_condition) { + } else if (other_condition) { do_two(); } Comments --------- +^^^^^^^^ Use ``//`` for single line comments. For multi-line comments it is okay to use either ``//`` on each line or a ``/* */`` block. @@ -160,7 +159,7 @@ Although not directly related to formatting, here are a few notes about using co Formatting your code --------------------- +^^^^^^^^^^^^^^^^^^^^ You can use ``astyle`` program to format your code according to the above recommendations. @@ -170,13 +169,18 @@ To re-format a file, run:: tools/format.sh components/my_component/file.c -Structure and naming -~~~~~~~~~~~~~~~~~~~~ +Documenting code +---------------- + +Please see the guide here: `Documenting Code `_. + +Structure and naming +-------------------- + -To be written. Language features -~~~~~~~~~~~~~~~~~ +----------------- To be written. From e314f42b0c71fd96913e0f4f8b79d4e16e69982a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 3 Nov 2016 20:18:30 +0800 Subject: [PATCH 122/285] nvs: fix Page::findItem and Storage::findItem regression When read caching was added, Page::findItem started modifying itemIndex reference argument even if item wasn't found. Incidentally, Storage::findItem reused itemIndex when starting search at next page. So, - if the first page had a cached index (findItem was called for that page), and it pointed to a non-zero index, - first page has a few empty items at the end (but is marked full), - next search looked up the item on the second page, - index of the item on the second page was less than the cached index on the first page, then the search would fail because cached starting index was reused. This change fixes both sides of the problem: - Page::findItem shouldn't modify itemIndex argument if item is not found - Storage::findItem should not reuse itemIndex between pages Two tests have been added. --- components/nvs_flash/src/nvs_page.cpp | 11 ++--- components/nvs_flash/src/nvs_storage.cpp | 2 +- components/nvs_flash/test/test_nvs.cpp | 54 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index 23cefd1aad..d2ca225352 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -656,19 +656,20 @@ esp_err_t Page::findItem(uint8_t nsIndex, ItemType datatype, const char* key, si if (mState == PageState::CORRUPT || mState == PageState::INVALID || mState == PageState::UNINITIALIZED) { return ESP_ERR_NVS_NOT_FOUND; } - - if (itemIndex >= ENTRY_COUNT) { + + size_t findBeginIndex = itemIndex; + if (findBeginIndex >= ENTRY_COUNT) { return ESP_ERR_NVS_NOT_FOUND; } CachedFindInfo findInfo(nsIndex, datatype, key); if (mFindInfo == findInfo) { - itemIndex = mFindInfo.itemIndex(); + findBeginIndex = mFindInfo.itemIndex(); } size_t start = mFirstUsedEntry; - if (itemIndex > mFirstUsedEntry && itemIndex < ENTRY_COUNT) { - start = itemIndex; + if (findBeginIndex > mFirstUsedEntry && findBeginIndex < ENTRY_COUNT) { + start = findBeginIndex; } size_t end = mNextFreeEntry; diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index eb90cac5bc..cacfbd4022 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -71,8 +71,8 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item) { - size_t itemIndex = 0; for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { + size_t itemIndex = 0; auto err = it->findItem(nsIndex, datatype, key, itemIndex, item); if (err == ESP_OK) { page = it; diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index 528c9df686..81bf7fd216 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -300,6 +300,27 @@ TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]") CHECK(page.findItem(1, itemTypeOf(), "bar") == ESP_OK); } +TEST_CASE("storage can find items on second page if first is not fully written and has cached search data", "[nvs]") +{ + SpiFlashEmulator emu(3); + Storage storage; + CHECK(storage.init(0, 3) == ESP_OK); + int bar = 0; + uint8_t bigdata[100 * 32] = {0}; + // write one big chunk of data + ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "first", bigdata, sizeof(bigdata))); + + // write second one; it will not fit into the first page + ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "second", bigdata, sizeof(bigdata))); + + size_t size; + ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "first", size)); + CHECK(size == sizeof(bigdata)); + ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "second", size)); + CHECK(size == sizeof(bigdata)); +} + + TEST_CASE("can write and read variable length data lots of times", "[nvs]") { SpiFlashEmulator emu(8); @@ -1055,6 +1076,39 @@ TEST_CASE("crc error in variable length item is handled", "[nvs]") } +TEST_CASE("read/write failure (TW8406)", "[nvs]") +{ + SpiFlashEmulator emu(3); + nvs_flash_init_custom(0, 3); + for (int attempts = 0; attempts < 3; ++attempts) { + int i = 0; + nvs_handle light_handle = 0; + char key[15] = {0}; + char data[76] = {12, 13, 14, 15, 16}; + uint8_t number = 20; + size_t data_len = sizeof(data); + + ESP_ERROR_CHECK(nvs_open("LIGHT", NVS_READWRITE, &light_handle)); + ESP_ERROR_CHECK(nvs_set_u8(light_handle, "RecordNum", number)); + for (i = 0; i < number; ++i) { + sprintf(key, "light%d", i); + ESP_ERROR_CHECK(nvs_set_blob(light_handle, key, data, sizeof(data))); + } + nvs_commit(light_handle); + + uint8_t get_number = 0; + ESP_ERROR_CHECK(nvs_get_u8(light_handle, "RecordNum", &get_number)); + REQUIRE(number == get_number); + for (i = 0; i < number; ++i) { + char data[76] = {0}; + sprintf(key, "light%d", i); + ESP_ERROR_CHECK(nvs_get_blob(light_handle, key, data, &data_len)); + } + nvs_close(light_handle); + } +} + + TEST_CASE("dump all performance data", "[nvs]") { std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl; From e96d4a0a3289ec2c57f5f446026f6ba74176ae73 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Tue, 4 Oct 2016 16:07:06 -0400 Subject: [PATCH 123/285] Allow OS X to build without libintl --- tools/kconfig/lxdialog/check-lxdialog.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/kconfig/lxdialog/check-lxdialog.sh b/tools/kconfig/lxdialog/check-lxdialog.sh index 6964d2b0c5..48c19278a9 100755 --- a/tools/kconfig/lxdialog/check-lxdialog.sh +++ b/tools/kconfig/lxdialog/check-lxdialog.sh @@ -5,8 +5,8 @@ ldflags() { if [ $(uname -s) == "Darwin" ]; then - #OSX seems to need intl too - echo -n "-lintl " + #OSX seems to need ncurses too + echo -n "-lncurses" fi pkg-config --libs ncursesw 2>/dev/null && exit pkg-config --libs ncurses 2>/dev/null && exit @@ -41,6 +41,10 @@ ccflags() else echo '-DCURSES_LOC=""' fi + if [ $(uname -s) == "Darwin" ]; then + #OSX doesn't have libintl + echo -n "-DKBUILD_NO_NLS" + fi } # Temp file, try to clean up after us From 9ed7c4f8bc7f0c8d1f26bf68877b95099b6703ef Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 3 Nov 2016 23:30:54 +0800 Subject: [PATCH 124/285] fix ringbuffer bug. --- components/driver/include/driver/uart.h | 3 +- components/driver/uart.c | 427 ++++++++++++------------ components/freertos/ringbuf.c | 30 +- 3 files changed, 214 insertions(+), 246 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 445d71b685..0d9ab3c563 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -437,13 +437,12 @@ esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf * @param queue_size UART event queue size/depth. * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. - * @param buf_type UART RX ring_buffer type * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue); /** * @brief Uninstall UART driver. diff --git a/components/driver/uart.c b/components/driver/uart.c index d6585405f2..9eaf783438 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -43,28 +43,35 @@ const char* UART_TAG = "UART"; #define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) typedef struct { - uart_port_t uart_num; - SemaphoreHandle_t tx_fifo_sem; - SemaphoreHandle_t tx_mutex; - SemaphoreHandle_t tx_buffer_mutex; - SemaphoreHandle_t tx_done_sem; - SemaphoreHandle_t tx_brk_sem; - SemaphoreHandle_t rx_mux; - QueueHandle_t xQueueUart; - int queue_size; - int intr_num; - int rx_buf_size; - ringbuf_type_t rx_buf_type; - RingbufHandle_t rx_ring_buf; - int tx_buf_size; - RingbufHandle_t tx_ring_buf; - bool buffer_full_flg; - bool tx_waiting; - int cur_remain; - uint8_t* rd_ptr; - uint8_t* head_ptr; - uint8_t data_buf[UART_FIFO_LEN]; - uint8_t data_len; + uart_port_t uart_num; /*!< UART port number*/ + int queue_size; /*!< UART event queue size*/ + QueueHandle_t xQueueUart; /*!< UART queue handler*/ + int intr_num; /*!< UART interrupt number*/ + //rx parameters + SemaphoreHandle_t rx_mux; /*!< UART RX data mutex*/ + int rx_buf_size; /*!< RX ring buffer size */ + RingbufHandle_t rx_ring_buf; /*!< RX ring buffer handler*/ + bool rx_buffer_full_flg; /*!< RX ring buffer full flag. */ + int rx_cur_remain; /*!< Data number that waiting to be read out in ring buffer item*/ + uint8_t* rx_ptr; /*!< pointer to the current data in ring buffer*/ + uint8_t* rx_head_ptr; /*!< pointer to the head of RX item*/ + uint8_t rx_data_buf[UART_FIFO_LEN]; /*!< Data buffer to stash FIFO data*/ + uint8_t rx_stash_len; /*!< stashed data length.(When using flow control, after reading out FIFO data, if we fail to push to buffer, we can just stash them.) */ + //tx parameters + SemaphoreHandle_t tx_fifo_sem; /*!< UART TX FIFO semaphore*/ + SemaphoreHandle_t tx_mux; /*!< UART TX mutex*/ + SemaphoreHandle_t tx_buffer_mux; /*!< UART TX buffer semaphore*/ + SemaphoreHandle_t tx_done_sem; /*!< UART TX done semaphore*/ + SemaphoreHandle_t tx_brk_sem; /*!< UART TX send break done semaphore*/ + int tx_buf_size; /*!< TX ring buffer size */ + RingbufHandle_t tx_ring_buf; /*!< TX ring buffer handler*/ + bool tx_waiting_fifo; /*!< this flag indicates that some task is waiting for FIFO empty interrupt, used to send all data without any data buffer*/ + uint8_t* tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/ + uart_event_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ + uint32_t tx_len_tot; /*!< Total length of current item in ring buffer*/ + uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */ + uint8_t tx_brk_len; /*!< TX break signal cycle length/number */ + uint8_t tx_waiting_brk; /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/ } uart_obj_t; static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; @@ -438,16 +445,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uint32_t uart_intr_status = UART[uart_num]->int_st.val; int rx_fifo_len = 0; uart_event_t uart_event; - - static uint8_t * tx_ptr; - static uart_event_t* tx_head; - static int tx_len_tot = 0; - static int brk_flg = 0; - static int tx_brk_len = 0; - static int wait_brk = 0; - - portBASE_TYPE HPTaskAwoken = 0; + while(uart_intr_status != 0x0) { buf_idx = 0; uart_event.type = UART_EVENT_MAX; @@ -456,85 +455,92 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_ena.txfifo_empty = 0; uart_reg->int_clr.txfifo_empty = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - if(wait_brk) { + if(p_uart->tx_waiting_brk) { return; } - if(p_uart->tx_waiting == true) { - p_uart->tx_waiting = false; + if(p_uart->tx_waiting_fifo == true) { + p_uart->tx_waiting_fifo = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } else { + //We don't use TX ring buffer, because the size if zero. + if(p_uart->tx_buf_size == 0) { + return; + } int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt; bool en_tx_flg = false; - if(tx_len_tot == 0) { - size_t size; -// ets_printf("dbg1,tot=0,get 1st head\n"); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - if(tx_head) { //enable empty intr -// tx_ptr = (uint8_t*)tx_head + sizeof(uart_event_t); - tx_ptr = NULL; -// en_tx_flg = true; - tx_len_tot = tx_head->data.size; - if(tx_head->type == UART_DATA_BREAK) { - tx_len_tot = tx_head->data.size; - brk_flg = 1; - tx_brk_len = tx_head->data.brk_len; + //We need to put a loop here, in case all the buffer items are very short. + //That would cause a watch_dog reset because empty interrupt happens so often. + //Although this is a loop in ISR, this loop will execute at most 128 turns. + while(tx_fifo_rem) { + if(p_uart->tx_len_tot == 0) { + size_t size; + //The first item is the data description + //Get the first item to get the data information + p_uart->tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + if(p_uart->tx_head) { + p_uart->tx_ptr = NULL; + p_uart->tx_len_tot = p_uart->tx_head->data.size; + if(p_uart->tx_head->type == UART_DATA_BREAK) { + p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_brk_flg = 1; + p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + } + //We have saved the data description from the 1st item, return buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + } + else { + //Can not get data from ring buffer, return; + return; } -// ets_printf("ret1,tot: %d\n", tx_len_tot); - vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); } - else { - return; - } - } - if(tx_ptr == NULL) { - size_t size; -// ets_printf("dbg2, tx ptr null, get 2nd tx ptr\n"); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); - -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - if(tx_ptr) { - tx_head = (void*) tx_ptr; -// ets_printf("get size: %d ; h size: %d\n", size, tx_len_tot); - en_tx_flg = true; - } else { - return; - } - } -// else - if(tx_len_tot > 0 && tx_ptr) { //tx - int send_len = tx_len_tot > tx_fifo_rem ? tx_fifo_rem : tx_len_tot; - for(buf_idx = 0; buf_idx < send_len; buf_idx++) { - WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(tx_ptr++) & 0xff); - } - tx_len_tot -= send_len; -// ets_printf("tot: %d\n", tx_len_tot); - if(tx_len_tot == 0) { - if(brk_flg == 1) { - UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); - uart_reg->int_ena.tx_brk_done = 0; - uart_reg->idle_conf.tx_brk_num = tx_brk_len; - uart_reg->conf0.txd_brk = 1; - uart_reg->int_clr.tx_brk_done = 1; - uart_reg->int_ena.tx_brk_done = 1; - UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - wait_brk = 1; + if(p_uart->tx_ptr == NULL) { + size_t size; + //2nd item is the data we need to send through UART + //Get 2nd item from ring buffer + p_uart->tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + if(p_uart->tx_ptr) { + //Update the TX item head, we will need this to return item to buffer. + p_uart->tx_head = (void*) p_uart->tx_ptr; + en_tx_flg = true; } else { + //Can not get data from ring buffer, return; + return; + } + } + if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr) { + //To fill the TX FIFO. + int send_len = p_uart->tx_len_tot > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_tot; + for(buf_idx = 0; buf_idx < send_len; buf_idx++) { + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(p_uart->tx_ptr++) & 0xff); + } + p_uart->tx_len_tot -= send_len; + tx_fifo_rem -= send_len; + if(p_uart->tx_len_tot == 0) { + //Sending item done, now we need to send break if there is a record. + //Return item to ring buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + p_uart->tx_head = NULL; + p_uart->tx_ptr = NULL; + //Set TX break signal after FIFO is empty + if(p_uart->tx_brk_flg == 1) { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + uart_reg->int_ena.tx_brk_done = 0; + uart_reg->idle_conf.tx_brk_num = p_uart->tx_brk_len; + uart_reg->conf0.txd_brk = 1; + uart_reg->int_clr.tx_brk_done = 1; + uart_reg->int_ena.tx_brk_done = 1; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + p_uart->tx_waiting_brk = 1; + return; + } else { + //enable TX empty interrupt + en_tx_flg = true; + } + } else { + //enable TX empty interrupt en_tx_flg = true; } -// ets_printf("ret2\n"); - vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, tx_head, &HPTaskAwoken); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); -// xRingbufferPrintInfo(p_uart->tx_ring_buf); - tx_head = NULL; - tx_ptr = NULL; - } else { - en_tx_flg = true; } } if(en_tx_flg) { @@ -546,14 +552,13 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } } else if((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M) || (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M)) { - if(p_uart->buffer_full_flg == false) { + if(p_uart->rx_buffer_full_flg == false) { //Get the buffer from the FIFO -// ESP_LOGE(UART_TAG, "FULL\n"); rx_fifo_len = uart_reg->status.rxfifo_cnt; - p_uart->data_len = rx_fifo_len; - memset(p_uart->data_buf, 0, sizeof(p_uart->data_buf)); + p_uart->rx_stash_len = rx_fifo_len; + //We have to read out all data in RX FIFO to clear the interrupt signal while(buf_idx < rx_fifo_len) { - p_uart->data_buf[buf_idx++] = uart_reg->fifo.rw_byte; + p_uart->rx_data_buf[buf_idx++] = uart_reg->fifo.rw_byte; } //After Copying the Data From FIFO ,Clear intr_status UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); @@ -562,12 +567,14 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_DATA; uart_event.data.size = rx_fifo_len; - if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->data_buf, p_uart->data_len, &HPTaskAwoken)) { + //If we fail to push data to ring buffer, we will have to stash the data, and send next time. + //Mainly for applications that uses flow control or small ring buffer. + if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->rx_data_buf, p_uart->rx_stash_len, &HPTaskAwoken)) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.rxfifo_full = 0; uart_reg->int_ena.rxfifo_tout = 0; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - p_uart->buffer_full_flg = true; + p_uart->rx_buffer_full_flg = true; uart_event.type = UART_BUFFER_FULL; } else { uart_event.type = UART_DATA; @@ -597,19 +604,17 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.frm_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { -// ESP_LOGE(UART_TAG, "UART TX BRK DONE\n"); - ets_printf("tx brk done\n"); UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->conf0.txd_brk = 0; uart_reg->int_ena.tx_brk_done = 0; uart_reg->int_clr.tx_brk_done = 1; - if(brk_flg == 1) { + if(p_uart->tx_brk_flg == 1) { uart_reg->int_ena.txfifo_empty = 1; } UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); - if(brk_flg == 1) { - brk_flg = 0; - wait_brk = 0; + if(p_uart->tx_brk_flg == 1) { + p_uart->tx_brk_flg = 0; + p_uart->tx_waiting_brk = 0; } else { xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); } @@ -644,8 +649,8 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); BaseType_t res; portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; - //Take tx_mutex - res = xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)ticks_to_wait); + //Take tx_mux + res = xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)ticks_to_wait); if(res == pdFALSE) { return ESP_ERR_TIMEOUT; } @@ -653,7 +658,7 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, 0); ticks_to_wait = ticks_end - xTaskGetTickCount(); if(UART[uart_num]->status.txfifo_cnt == 0) { - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return ESP_OK; } uart_enable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); @@ -661,10 +666,10 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) res = xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, (portTickType)ticks_to_wait); if(res == pdFALSE) { uart_disable_intr_mask(uart_num, UART_TX_DONE_INT_ENA_M); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return ESP_ERR_TIMEOUT; } - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return ESP_OK; } @@ -701,9 +706,9 @@ int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) if(len == 0) { return 0; } - xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); int tx_len = uart_fill_fifo(uart_num, buffer, len); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return tx_len; } @@ -716,14 +721,14 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool return 0; } //lock for uart_tx - xSemaphoreTake(p_uart_obj[uart_num]->tx_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); size_t original_size = size; while(size) { //semaphore for tx_fifo available if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { size_t sent = uart_fill_fifo(uart_num, (char*) src, size); if(sent < size) { - p_uart_obj[uart_num]->tx_waiting = true; + p_uart_obj[uart_num]->tx_waiting_fifo = true; uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); } size -= sent; @@ -735,49 +740,25 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); } xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return original_size; } -//static void uart_tx_task(void* arg) -//{ -// uart_obj_t* p_uart = (uart_obj_t*) arg; -// size_t size; -// uart_event_t evt; -// for(;;) { -// char* data = (char*) xRingbufferReceive(p_uart->tx_ring_buf, &size, portMAX_DELAY); -// if(data == NULL) { -// continue; -// } -// memcpy(&evt, data, sizeof(evt)); -// if(evt.type == UART_DATA) { -// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 0, 0); -// } else if(evt.type == UART_DATA_BREAK) { -// uart_tx_all(p_uart->uart_num, (const char*) data + sizeof(uart_event_t), evt.data.size, 1, evt.data.brk_len); -// } -// vRingbufferReturnItem(p_uart->tx_ring_buf, data); -// } -// vTaskDelete(NULL); -//} - int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error"); UART_CHECK(src, "buffer null"); + //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); evt.type = UART_DATA; evt.data.size = size; - ets_printf("-----1st send-----\n"); xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); - ets_printf("====2nd send====\n"); xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xRingbufferPrintInfo(p_uart_obj[uart_num]->tx_ring_buf); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { @@ -785,6 +766,7 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) return uart_tx_all(uart_num, src, size, 0, 0); } } else { + //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 0, 0); } } @@ -796,16 +778,17 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s UART_CHECK((size > 0), "uart size error"); UART_CHECK((src), "uart data null"); UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); + //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mutex, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); evt.type = UART_DATA_BREAK; evt.data.size = size; evt.data.brk_len = brk_len; xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mutex); + xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); return size; } else { @@ -813,6 +796,7 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s return uart_tx_all(uart_num, src, size, 1, brk_len); } } else { + //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 1, brk_len); } } @@ -828,29 +812,29 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { return -1; } - if(p_uart_obj[uart_num]->cur_remain == 0) { + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { ticks_to_wait = ticks_end - xTaskGetTickCount(); data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { - p_uart_obj[uart_num]->head_ptr = data; - p_uart_obj[uart_num]->rd_ptr = data; - p_uart_obj[uart_num]->cur_remain = size; + p_uart_obj[uart_num]->rx_head_ptr = data; + p_uart_obj[uart_num]->rx_ptr = data; + p_uart_obj[uart_num]->rx_cur_remain = size; } else { xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return -1; } } - val = *(p_uart_obj[uart_num]->rd_ptr); - p_uart_obj[uart_num]->rd_ptr++; - p_uart_obj[uart_num]->cur_remain--; - if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->rd_ptr = NULL; - if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); + val = *(p_uart_obj[uart_num]->rx_ptr); + p_uart_obj[uart_num]->rx_ptr++; + p_uart_obj[uart_num]->rx_cur_remain--; + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_head_ptr); + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ptr = NULL; + if(p_uart_obj[uart_num]->rx_buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); if(res == pdTRUE) { - p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->rx_buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); } } @@ -872,46 +856,40 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp return -1; } while(length) { - if(p_uart_obj[uart_num]->cur_remain == 0) { + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); if(data) { - p_uart_obj[uart_num]->head_ptr = data; - p_uart_obj[uart_num]->rd_ptr = data; - p_uart_obj[uart_num]->cur_remain = size; -// ets_printf("dbg0\n"); + p_uart_obj[uart_num]->rx_head_ptr = data; + p_uart_obj[uart_num]->rx_ptr = data; + p_uart_obj[uart_num]->rx_cur_remain = size; } else { xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); -// ets_printf("dbg1\n"); return copy_len; } } - if(p_uart_obj[uart_num]->cur_remain > length) { + if(p_uart_obj[uart_num]->rx_cur_remain > length) { len_tmp = length; } else { - len_tmp = p_uart_obj[uart_num]->cur_remain; + len_tmp = p_uart_obj[uart_num]->rx_cur_remain; } -// ets_printf("dbga\n"); - memcpy(buf + copy_len, p_uart_obj[uart_num]->rd_ptr, len_tmp); - p_uart_obj[uart_num]->rd_ptr += len_tmp; - p_uart_obj[uart_num]->cur_remain -= len_tmp; + memcpy(buf + copy_len, p_uart_obj[uart_num]->rx_ptr, len_tmp); + p_uart_obj[uart_num]->rx_ptr += len_tmp; + p_uart_obj[uart_num]->rx_cur_remain -= len_tmp; copy_len += len_tmp; length -= len_tmp; -// ets_printf("dbgb\n"); - if(p_uart_obj[uart_num]->cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->head_ptr); - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->rd_ptr = NULL; - if(p_uart_obj[uart_num]->buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->data_buf, p_uart_obj[uart_num]->data_len, 1); -// ets_printf("dbg2\n"); + if(p_uart_obj[uart_num]->rx_cur_remain == 0) { + vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_head_ptr); + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ptr = NULL; + if(p_uart_obj[uart_num]->rx_buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); if(res == pdTRUE) { - p_uart_obj[uart_num]->buffer_full_flg = false; + p_uart_obj[uart_num]->rx_buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); } } } } -// ets_printf("dbg3\n"); xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); return copy_len; } @@ -926,11 +904,11 @@ esp_err_t uart_flush(uart_port_t uart_num) //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); while(true) { - if(p_uart->head_ptr) { - vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->head_ptr); - p_uart->rd_ptr = NULL; - p_uart->cur_remain = 0; - p_uart->head_ptr = NULL; + if(p_uart->rx_head_ptr) { + vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); + p_uart->rx_ptr = NULL; + p_uart->rx_cur_remain = 0; + p_uart->rx_head_ptr = NULL; } data = (uint8_t*) xRingbufferReceive(p_uart->rx_ring_buf, &size, (portTickType) 0); if(data == NULL) { @@ -938,19 +916,24 @@ esp_err_t uart_flush(uart_port_t uart_num) } vRingbufferReturnItem(p_uart->rx_ring_buf, data); } - p_uart->rd_ptr = NULL; - p_uart->cur_remain = 0; - p_uart->head_ptr = NULL; + p_uart->rx_ptr = NULL; + p_uart->rx_cur_remain = 0; + p_uart->rx_head_ptr = NULL; xSemaphoreGive(p_uart->rx_mux); - xSemaphoreTake(p_uart->tx_mutex, (portTickType)portMAX_DELAY); - do { - data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); - if(data == NULL) { - break; - } - vRingbufferReturnItem(p_uart->rx_ring_buf, data); - } while(1); - xSemaphoreGive(p_uart->tx_mutex); + + xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); + if(p_uart->tx_buf_size > 0) { + xSemaphoreTake(p_uart->tx_buffer_mux, (portTickType)portMAX_DELAY); + do { + data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); + if(data == NULL) { + break; + } + vRingbufferReturnItem(p_uart->rx_ring_buf, data); + } while(1); + xSemaphoreGive(p_uart->tx_buffer_mux); + } + xSemaphoreGive(p_uart->tx_mux); uart_wait_tx_done(uart_num, portMAX_DELAY); uart_reset_fifo(uart_num); return ESP_OK; @@ -1009,7 +992,7 @@ int uart_get_print_port() return s_uart_print_nport; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue, ringbuf_type_t rx_buf_type) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n"); @@ -1025,11 +1008,16 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary(); p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); - p_uart_obj[uart_num]->tx_mutex = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->tx_buffer_mutex = xSemaphoreCreateMutex(); + p_uart_obj[uart_num]->tx_mux = xSemaphoreCreateMutex(); p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); p_uart_obj[uart_num]->intr_num = uart_intr_num; p_uart_obj[uart_num]->queue_size = queue_size; + p_uart_obj[uart_num]->tx_ptr = NULL; + p_uart_obj[uart_num]->tx_head = NULL; + p_uart_obj[uart_num]->tx_len_tot = 0; + p_uart_obj[uart_num]->tx_brk_flg = 0; + p_uart_obj[uart_num]->tx_brk_len = 0; + p_uart_obj[uart_num]->tx_waiting_brk = 0; if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); @@ -1038,19 +1026,20 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b } else { p_uart_obj[uart_num]->xQueueUart = NULL; } - p_uart_obj[uart_num]->buffer_full_flg = false; - p_uart_obj[uart_num]->tx_waiting = false; - p_uart_obj[uart_num]->rd_ptr = NULL; - p_uart_obj[uart_num]->cur_remain = 0; - p_uart_obj[uart_num]->head_ptr = NULL; - p_uart_obj[uart_num]->rx_buf_type = rx_buf_type; - p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, rx_buf_type); + p_uart_obj[uart_num]->rx_buffer_full_flg = false; + p_uart_obj[uart_num]->tx_waiting_fifo = false; + p_uart_obj[uart_num]->rx_ptr = NULL; + p_uart_obj[uart_num]->rx_cur_remain = 0; + p_uart_obj[uart_num]->rx_head_ptr = NULL; + p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, RINGBUF_TYPE_BYTEBUF); if(tx_buffer_size > 0) { p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT);//RINGBUF_TYPE_BYTEBUF);//RINGBUF_TYPE_NOSPLIT); p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; + p_uart_obj[uart_num]->tx_buffer_mux = xSemaphoreCreateMutex(); } else { p_uart_obj[uart_num]->tx_ring_buf = NULL; p_uart_obj[uart_num]->tx_buf_size = 0; + p_uart_obj[uart_num]->tx_buffer_mux = NULL; } } else { ESP_LOGE(UART_TAG, "UART driver already installed\n"); @@ -1097,13 +1086,13 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) vSemaphoreDelete(p_uart_obj[uart_num]->tx_brk_sem); p_uart_obj[uart_num]->tx_brk_sem = NULL; } - if(p_uart_obj[uart_num]->tx_mutex) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_mutex); - p_uart_obj[uart_num]->tx_mutex = NULL; + if(p_uart_obj[uart_num]->tx_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_mux); + p_uart_obj[uart_num]->tx_mux = NULL; } - if(p_uart_obj[uart_num]->tx_buffer_mutex) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mutex); - p_uart_obj[uart_num]->tx_buffer_mutex = NULL; + if(p_uart_obj[uart_num]->tx_buffer_mux) { + vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mux); + p_uart_obj[uart_num]->tx_buffer_mux = NULL; } if(p_uart_obj[uart_num]->rx_mux) { vSemaphoreDelete(p_uart_obj[uart_num]->rx_mux); diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 560eb5fdd9..a4205d88dc 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -77,13 +77,9 @@ static int ringbufferFreeMem(ringbuf_t *rb) { int free_size = rb->free_ptr-rb->write_ptr; if (free_size <= 0) free_size += rb->size; - //If we free the last dummy item in the buffer, free_ptr will point to rb->data - //In this case, after we write the last some bytes, the buffer might wrap around if we don't have room for a header anymore. -// if (free_size == 0 && rb->read_ptr == rb->write_ptr) free_size += rb->size; //Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation - //where write_ptr == free_ptr, messing up the next calculation. -// return free_size == 0 ? 0 : free_size - 1; - return free_size - 1; + //where read_ptr == free_ptr, messing up the next calculation. + return free_size-1; } @@ -338,10 +334,6 @@ static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wan //can be increase. //This function by itself is not threadsafe, always call from within a muxed section. static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { - ets_printf("in returnItemToRingbufDefault\n"); - xRingbufferPrintInfo(rb); - - uint8_t *data=(uint8_t*)item; configASSERT(((int)rb->free_ptr&3)==0); configASSERT(data >= rb->data); @@ -358,16 +350,9 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { hdr=(buf_entry_hdr_t *)rb->free_ptr; //basically forward free_ptr until we run into either a block that is still in use or the write pointer. while (((hdr->flags & iflag_free) || (hdr->flags & iflag_dummydata)) && rb->free_ptr != rb->write_ptr) { - if (hdr->flags & iflag_dummydata) { - ets_printf("hrd len: %d; flg: 0x%02x\n",hdr->len,hdr->flags); //Rest is dummy data. Reset to start of ringbuffer. rb->free_ptr=rb->data; - //If the read_ptr is pointing to this dummy item, - //we should also move the read pointer to data, in case we overwrite the read hdr. -// if(rb->read_ptr == (uint8_t*)hdr) { -// rb->read_ptr = rb->data; -// } } else { //Skip past item size_t len=(hdr->len+3)&~3; @@ -378,10 +363,11 @@ static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) { if ((rb->data+rb->size)-rb->free_ptr < sizeof(buf_entry_hdr_t)) { rb->free_ptr=rb->data; } + //The free_ptr can not exceed read_ptr, otherwise write_ptr might overwrite read_ptr. + //Read_ptr can not set to rb->data with free_ptr, otherwise write_ptr might wrap around to rb->data. if(rb->free_ptr == rb->read_ptr) break; //Next header hdr=(buf_entry_hdr_t *)rb->free_ptr; - } } @@ -403,12 +389,6 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf) configASSERT(rb); ets_printf("Rb size %d free %d rptr %d freeptr %d wptr %d\n", rb->size, ringbufferFreeMem(rb), rb->read_ptr-rb->data, rb->free_ptr-rb->data, rb->write_ptr-rb->data); - buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr; - if(rb->write_ptr == rb->read_ptr) { - ets_printf("write que read\n"); - } else { - ets_printf("hdr len: %d; flg: 0x%08x\n", hdr->len, hdr->flags); - } } @@ -516,7 +496,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, ticks_to_wait = ticks_end - xTaskGetTickCount(); } } while (ringbufferFreeMem(rb) < needed_size && ticks_to_wait>=0); - + //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry From a6b3be6734b8f8acd13a8f401d046f3531b7a893 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 4 Nov 2016 02:48:25 +0800 Subject: [PATCH 125/285] Update UART driver 1. fix bug in ringbuffer.c: When return an dummy item, free_ptr might exceed rd_ptr, so the write_ptr would overwrite rd_ptr in this case. 2. Delete UART tx task in buffer mode. UART ISR will copy the data from tx buffer to fifo. --- components/driver/include/driver/uart.h | 110 +++++++-- components/driver/uart.c | 285 ++++++++++++------------ 2 files changed, 237 insertions(+), 158 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 0d9ab3c563..200d1148cb 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -87,8 +87,8 @@ typedef struct { uart_word_length_t data_bits; /*!< UART byte size*/ uart_parity_t parity; /*!< UART parity mode*/ uart_stop_bits_t stop_bits; /*!< UART stop bits*/ - uart_hw_flowcontrol_t flow_ctrl; /*!< UART hw flow control mode(cts/rts)*/ - uint8_t rx_flow_ctrl_thresh ; /*!< UART hw RTS threshold*/ + uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode(cts/rts)*/ + uint8_t rx_flow_ctrl_thresh ; /*!< UART HW RTS threshold*/ } uart_config_t; typedef struct { @@ -124,6 +124,7 @@ typedef struct { * @brief Set UART data bits. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param data_bit UART data bits * * @return @@ -147,6 +148,7 @@ esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bi * @brief Set UART stop bits. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param bit_num UART stop bits * * @return @@ -170,6 +172,7 @@ esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); * @brief Set UART parity. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param parity_mode the enum of uart parity configuration * * @return @@ -194,6 +197,7 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); * @brief Set UART baud rate. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param baud_rate UART baud-rate. * * @return @@ -216,7 +220,9 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** * @brief Set UART line inverse mode + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param inverse_mask Choose the wires that need to be inversed. * * (inverse_mask should be chosen from uart_inverse_t, combine with OR-OPERATION) @@ -232,7 +238,9 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; * @brief Set hardware flow control. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param flow_ctrl Hardware flow control mode + * * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * * @return @@ -243,6 +251,7 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ /** * @brief Get hardware flow control mode + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return @@ -255,6 +264,7 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo * @brief Clear UART interrupt status * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param clr_mask Bit mask of the status that to be cleared. * * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) @@ -269,6 +279,7 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); * @brief Set UART interrupt enable * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param enable_mask Bit mask of the enable bits. * * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) @@ -283,6 +294,7 @@ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); * @brief Clear UART interrupt enable bits * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param disable_mask Bit mask of the disable bits. * * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) @@ -331,7 +343,9 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param enable 1: enable; 0: disable + * * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * * @return @@ -342,13 +356,16 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); /** * @brief register UART interrupt handler(ISR). +* @note * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * * @param fn Interrupt handler function. * @attention * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. @@ -364,9 +381,13 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* * @brief Set UART pin number * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param tx_io_num UART TX pin GPIO number + * * @param rx_io_num UART RX pin GPIO number + * * @param rts_io_num UART RTS pin GPIO number + * * @param cts_io_num UART CTS pin GPIO number * * @return @@ -380,6 +401,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r * UART rx hardware flow control should not be set. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param level 1: RTS output low(active); 0: RTS output high(block) * * @return @@ -392,6 +414,7 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level); * @brief UART set DTR level (before inverse) * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param level 1: DTR output low; 0: DTR output high * * @return @@ -404,6 +427,7 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); * @brief UART parameter configure * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param uart_config UART parameter settings * * @return @@ -416,6 +440,7 @@ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); * @brief UART interrupt configure * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param p_intr_conf UART interrupt settings * * @return @@ -431,12 +456,19 @@ esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param rx_buffer_size UART RX ring buffer size - * @param tx_buffer_size UART TX ring buffer size, if set to zero, driver will not use TX buffer and TX task. - * @param queue_size UART event queue size/depth. + * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param rx_buffer_size UART RX ring buffer size + * + * @param tx_buffer_size UART TX ring buffer size. + * + * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. + * + * @param queue_size UART event queue size/depth. + * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * + * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. * * @return * - ESP_OK Success @@ -459,6 +491,7 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); * @brief Wait UART TX FIFO empty * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param ticks_to_wait Timeout, count in RTOS ticks * * @return @@ -473,7 +506,9 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param buffer data buffer address + * * @param len data length to send * * @return @@ -484,10 +519,17 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); /** * @brief Send data to the UART port from a given buffer and length, + * + * If parameter tx_buffer_size is set to zero: * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. * + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param src data buffer address + * * @param size data length to send * * @return @@ -498,23 +540,37 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); /** * @brief Send data to the UART port from a given buffer and length, + * + * If parameter tx_buffer_size is set to zero: * This function will not return until all the data and the break signal have been sent out. + * After all data send out, send a break signal. + * + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * After all data send out, send a break signal. + * + * * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param src data buffer address + * * @param size data length to send + * * @param brk_len break signal length (unit: one bit's time@current_baudrate) * * @return * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ + int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** * @brief UART read one char * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param ticks_to_wait Timeout, count in RTOS ticks * * @return @@ -527,10 +583,14 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); * @brief UART read bytes from UART buffer * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * * @param buf pointer to the buffer. + * * @param length data length + * * @param ticks_to_wait sTimeout, count in RTOS ticks * + * * @return * - (-1) Error * - Others return a char data from uart fifo. @@ -588,14 +648,17 @@ int uart_get_print_port(void); * uart_param_config(uart_num, &uart_config); * //b1. Setup UART driver(with UART queue) * QueueHandle_t uart_queue; - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);//parameters here are just an example + * //parameters here are just an example, tx buffer size is 2048 + * uart_driver_install(uart_num, 1024 * 2, 1024 * 2, 10, UART_INTR_NUM, &uart_queue); * //b2. Setup UART driver(without UART queue) - * uart_driver_install(uart_num, 1024 * 2, 10, UART_INTR_NUM, NULL); //parameters here are just an example + * //parameters here are just an example, tx buffer size is 0 + * uart_driver_install(uart_num, 1024 * 2, 0, 10, UART_INTR_NUM, NULL); *@endcode *-----------------------------------------------------------------------------* * @code{c} * //2. Set UART pin - * uart_set_pin(uart_num, -1, -1, 15, 13); //set UART pin, not needed if use default pins. + * //set UART pin, not needed if use default pins. + * uart_set_pin(uart_num, -1, -1, 15, 13); * @endcode *-----------------------------------------------------------------------------* * @code{c} @@ -629,15 +692,20 @@ int uart_get_print_port(void); * .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, * .rx_flow_ctrl_thresh = 122, * }; - * uart_param_config(uart_num, &uart_config); //Config UART1 parameters - * uart_set_pin(uart_num, 16, 17, 18, 19); //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) - * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); //Set UART log level + * //Configure UART1 parameters + * uart_param_config(uart_num, &uart_config); + * //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) + * uart_set_pin(uart_num, 16, 17, 18, 19); + * //Set UART log level + * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); * //Install UART driver( We don't need an event queue here) * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF); * uint8_t data[1000]; * while(1) { - * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); //Read data from UART - * uart_tx_all_chars(uart_num, (const char*)data, len); //Write data back to UART + * //Read data from UART + * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); + * //Write data back to UART + * uart_tx_all_chars(uart_num, (const char*)data, len); * } * } * @endcode @@ -704,12 +772,16 @@ int uart_get_print_port(void); * .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, * .rx_flow_ctrl_thresh = 122, * }; - * uart_param_config(uart_num, &uart_config); //Set UART parameters - * uart_set_pin(uart_num, -1, -1, 15, 13); //Set UART pins,(-1: default pin, no change.) - * esp_log_level_set(UART_TAG, ESP_LOG_INFO); //Set UART log level + * //Set UART parameters + * uart_param_config(uart_num, &uart_config); + * //Set UART pins,(-1: default pin, no change.) + * uart_set_pin(uart_num, -1, -1, 15, 13); + * //Set UART log level + * esp_log_level_set(UART_TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); - * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); //Create a task to handler UART event from ISR + * //Create a task to handler UART event from ISR + * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); * } * @endcode * diff --git a/components/driver/uart.c b/components/driver/uart.c index 9eaf783438..cab05dd0be 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -30,9 +30,9 @@ #include "soc/uart_struct.h" const char* UART_TAG = "UART"; -#define UART_CHECK(a, str) if (!(a)) { \ +#define UART_CHECK(a, str, ret) if (!(a)) { \ ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ - return ESP_FAIL; \ + return (ret); \ } #define UART_EMPTY_THRESH_DEFAULT (10) #define UART_FULL_THRESH_DEFAULT (120) @@ -42,6 +42,7 @@ const char* UART_TAG = "UART"; #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) #define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + typedef struct { uart_port_t uart_num; /*!< UART port number*/ int queue_size; /*!< UART event queue size*/ @@ -60,7 +61,6 @@ typedef struct { //tx parameters SemaphoreHandle_t tx_fifo_sem; /*!< UART TX FIFO semaphore*/ SemaphoreHandle_t tx_mux; /*!< UART TX mutex*/ - SemaphoreHandle_t tx_buffer_mux; /*!< UART TX buffer semaphore*/ SemaphoreHandle_t tx_done_sem; /*!< UART TX done semaphore*/ SemaphoreHandle_t tx_brk_sem; /*!< UART TX send break done semaphore*/ int tx_buf_size; /*!< TX ring buffer size */ @@ -69,6 +69,7 @@ typedef struct { uint8_t* tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/ uart_event_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ uint32_t tx_len_tot; /*!< Total length of current item in ring buffer*/ + uint32_t tx_len_cur; uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */ uint8_t tx_brk_len; /*!< TX break signal cycle length/number */ uint8_t tx_waiting_brk; /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/ @@ -80,8 +81,8 @@ static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.bit_num = data_bit; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -90,15 +91,15 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); *(data_bit) = UART[uart_num]->conf0.bit_num; return ESP_OK; } esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((stop_bit < UART_STOP_BITS_MAX), "stop bit error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((stop_bit < UART_STOP_BITS_MAX), "stop bit error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.stop_bit_num = stop_bit; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -107,14 +108,14 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t stop_bit) esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); (*stop_bit) = UART[uart_num]->conf0.stop_bit_num; return ESP_OK; } esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.parity = parity_mode & 0x1; UART[uart_num]->conf0.parity_en = (parity_mode >> 1) & 0x1; @@ -124,7 +125,7 @@ esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode) esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); int val = UART[uart_num]->conf0.val; if(val & UART_PARITY_EN_M) { if(val & UART_PARITY_M) { @@ -140,8 +141,8 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode) esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((baud_rate < UART_BITRATE_MAX), "baud_rate error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((baud_rate < UART_BITRATE_MAX), "baud_rate error", ESP_FAIL); uint32_t clk_div = (((UART_CLK_FREQ) << 4) / baud_rate); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->clk_div.div_int = clk_div >> 4; @@ -152,7 +153,7 @@ esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate) esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); uint32_t clk_div = (UART[uart_num]->clk_div.div_int << 4) | UART[uart_num]->clk_div.div_frag; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -162,8 +163,8 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate) esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((((inverse_mask & UART_LINE_INV_MASK) == 0) && (inverse_mask != 0)), "inverse_mask error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((((inverse_mask & UART_LINE_INV_MASK) == 0) && (inverse_mask != 0)), "inverse_mask error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); CLEAR_PERI_REG_MASK(UART_CONF0_REG(uart_num), UART_LINE_INV_MASK); SET_PERI_REG_MASK(UART_CONF0_REG(uart_num), inverse_mask); @@ -174,9 +175,9 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask) //only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((rx_thresh < UART_FIFO_LEN), "rx flow thresh error"); - UART_CHECK((flow_ctrl < UART_HW_FLOWCTRL_MAX), "hw_flowctrl mode error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((rx_thresh < UART_FIFO_LEN), "rx flow thresh error", ESP_FAIL); + UART_CHECK((flow_ctrl < UART_HW_FLOWCTRL_MAX), "hw_flowctrl mode error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); if(flow_ctrl & UART_HW_FLOWCTRL_RTS) { UART[uart_num]->conf1.rx_flow_thrhd = rx_thresh; @@ -195,7 +196,7 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); uart_hw_flowcontrol_t val = UART_HW_FLOWCTRL_DISABLE; if(UART[uart_num]->conf1.rx_flow_en) { val |= UART_HW_FLOWCTRL_RTS; @@ -209,7 +210,7 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo static esp_err_t uart_reset_fifo(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.rxfifo_rst = 1; UART[uart_num]->conf0.rxfifo_rst = 0; @@ -221,7 +222,7 @@ static esp_err_t uart_reset_fifo(uart_port_t uart_num) esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); //intr_clr register is write-only UART[uart_num]->int_clr.val = clr_mask; return ESP_OK; @@ -229,7 +230,7 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask) esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); SET_PERI_REG_MASK(UART_INT_CLR_REG(uart_num), enable_mask); SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), enable_mask); @@ -239,7 +240,7 @@ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask) esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), disable_mask); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -248,7 +249,7 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) esp_err_t uart_enable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -257,7 +258,7 @@ esp_err_t uart_enable_rx_intr(uart_port_t uart_num) esp_err_t uart_disable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -266,7 +267,7 @@ esp_err_t uart_disable_rx_intr(uart_port_t uart_num) esp_err_t uart_disable_tx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_ena.txfifo_empty = 0; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -275,8 +276,8 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num) esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((thresh < UART_FIFO_LEN), "empty intr threshold error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((thresh < UART_FIFO_LEN), "empty intr threshold error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.txfifo_empty = 1; UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V; @@ -288,7 +289,7 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); ESP_INTR_DISABLE(uart_intr_num); switch(uart_num) { @@ -313,11 +314,11 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* //only one GPIO pad can connect with input signal esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error"); - UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error"); - UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error"); - UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((tx_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(tx_io_num))), "tx_io_num error", ESP_FAIL); + UART_CHECK((rx_io_num < 0 || (GPIO_IS_VALID_GPIO(rx_io_num))), "rx_io_num error", ESP_FAIL); + UART_CHECK((rts_io_num < 0 || (GPIO_IS_VALID_OUTPUT_GPIO(rts_io_num))), "rts_io_num error", ESP_FAIL); + UART_CHECK((cts_io_num < 0 || (GPIO_IS_VALID_GPIO(cts_io_num))), "cts_io_num error", ESP_FAIL); int tx_sig, rx_sig, rts_sig, cts_sig; switch(uart_num) { @@ -373,8 +374,8 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r esp_err_t uart_set_rts(uart_port_t uart_num, int level) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.sw_rts = level & 0x1; UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); @@ -383,7 +384,7 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level) esp_err_t uart_set_dtr(uart_port_t uart_num, int level) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.sw_dtr = level & 0x1; UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); @@ -392,8 +393,8 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level) esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((uart_config), "param null\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((uart_config), "param null\n", ESP_FAIL); if(uart_num == UART_NUM_0) { periph_module_enable(PERIPH_UART0_MODULE); } else if(uart_num == UART_NUM_1) { @@ -414,8 +415,8 @@ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_intr_conf), "param null\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_intr_conf), "param null\n", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.val = UART_INTR_MASK; if(p_intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { @@ -458,7 +459,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(p_uart->tx_waiting_brk) { return; } - if(p_uart->tx_waiting_fifo == true) { + //TX semaphore used in none tx ringbuffer mode. + if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size > 0) { p_uart->tx_waiting_fifo = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } @@ -473,57 +475,51 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) //That would cause a watch_dog reset because empty interrupt happens so often. //Although this is a loop in ISR, this loop will execute at most 128 turns. while(tx_fifo_rem) { - if(p_uart->tx_len_tot == 0) { + if(p_uart->tx_len_tot == 0 || p_uart->tx_ptr == NULL || p_uart->tx_len_cur == 0) { size_t size; - //The first item is the data description - //Get the first item to get the data information p_uart->tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); if(p_uart->tx_head) { - p_uart->tx_ptr = NULL; - p_uart->tx_len_tot = p_uart->tx_head->data.size; - if(p_uart->tx_head->type == UART_DATA_BREAK) { + //The first item is the data description + //Get the first item to get the data information + if(p_uart->tx_len_tot == 0) { + p_uart->tx_ptr = NULL; p_uart->tx_len_tot = p_uart->tx_head->data.size; - p_uart->tx_brk_flg = 1; - p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + if(p_uart->tx_head->type == UART_DATA_BREAK) { + p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_brk_flg = 1; + p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + } + //We have saved the data description from the 1st item, return buffer. + vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + }else if(p_uart->tx_ptr == NULL) { + //Update the TX item pointer, we will need this to return item to buffer. + p_uart->tx_ptr = (uint8_t*) p_uart->tx_head; + en_tx_flg = true; + p_uart->tx_len_cur = size; } - //We have saved the data description from the 1st item, return buffer. - vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); } else { //Can not get data from ring buffer, return; return; } } - if(p_uart->tx_ptr == NULL) { - size_t size; - //2nd item is the data we need to send through UART - //Get 2nd item from ring buffer - p_uart->tx_ptr = (uint8_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); - if(p_uart->tx_ptr) { - //Update the TX item head, we will need this to return item to buffer. - p_uart->tx_head = (void*) p_uart->tx_ptr; - en_tx_flg = true; - } else { - //Can not get data from ring buffer, return; - return; - } - } - if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr) { + if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr && p_uart->tx_len_cur > 0) { //To fill the TX FIFO. - int send_len = p_uart->tx_len_tot > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_tot; + int send_len = p_uart->tx_len_cur > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_cur; for(buf_idx = 0; buf_idx < send_len; buf_idx++) { WRITE_PERI_REG(UART_FIFO_AHB_REG(uart_num), *(p_uart->tx_ptr++) & 0xff); } p_uart->tx_len_tot -= send_len; + p_uart->tx_len_cur -= send_len; tx_fifo_rem -= send_len; - if(p_uart->tx_len_tot == 0) { - //Sending item done, now we need to send break if there is a record. + if(p_uart->tx_len_cur == 0) { //Return item to ring buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); p_uart->tx_head = NULL; p_uart->tx_ptr = NULL; + //Sending item done, now we need to send break if there is a record. //Set TX break signal after FIFO is empty - if(p_uart->tx_brk_flg == 1) { + if(p_uart->tx_brk_flg == 1 && p_uart->tx_len_tot == 0) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.tx_brk_done = 0; uart_reg->idle_conf.tx_brk_num = p_uart->tx_brk_len; @@ -645,8 +641,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) /**************************************************************/ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_FAIL); BaseType_t res; portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; //Take tx_mux @@ -700,9 +696,9 @@ static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - UART_CHECK(buffer, "buffer null"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); + UART_CHECK(buffer, "buffer null", (-1)); if(len == 0) { return 0; } @@ -714,9 +710,6 @@ int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool brk_en, int brk_len) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - UART_CHECK(src, "buffer null"); if(size == 0) { return 0; } @@ -746,25 +739,28 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error"); - UART_CHECK(src, "buffer null"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error", (-1)); + UART_CHECK(src, "buffer null", (-1)); //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { - if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size + sizeof(uart_event_t))) { - uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); - evt.type = UART_DATA; - evt.data.size = size; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return size; - } else { - ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[0], SEND DIRECTLY\n"); - return uart_tx_all(uart_num, src, size, 0, 0); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); + int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); + int ori_size = size; + int offset = 0; + uart_event_t evt; + evt.type = UART_DATA; + evt.data.size = size; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + while(size > 0) { + int send_size = size > max_size / 2 ? max_size / 2 : size; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); + size -= send_size; + offset += send_size; } + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + return ori_size; } else { //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 0, 0); @@ -773,28 +769,31 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); - UART_CHECK((size > 0), "uart size error"); - UART_CHECK((src), "uart data null"); - UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); + UART_CHECK((size > 0), "uart size error", (-1)); + UART_CHECK((src), "uart data null", (-1)); + UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error", (-1)); //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { - if(xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf) > (size)) { - uart_event_t evt; - xSemaphoreTake(p_uart_obj[uart_num]->tx_buffer_mux, (portTickType)portMAX_DELAY); - evt.type = UART_DATA_BREAK; - evt.data.size = size; - evt.data.brk_len = brk_len; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) src, size, portMAX_DELAY); - xSemaphoreGive(p_uart_obj[uart_num]->tx_buffer_mux); - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return size; - } else { - ESP_LOGW(UART_TAG, "UART TX BUFFER TOO SMALL[1], SEND DIRECTLY\n"); - return uart_tx_all(uart_num, src, size, 1, brk_len); + xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); + int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); + int ori_size = size; + int offset = 0; + uart_event_t evt; + evt.type = UART_DATA_BREAK; + evt.data.size = size; + evt.data.brk_len = brk_len; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + while(size > 0) { + int send_size = size > max_size / 2 ? max_size / 2 : size; + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); + size -= send_size; + offset += send_size; } + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + return ori_size; } else { //Send data without TX ring buffer, the task will block until all data have been sent out return uart_tx_all(uart_num, src, size, 1, brk_len); @@ -803,8 +802,8 @@ int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t s int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); uint8_t* data; size_t size; int val; @@ -845,9 +844,9 @@ int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((buf), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((buf), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); uint8_t* data = NULL; size_t size; size_t copy_len = 0; @@ -896,13 +895,15 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp esp_err_t uart_flush(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_FAIL); uart_obj_t* p_uart = p_uart_obj[uart_num]; uint8_t* data; size_t size; + //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); + ESP_INTR_DISABLE(p_uart->intr_num); while(true) { if(p_uart->rx_head_ptr) { vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); @@ -919,11 +920,16 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->rx_ptr = NULL; p_uart->rx_cur_remain = 0; p_uart->rx_head_ptr = NULL; + ESP_INTR_ENABLE(p_uart->intr_num); xSemaphoreGive(p_uart->rx_mux); - xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); if(p_uart->tx_buf_size > 0) { - xSemaphoreTake(p_uart->tx_buffer_mux, (portTickType)portMAX_DELAY); + xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); + ESP_INTR_DISABLE(p_uart->intr_num); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + UART[uart_num]->int_ena.txfifo_empty = 0; + UART[uart_num]->int_clr.txfifo_empty = 1; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); do { data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); if(data == NULL) { @@ -931,10 +937,17 @@ esp_err_t uart_flush(uart_port_t uart_num) } vRingbufferReturnItem(p_uart->rx_ring_buf, data); } while(1); - xSemaphoreGive(p_uart->tx_buffer_mux); + p_uart->tx_brk_flg = 0; + p_uart->tx_brk_len = 0; + p_uart->tx_head = NULL; + p_uart->tx_len_cur = 0; + p_uart->tx_len_tot = 0; + p_uart->tx_ptr = NULL; + p_uart->tx_waiting_brk = 0; + p_uart->tx_waiting_fifo = false; + ESP_INTR_ENABLE(p_uart->intr_num); + xSemaphoreGive(p_uart->tx_mux); } - xSemaphoreGive(p_uart->tx_mux); - uart_wait_tx_done(uart_num, portMAX_DELAY); uart_reset_fifo(uart_num); return ESP_OK; } @@ -966,8 +979,8 @@ static void uart_ignore_char(char chr) //Only effective to ets_printf function, not ESP_LOGX macro. esp_err_t uart_set_print_port(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((p_uart_obj[uart_num]), "UART driver error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_uart_obj[uart_num]), "UART driver error", ESP_FAIL); s_uart_print_nport = uart_num; switch(s_uart_print_nport) { case UART_NUM_0: @@ -994,8 +1007,8 @@ int uart_get_print_port() esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); - UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { ESP_INTR_DISABLE(uart_intr_num); p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); @@ -1033,13 +1046,11 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->rx_head_ptr = NULL; p_uart_obj[uart_num]->rx_ring_buf = xRingbufferCreate(rx_buffer_size, RINGBUF_TYPE_BYTEBUF); if(tx_buffer_size > 0) { - p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT);//RINGBUF_TYPE_BYTEBUF);//RINGBUF_TYPE_NOSPLIT); + p_uart_obj[uart_num]->tx_ring_buf = xRingbufferCreate(tx_buffer_size, RINGBUF_TYPE_NOSPLIT); p_uart_obj[uart_num]->tx_buf_size = tx_buffer_size; - p_uart_obj[uart_num]->tx_buffer_mux = xSemaphoreCreateMutex(); } else { p_uart_obj[uart_num]->tx_ring_buf = NULL; p_uart_obj[uart_num]->tx_buf_size = 0; - p_uart_obj[uart_num]->tx_buffer_mux = NULL; } } else { ESP_LOGE(UART_TAG, "UART driver already installed\n"); @@ -1064,7 +1075,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b //Make sure no other tasks are still using UART before you call this function esp_err_t uart_driver_delete(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error"); + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { ESP_LOGI(UART_TAG, "ALREADY NULL\n"); return ESP_OK; @@ -1090,10 +1101,6 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) vSemaphoreDelete(p_uart_obj[uart_num]->tx_mux); p_uart_obj[uart_num]->tx_mux = NULL; } - if(p_uart_obj[uart_num]->tx_buffer_mux) { - vSemaphoreDelete(p_uart_obj[uart_num]->tx_buffer_mux); - p_uart_obj[uart_num]->tx_buffer_mux = NULL; - } if(p_uart_obj[uart_num]->rx_mux) { vSemaphoreDelete(p_uart_obj[uart_num]->rx_mux); p_uart_obj[uart_num]->rx_mux = NULL; From 8bdcf0c5f7872953220283ff4ac04145155fda86 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 4 Nov 2016 10:55:25 +0800 Subject: [PATCH 126/285] newlib: fix compilation error when no timers are enabled in menuconfig --- components/newlib/time.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/components/newlib/time.c b/components/newlib/time.c index 5f60e1d7b2..a474f1ca33 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -55,14 +55,16 @@ static uint64_t get_rtc_time_us() #endif // WITH_RTC -// time from Epoch to the first boot time +// s_boot_time: time from Epoch to the first boot time #ifdef WITH_RTC static RTC_DATA_ATTR struct timeval s_boot_time; -#else +#elif defined(WITH_FRC1) static struct timeval s_boot_time; #endif -static _lock_t s_boot_time_lock; +#if defined(WITH_RTC) || defined(WITH_FRC1) +static _lock_t s_boot_time_lock; +#endif #ifdef WITH_FRC1 #define FRC1_PRESCALER 16 @@ -121,6 +123,7 @@ clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) return (clock_t) tv.tv_sec; } +#if defined( WITH_FRC1 ) || defined( WITH_RTC ) static uint64_t get_time_since_boot() { uint64_t microseconds = 0; @@ -140,6 +143,7 @@ static uint64_t get_time_since_boot() #endif return microseconds; } +#endif // defined( WITH_FRC1 ) || defined( WITH_RTC ) int IRAM_ATTR _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz) { @@ -176,7 +180,7 @@ int settimeofday(const struct timeval *tv, const struct timezone *tz) } return 0; #else - __errno_r(r) = ENOSYS; + errno = ENOSYS; return -1; #endif } From 2fa00ebd909bef152518194d4f891c2670a75e90 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 4 Nov 2016 12:18:57 +0800 Subject: [PATCH 127/285] ld scripts: fix overlap between bootloader and application IRAM ranges --- components/bootloader/src/main/bootloader_start.c | 9 ++++----- components/bootloader/src/main/esp32.bootloader.ld | 2 +- components/esp32/cpu_start.c | 5 ++++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 5b1e152070..e1bcc18fd5 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -51,7 +51,7 @@ extern void Cache_Flush(int); void bootloader_main(); void unpack_load_app(const esp_partition_pos_t *app_node); void print_flash_info(const esp_image_header_t* pfhdr); -void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, +void set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, uint32_t drom_size, uint32_t irom_addr, @@ -445,7 +445,7 @@ void unpack_load_app(const esp_partition_pos_t* partition) image_header.entry_addr); } -void IRAM_ATTR set_cache_and_start_app( +void set_cache_and_start_app( uint32_t drom_addr, uint32_t drom_load_addr, uint32_t drom_size, @@ -456,9 +456,7 @@ void IRAM_ATTR set_cache_and_start_app( { ESP_LOGD(TAG, "configure drom and irom and start"); Cache_Read_Disable( 0 ); - Cache_Read_Disable( 1 ); Cache_Flush( 0 ); - Cache_Flush( 1 ); uint32_t drom_page_count = (drom_size + 64*1024 - 1) / (64*1024); // round up to 64k ESP_LOGV(TAG, "d mmu set paddr=%08x vaddr=%08x size=%d n=%d", drom_addr & 0xffff0000, drom_load_addr & 0xffff0000, drom_size, drom_page_count ); int rc = cache_flash_mmu_set( 0, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count ); @@ -474,7 +472,8 @@ void IRAM_ATTR set_cache_and_start_app( REG_CLR_BIT( DPORT_PRO_CACHE_CTRL1_REG, (DPORT_PRO_CACHE_MASK_IRAM0) | (DPORT_PRO_CACHE_MASK_IRAM1 & 0) | (DPORT_PRO_CACHE_MASK_IROM0 & 0) | DPORT_PRO_CACHE_MASK_DROM0 | DPORT_PRO_CACHE_MASK_DRAM1 ); REG_CLR_BIT( DPORT_APP_CACHE_CTRL1_REG, (DPORT_APP_CACHE_MASK_IRAM0) | (DPORT_APP_CACHE_MASK_IRAM1 & 0) | (DPORT_APP_CACHE_MASK_IROM0 & 0) | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 ); Cache_Read_Enable( 0 ); - Cache_Read_Enable( 1 ); + + // Application will need to do Cache_Flush(1) and Cache_Read_Enable(1) ESP_LOGD(TAG, "start: 0x%08x", entry_addr); typedef void (*entry_t)(void); diff --git a/components/bootloader/src/main/esp32.bootloader.ld b/components/bootloader/src/main/esp32.bootloader.ld index af97bda3ad..6a77eb6ade 100644 --- a/components/bootloader/src/main/esp32.bootloader.ld +++ b/components/bootloader/src/main/esp32.bootloader.ld @@ -15,7 +15,7 @@ MEMORY of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but are connected to the data port of the CPU and eg allow bytewise access. */ dport0_seg (RW) : org = 0x3FF00000, len = 0x10 /* IO */ - iram_seg (RWX) : org = 0x40098000, len = 0x1000 + iram_seg (RWX) : org = 0x40080000, len = 0x400 /* 1k of IRAM used by bootloader functions which need to flush/enable APP CPU cache */ iram_pool_1_seg (RWX) : org = 0x40078000, len = 0x8000 /* IRAM POOL1, used for APP CPU cache. We can abuse it in bootloader because APP CPU is still held in reset, until we enable APP CPU cache */ dram_seg (RW) : org = 0x3FFC0000, len = 0x20000 /* Shared RAM, minus rom bss/data/stack.*/ } diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index c82c528597..2688cd7f81 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -20,6 +20,7 @@ #include "rom/ets_sys.h" #include "rom/uart.h" #include "rom/rtc.h" +#include "rom/cache.h" #include "soc/cpu.h" #include "soc/dport_reg.h" @@ -110,7 +111,9 @@ void IRAM_ATTR call_start_cpu0() #if !CONFIG_FREERTOS_UNICORE ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1); - + //Flush and enable icache for APP CPU + Cache_Flush(1); + Cache_Read_Enable(1); //Un-stall the app cpu; the panic handler may have stalled it. CLEAR_PERI_REG_MASK(RTC_CNTL_SW_CPU_STALL_REG, RTC_CNTL_SW_STALL_APPCPU_C1_M); CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_APPCPU_C0_M); From 3ec23f1b837959548da8e7ddec4dfd7db7929a6c Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 4 Nov 2016 12:52:34 +0800 Subject: [PATCH 128/285] Modify as Angus's suggestion: 1. Set XXX_TAG static, remove extern XXX_TAG in uart.h/ledc.h/gpio.h 2. I removed uart_set/get_print_port() functions, these functions are not well tested, I removed them for now. 3. Modify some function names for uart_read/write_bytes 4. Modify uart_write_bytes and uart_write_bytes_with_break. --- components/driver/gpio.c | 2 +- components/driver/include/driver/gpio.h | 2 +- components/driver/include/driver/ledc.h | 1 - components/driver/include/driver/uart.h | 98 ++++------ components/driver/ledc.c | 2 +- components/driver/uart.c | 238 ++++++------------------ 6 files changed, 96 insertions(+), 247 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index da0fedb895..62a0e7faa7 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -20,7 +20,7 @@ #include "soc/soc.h" #include "esp_log.h" -const char* GPIO_TAG = "GPIO"; +static const char* GPIO_TAG = "GPIO"; #define GPIO_CHECK(a, str, ret_val) if (!(a)) { \ ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret_val); \ diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index a31c2f64b7..903621f619 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -27,7 +27,7 @@ #ifdef __cplusplus extern "C" { #endif -extern const char* GPIO_TAG; + #define GPIO_SEL_0 (BIT(0)) /*!< Pin 0 selected */ #define GPIO_SEL_1 (BIT(1)) /*!< Pin 1 selected */ #define GPIO_SEL_2 (BIT(2)) /*!< Pin 2 selected */ diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index ac29eaf56a..3ab0ebff1e 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -26,7 +26,6 @@ extern "C" { #endif -extern const char* LEDC_TAG; #define LEDC_APB_CLK_HZ (APB_CLK_FREQ) #define LEDC_REF_CLK_HZ (1*1000000) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 200d1148cb..ba487d57d5 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -32,11 +32,11 @@ extern "C" { #include "freertos/ringbuf.h" #include -extern const char* UART_TAG; -#define UART_FIFO_LEN (128) //Do not change this, this value describes the length of the gardware FIFO in the ESP32 +#define UART_FIFO_LEN (128) /*< Length of the hardware FIFO buffers */ #define UART_INTR_MASK 0x1ff #define UART_LINE_INV_MASK (0x3f << 19) #define UART_BITRATE_MAX 5000000 +#define UART_PIN_NO_CHANGE (-1) typedef enum { UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ @@ -243,6 +243,8 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; * * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * + * Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. + * * @return * - ESP_OK Success * - ESP_FAIL Parameter error @@ -380,15 +382,19 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* /** * @brief Set UART pin number * + * @note + * Internal signal can be output to multiple GPIO pads + * Only one GPIO pad can connect with input signal + * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param tx_io_num UART TX pin GPIO number + * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rx_io_num UART RX pin GPIO number + * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rts_io_num UART RTS pin GPIO number + * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param cts_io_num UART CTS pin GPIO number + * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * * @return * - ESP_OK Success @@ -434,20 +440,20 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config); +esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config); /** * @brief UART interrupt configure * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param p_intr_conf UART interrupt settings + * @param intr_conf UART interrupt settings * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf); +esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf); /** * @brief Install UART driver. @@ -504,6 +510,9 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** * @brief Send data to the UART port from a given buffer and length, * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. + * @note + * This function should only be used when UART TX buffer is not enabled. + * * * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 * @@ -515,7 +524,7 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); +int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); /** * @brief Send data to the UART port from a given buffer and length, @@ -536,7 +545,7 @@ int uart_tx_chars(uart_port_t uart_no, char* buffer, uint32_t len); * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); +int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); /** * @brief Send data to the UART port from a given buffer and length, @@ -564,20 +573,7 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size); * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); - -/** -* @brief UART read one char - * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @param ticks_to_wait Timeout, count in RTOS ticks - * - * @return - * - (-1) Error - * - Others return a char data from UART. - */ -int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait); +int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** * @brief UART read bytes from UART buffer @@ -608,25 +604,6 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp */ esp_err_t uart_flush(uart_port_t uart_num); -/** - * @brief Set the serial output port for ets_printf function, not effective for ESP_LOGX macro. - * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @return - * - ESP_OK Success - * - ESP_FAIL Parameter error, or UART driver not installed. - */ -esp_err_t uart_set_print_port(uart_port_t uart_no); - -/** - * @brief Get the current serial port for ets_printf function - * - * - * @return current print port(0: UART0; 1: UART1; 2: UART2) - */ -int uart_get_print_port(void); - /***************************EXAMPLE********************************** * * @@ -658,7 +635,7 @@ int uart_get_print_port(void); * @code{c} * //2. Set UART pin * //set UART pin, not needed if use default pins. - * uart_set_pin(uart_num, -1, -1, 15, 13); + * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13); * @endcode *-----------------------------------------------------------------------------* * @code{c} @@ -671,12 +648,12 @@ int uart_get_print_port(void); * @code{c} * //4. Write data to UART. * char* test_str = "This is a test string.\n" - * uart_tx_all_chars(uart_num, (const char*)test_str, strlen(test_str)); + * uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str)); * @endcode *-----------------------------------------------------------------------------* * @code{c} * //5. Write data to UART, end with a break signal. - * uart_tx_all_chars_with_break(0, "test break\n",strlen("test break\n"), 100); + * uart_write_bytes_with_break(0, "test break\n",strlen("test break\n"), 100); * @endcode *-----------------------------------------------------------------------------* * @code{c} @@ -696,8 +673,6 @@ int uart_get_print_port(void); * uart_param_config(uart_num, &uart_config); * //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) * uart_set_pin(uart_num, 16, 17, 18, 19); - * //Set UART log level - * esp_log_level_set(UART_TAG, ESP_LOG_ERROR); * //Install UART driver( We don't need an event queue here) * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF); * uint8_t data[1000]; @@ -705,7 +680,7 @@ int uart_get_print_port(void); * //Read data from UART * int len = uart_read_bytes(uart_num, data, sizeof(data), 10); * //Write data back to UART - * uart_tx_all_chars(uart_num, (const char*)data, len); + * uart_write_bytes(uart_num, (const char*)data, len); * } * } * @endcode @@ -715,6 +690,7 @@ int uart_get_print_port(void); * #include "freertos/queue.h" * //A queue to handle UART event. * QueueHandle_t uart0_queue; + * static const char *TAG = "uart_example"; * void uart_task(void *pvParameters) * { * int uart_num = (int)pvParameters; @@ -723,37 +699,37 @@ int uart_get_print_port(void); * for(;;) { * //Waiting for UART event. * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { - * ESP_LOGI(UART_TAG, "uart[%d] event:", uart_num); + * ESP_LOGI(TAG, "uart[%d] event:", uart_num); * switch(event.type) { * //Event of UART receving data * case UART_DATA: - * ESP_LOGI(UART_TAG,"data, len: %d\n", event.data.size); + * ESP_LOGI(TAG,"data, len: %d\n", event.data.size); * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); - * ESP_LOGI(UART_TAG, "uart read: %d\n", len); + * ESP_LOGI(TAG, "uart read: %d\n", len); * break; * //Event of HW FIFO overflow detected * case UART_FIFO_OVF: - * ESP_LOGI(UART_TAG, "hw fifo overflow\n"); + * ESP_LOGI(TAG, "hw fifo overflow\n"); * break; * //Event of UART ring buffer full * case UART_BUFFER_FULL: - * ESP_LOGI(UART_TAG, "ring buffer full\n"); + * ESP_LOGI(TAG, "ring buffer full\n"); * break; * //Event of UART RX break detected * case UART_BREAK: - * ESP_LOGI(UART_TAG, "uart rx break\n"); + * ESP_LOGI(TAG, "uart rx break\n"); * break; * //Event of UART parity check error * case UART_PARITY_ERR: - * ESP_LOGI(UART_TAG, "uart parity error\n"); + * ESP_LOGI(TAG, "uart parity error\n"); * break; * //Event of UART frame error * case UART_FRAME_ERR: - * ESP_LOGI(UART_TAG, "uart frame error\n"); + * ESP_LOGI(TAG, "uart frame error\n"); * break; * //Others * default: - * ESP_LOGI(UART_TAG, "uart event type: %d\n", event.type); + * ESP_LOGI(TAG, "uart event type: %d\n", event.type); * break; * } * } @@ -775,9 +751,9 @@ int uart_get_print_port(void); * //Set UART parameters * uart_param_config(uart_num, &uart_config); * //Set UART pins,(-1: default pin, no change.) - * uart_set_pin(uart_num, -1, -1, 15, 13); + * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13); * //Set UART log level - * esp_log_level_set(UART_TAG, ESP_LOG_INFO); + * esp_log_level_set(TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); * //Create a task to handler UART event from ISR diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 771c4a17df..b9039cf626 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -20,7 +20,7 @@ #include "driver/ledc.h" #include "esp_log.h" -const char* LEDC_TAG = "LEDC"; +static const char* LEDC_TAG = "LEDC"; static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; #define LEDC_CHECK(a, str, ret_val) if (!(a)) { \ ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ diff --git a/components/driver/uart.c b/components/driver/uart.c index cab05dd0be..a3e0b92b2b 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -29,7 +29,7 @@ #include "driver/gpio.h" #include "soc/uart_struct.h" -const char* UART_TAG = "UART"; +static const char* UART_TAG = "UART"; #define UART_CHECK(a, str, ret) if (!(a)) { \ ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret); \ @@ -249,28 +249,19 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) esp_err_t uart_enable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uart_enable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); return ESP_OK; } esp_err_t uart_disable_rx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uart_disable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); return ESP_OK; } esp_err_t uart_disable_tx_intr(uart_port_t uart_num) { - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - UART[uart_num]->int_ena.txfifo_empty = 0; - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + uart_disable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA); return ESP_OK; } @@ -391,7 +382,7 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level) return ESP_OK; } -esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) +esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((uart_config), "param null\n", ESP_FAIL); @@ -413,25 +404,25 @@ esp_err_t uart_param_config(uart_port_t uart_num, uart_config_t *uart_config) return ESP_OK; } -esp_err_t uart_intr_config(uart_port_t uart_num, uart_intr_config_t *p_intr_conf) +esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((p_intr_conf), "param null\n", ESP_FAIL); + UART_CHECK((intr_conf), "param null\n", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.val = UART_INTR_MASK; - if(p_intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { - UART[uart_num]->conf1.rx_tout_thrhd = ((p_intr_conf->rx_timeout_thresh) & UART_RX_TOUT_THRHD_V); + if(intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { + UART[uart_num]->conf1.rx_tout_thrhd = ((intr_conf->rx_timeout_thresh) & UART_RX_TOUT_THRHD_V); UART[uart_num]->conf1.rx_tout_en = 1; } else { UART[uart_num]->conf1.rx_tout_en = 0; } - if(p_intr_conf->intr_enable_mask & UART_RXFIFO_FULL_INT_ENA_M) { - UART[uart_num]->conf1.rxfifo_full_thrhd = p_intr_conf->rxfifo_full_thresh; + if(intr_conf->intr_enable_mask & UART_RXFIFO_FULL_INT_ENA_M) { + UART[uart_num]->conf1.rxfifo_full_thrhd = intr_conf->rxfifo_full_thresh; } - if(p_intr_conf->intr_enable_mask & UART_TXFIFO_EMPTY_INT_ENA_M) { - UART[uart_num]->conf1.txfifo_empty_thrhd = p_intr_conf->txfifo_empty_intr_thresh; + if(intr_conf->intr_enable_mask & UART_TXFIFO_EMPTY_INT_ENA_M) { + UART[uart_num]->conf1.txfifo_empty_thrhd = intr_conf->txfifo_empty_intr_thresh; } - UART[uart_num]->int_ena.val = p_intr_conf->intr_enable_mask; + UART[uart_num]->int_ena.val = intr_conf->intr_enable_mask; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); return ESP_FAIL; } @@ -459,8 +450,8 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(p_uart->tx_waiting_brk) { return; } - //TX semaphore used in none tx ringbuffer mode. - if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size > 0) { + //TX semaphore will only be used when tx_buf_size is zero. + if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size == 0) { p_uart->tx_waiting_fifo = false; xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); } @@ -682,7 +673,7 @@ static esp_err_t uart_set_break(uart_port_t uart_num, int break_num) //Fill UART tx_fifo and return a number, //This function by itself is not thread-safe, always call from within a muxed section. -static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) +static int uart_fill_fifo(uart_port_t uart_num, const char* buffer, uint32_t len) { uint8_t i = 0; uint8_t tx_fifo_cnt = UART[uart_num]->status.txfifo_cnt; @@ -694,7 +685,7 @@ static int uart_fill_fifo(uart_port_t uart_num, char* buffer, uint32_t len) return copy_cnt; } -int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) +int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); @@ -703,7 +694,7 @@ int uart_tx_chars(uart_port_t uart_num, char* buffer, uint32_t len) return 0; } xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); - int tx_len = uart_fill_fifo(uart_num, buffer, len); + int tx_len = uart_fill_fifo(uart_num, (const char*) buffer, len); xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); return tx_len; } @@ -713,44 +704,21 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool if(size == 0) { return 0; } + size_t original_size = size; + //lock for uart_tx xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); - size_t original_size = size; - while(size) { - //semaphore for tx_fifo available - if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { - size_t sent = uart_fill_fifo(uart_num, (char*) src, size); - if(sent < size) { - p_uart_obj[uart_num]->tx_waiting_fifo = true; - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - } - size -= sent; - src += sent; - } - } - if(brk_en) { - uart_set_break(uart_num, brk_len); - xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); - } - xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); - xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); - return original_size; -} - -int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); - UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error", (-1)); - UART_CHECK(src, "buffer null", (-1)); - //Push data to TX ring buffer and return, ISR will send the data. if(p_uart_obj[uart_num]->tx_buf_size > 0) { - xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); - int ori_size = size; int offset = 0; uart_event_t evt; - evt.type = UART_DATA; evt.data.size = size; + evt.data.brk_len = brk_len; + if(brk_en) { + evt.type = UART_DATA_BREAK; + } else { + evt.type = UART_DATA; + } xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); while(size > 0) { int send_size = size > max_size / 2 ? max_size / 2 : size; @@ -760,86 +728,45 @@ int uart_tx_all_chars(uart_port_t uart_num, const char* src, size_t size) } xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return ori_size; } else { - //Send data without TX ring buffer, the task will block until all data have been sent out - return uart_tx_all(uart_num, src, size, 0, 0); + while(size) { + //semaphore for tx_fifo available + if(pdTRUE == xSemaphoreTake(p_uart_obj[uart_num]->tx_fifo_sem, (portTickType)portMAX_DELAY)) { + size_t sent = uart_fill_fifo(uart_num, (char*) src, size); + if(sent < size) { + p_uart_obj[uart_num]->tx_waiting_fifo = true; + uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); + } + size -= sent; + src += sent; + } + } + if(brk_en) { + uart_set_break(uart_num, brk_len); + xSemaphoreTake(p_uart_obj[uart_num]->tx_brk_sem, (portTickType)portMAX_DELAY); + } + xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem); } + xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); + return original_size; } -int uart_tx_all_chars_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) +int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); + UART_CHECK((p_uart_obj[uart_num] != NULL), "uart driver error", (-1)); + UART_CHECK(src, "buffer null", (-1)); + return uart_tx_all(uart_num, src, size, 0, 0); +} + +int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); UART_CHECK((size > 0), "uart size error", (-1)); UART_CHECK((src), "uart data null", (-1)); UART_CHECK((brk_len > 0 && brk_len < 256), "break_num error", (-1)); - //Push data to TX ring buffer and return, ISR will send the data. - if(p_uart_obj[uart_num]->tx_buf_size > 0) { - xSemaphoreTake(p_uart_obj[uart_num]->tx_mux, (portTickType)portMAX_DELAY); - int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); - int ori_size = size; - int offset = 0; - uart_event_t evt; - evt.type = UART_DATA_BREAK; - evt.data.size = size; - evt.data.brk_len = brk_len; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); - while(size > 0) { - int send_size = size > max_size / 2 ? max_size / 2 : size; - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); - size -= send_size; - offset += send_size; - } - xSemaphoreGive(p_uart_obj[uart_num]->tx_mux); - uart_enable_tx_intr(uart_num, 1, UART_EMPTY_THRESH_DEFAULT); - return ori_size; - } else { - //Send data without TX ring buffer, the task will block until all data have been sent out - return uart_tx_all(uart_num, src, size, 1, brk_len); - } -} - -int uart_read_char(uart_port_t uart_num, TickType_t ticks_to_wait) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", (-1)); - UART_CHECK((p_uart_obj[uart_num]), "uart driver error", (-1)); - uint8_t* data; - size_t size; - int val; - portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; - if(xSemaphoreTake(p_uart_obj[uart_num]->rx_mux,(portTickType)ticks_to_wait) != pdTRUE) { - return -1; - } - if(p_uart_obj[uart_num]->rx_cur_remain == 0) { - ticks_to_wait = ticks_end - xTaskGetTickCount(); - data = (uint8_t*) xRingbufferReceive(p_uart_obj[uart_num]->rx_ring_buf, &size, (portTickType) ticks_to_wait); - if(data) { - p_uart_obj[uart_num]->rx_head_ptr = data; - p_uart_obj[uart_num]->rx_ptr = data; - p_uart_obj[uart_num]->rx_cur_remain = size; - } else { - xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); - return -1; - } - } - val = *(p_uart_obj[uart_num]->rx_ptr); - p_uart_obj[uart_num]->rx_ptr++; - p_uart_obj[uart_num]->rx_cur_remain--; - if(p_uart_obj[uart_num]->rx_cur_remain == 0) { - vRingbufferReturnItem(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_head_ptr); - p_uart_obj[uart_num]->rx_head_ptr = NULL; - p_uart_obj[uart_num]->rx_ptr = NULL; - if(p_uart_obj[uart_num]->rx_buffer_full_flg) { - BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); - if(res == pdTRUE) { - p_uart_obj[uart_num]->rx_buffer_full_flg = false; - uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); - } - } - } - xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); - return val; + return uart_tx_all(uart_num, src, size, 1, brk_len); } int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait) @@ -952,59 +879,6 @@ esp_err_t uart_flush(uart_port_t uart_num) return ESP_OK; } -//----------------------------------- -//Should not enable hw flow control the debug print port. -//Use uart_tx_all_chars() as a thread-safe function to send data. -static int s_uart_print_nport = UART_NUM_0; -static void uart2_write_char(char chr) -{ - uart_tx_all_chars(UART_NUM_2, (const char*)&chr, 1); -} - -static void uart1_write_char(char chr) -{ - uart_tx_all_chars(UART_NUM_1, (const char*)&chr, 1); -} - -static void uart0_write_char(char chr) -{ - uart_tx_all_chars(UART_NUM_0, (const char*)&chr, 1); -} - -static void uart_ignore_char(char chr) -{ - -} - -//Only effective to ets_printf function, not ESP_LOGX macro. -esp_err_t uart_set_print_port(uart_port_t uart_num) -{ - UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((p_uart_obj[uart_num]), "UART driver error", ESP_FAIL); - s_uart_print_nport = uart_num; - switch(s_uart_print_nport) { - case UART_NUM_0: - ets_install_putc1(uart0_write_char); - break; - case UART_NUM_1: - ets_install_putc1(uart1_write_char); - break; - case UART_NUM_2: - ets_install_putc1(uart2_write_char); - break; - case UART_NUM_MAX: - default: - ets_install_putc1(uart_ignore_char); - break; - } - return ESP_OK; -} - -int uart_get_print_port() -{ - return s_uart_print_nport; -} - esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); From 2d88fc0329d5679d0ff8f6280443e5c4cb83dac0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 4 Nov 2016 14:12:21 +0800 Subject: [PATCH 129/285] wpa_supplicant: replace pre-built crypto library with source code --- components/esp32/component.mk | 2 +- components/wpa_supplicant/COPYING | 22 + components/wpa_supplicant/component.mk | 6 + .../wpa_supplicant/include/crypto/aes.h | 27 + .../wpa_supplicant/include/crypto/aes_i.h | 131 + .../wpa_supplicant/include/crypto/aes_wrap.h | 48 + .../wpa_supplicant/include/crypto/base64.h | 23 + .../wpa_supplicant/include/crypto/common.h | 481 +++ .../wpa_supplicant/include/crypto/crypto.h | 471 +++ .../wpa_supplicant/include/crypto/dh_group5.h | 23 + .../wpa_supplicant/include/crypto/dh_groups.h | 32 + .../wpa_supplicant/include/crypto/includes.h | 65 + .../wpa_supplicant/include/crypto/md5.h | 35 + .../wpa_supplicant/include/crypto/md5_i.h | 29 + .../wpa_supplicant/include/crypto/random.h | 34 + .../wpa_supplicant/include/crypto/sha1.h | 33 + .../wpa_supplicant/include/crypto/sha1_i.h | 29 + .../wpa_supplicant/include/crypto/sha256.h | 27 + .../wpa_supplicant/include/wpa/ap_config.h | 544 +++ .../wpa_supplicant/include/wpa/common.h | 324 ++ components/wpa_supplicant/include/wpa/defs.h | 307 ++ .../wpa_supplicant/include/wpa/eapol_common.h | 71 + .../wpa_supplicant/include/wpa/hostapd.h | 312 ++ .../include/wpa/ieee80211_crypto.h | 226 ++ .../include/wpa/ieee802_11_defs.h | 607 +++ .../wpa_supplicant/include/wpa/ieee802_1x.h | 64 + .../wpa_supplicant/include/wpa/includes.h | 31 + components/wpa_supplicant/include/wpa/list.h | 101 + .../wpa_supplicant/include/wpa/sta_info.h | 194 + .../include/wpa/state_machine.h | 138 + components/wpa_supplicant/include/wpa/wpa.h | 193 + .../wpa_supplicant/include/wpa/wpa_auth.h | 292 ++ .../wpa_supplicant/include/wpa/wpa_auth_i.h | 234 ++ .../wpa_supplicant/include/wpa/wpa_auth_ie.h | 50 + .../wpa_supplicant/include/wpa/wpa_common.h | 332 ++ .../wpa_supplicant/include/wpa/wpa_debug.h | 193 + components/wpa_supplicant/include/wpa/wpa_i.h | 89 + .../wpa_supplicant/include/wpa/wpa_ie.h | 56 + .../wpa_supplicant/include/wpa/wpabuf.h | 168 + .../wpa_supplicant/include/wpa/wpas_glue.h | 31 + .../include/wpa2/eap_peer/eap.h | 24 + .../include/wpa2/eap_peer/eap_common.h | 23 + .../include/wpa2/eap_peer/eap_config.h | 220 ++ .../include/wpa2/eap_peer/eap_defs.h | 92 + .../include/wpa2/eap_peer/eap_i.h | 88 + .../include/wpa2/eap_peer/eap_tls.h | 25 + .../include/wpa2/eap_peer/eap_tls_common.h | 131 + .../wpa_supplicant/include/wpa2/tls/asn1.h | 66 + .../wpa_supplicant/include/wpa2/tls/bignum.h | 38 + .../include/wpa2/tls/libtommath.h | 3443 +++++++++++++++++ .../wpa_supplicant/include/wpa2/tls/pkcs1.h | 22 + .../wpa_supplicant/include/wpa2/tls/pkcs5.h | 16 + .../wpa_supplicant/include/wpa2/tls/pkcs8.h | 16 + .../wpa_supplicant/include/wpa2/tls/rsa.h | 23 + .../wpa_supplicant/include/wpa2/tls/tls.h | 537 +++ .../include/wpa2/tls/tlsv1_client.h | 54 + .../include/wpa2/tls/tlsv1_client_i.h | 84 + .../include/wpa2/tls/tlsv1_common.h | 261 ++ .../include/wpa2/tls/tlsv1_cred.h | 40 + .../include/wpa2/tls/tlsv1_record.h | 71 + .../include/wpa2/tls/tlsv1_server.h | 48 + .../include/wpa2/tls/tlsv1_server_i.h | 71 + .../wpa_supplicant/include/wpa2/tls/x509v3.h | 123 + .../include/wpa2/utils/base64.h | 17 + .../include/wpa2/utils/ext_password.h | 33 + .../include/wpa2/utils/ext_password_i.h | 23 + .../wpa_supplicant/port/include/byteswap.h | 65 + .../wpa_supplicant/port/include/endian.h | 229 ++ components/wpa_supplicant/port/include/os.h | 286 ++ .../wpa_supplicant/src/crypto/aes-cbc.c | 88 + .../src/crypto/aes-internal-dec.c | 172 + .../src/crypto/aes-internal-enc.c | 134 + .../wpa_supplicant/src/crypto/aes-internal.c | 854 ++++ .../wpa_supplicant/src/crypto/aes-unwrap.c | 80 + .../wpa_supplicant/src/crypto/aes-wrap.c | 70 + components/wpa_supplicant/src/crypto/bignum.c | 244 ++ components/wpa_supplicant/src/crypto/bignum.h | 38 + .../src/crypto/crypto_internal-cipher.c | 268 ++ .../src/crypto/crypto_internal-modexp.c | 56 + .../src/crypto/crypto_internal-rsa.c | 111 + .../src/crypto/crypto_internal.c | 280 ++ .../wpa_supplicant/src/crypto/dh_group5.c | 43 + .../wpa_supplicant/src/crypto/dh_groups.c | 641 +++ .../wpa_supplicant/src/crypto/libtommath.h | 3440 ++++++++++++++++ .../wpa_supplicant/src/crypto/md5-internal.c | 298 ++ components/wpa_supplicant/src/crypto/md5.c | 113 + components/wpa_supplicant/src/crypto/rc4.c | 61 + .../wpa_supplicant/src/crypto/sha1-internal.c | 313 ++ .../wpa_supplicant/src/crypto/sha1-pbkdf2.c | 101 + components/wpa_supplicant/src/crypto/sha1.c | 166 + .../src/crypto/sha256-internal.c | 249 ++ components/wpa_supplicant/src/crypto/sha256.c | 160 + 92 files changed, 20223 insertions(+), 1 deletion(-) create mode 100644 components/wpa_supplicant/COPYING create mode 100644 components/wpa_supplicant/component.mk create mode 100644 components/wpa_supplicant/include/crypto/aes.h create mode 100644 components/wpa_supplicant/include/crypto/aes_i.h create mode 100644 components/wpa_supplicant/include/crypto/aes_wrap.h create mode 100644 components/wpa_supplicant/include/crypto/base64.h create mode 100644 components/wpa_supplicant/include/crypto/common.h create mode 100644 components/wpa_supplicant/include/crypto/crypto.h create mode 100644 components/wpa_supplicant/include/crypto/dh_group5.h create mode 100644 components/wpa_supplicant/include/crypto/dh_groups.h create mode 100644 components/wpa_supplicant/include/crypto/includes.h create mode 100644 components/wpa_supplicant/include/crypto/md5.h create mode 100644 components/wpa_supplicant/include/crypto/md5_i.h create mode 100644 components/wpa_supplicant/include/crypto/random.h create mode 100644 components/wpa_supplicant/include/crypto/sha1.h create mode 100644 components/wpa_supplicant/include/crypto/sha1_i.h create mode 100644 components/wpa_supplicant/include/crypto/sha256.h create mode 100644 components/wpa_supplicant/include/wpa/ap_config.h create mode 100644 components/wpa_supplicant/include/wpa/common.h create mode 100644 components/wpa_supplicant/include/wpa/defs.h create mode 100644 components/wpa_supplicant/include/wpa/eapol_common.h create mode 100644 components/wpa_supplicant/include/wpa/hostapd.h create mode 100644 components/wpa_supplicant/include/wpa/ieee80211_crypto.h create mode 100644 components/wpa_supplicant/include/wpa/ieee802_11_defs.h create mode 100644 components/wpa_supplicant/include/wpa/ieee802_1x.h create mode 100644 components/wpa_supplicant/include/wpa/includes.h create mode 100644 components/wpa_supplicant/include/wpa/list.h create mode 100644 components/wpa_supplicant/include/wpa/sta_info.h create mode 100644 components/wpa_supplicant/include/wpa/state_machine.h create mode 100644 components/wpa_supplicant/include/wpa/wpa.h create mode 100644 components/wpa_supplicant/include/wpa/wpa_auth.h create mode 100644 components/wpa_supplicant/include/wpa/wpa_auth_i.h create mode 100644 components/wpa_supplicant/include/wpa/wpa_auth_ie.h create mode 100644 components/wpa_supplicant/include/wpa/wpa_common.h create mode 100644 components/wpa_supplicant/include/wpa/wpa_debug.h create mode 100644 components/wpa_supplicant/include/wpa/wpa_i.h create mode 100644 components/wpa_supplicant/include/wpa/wpa_ie.h create mode 100644 components/wpa_supplicant/include/wpa/wpabuf.h create mode 100644 components/wpa_supplicant/include/wpa/wpas_glue.h create mode 100644 components/wpa_supplicant/include/wpa2/eap_peer/eap.h create mode 100644 components/wpa_supplicant/include/wpa2/eap_peer/eap_common.h create mode 100644 components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h create mode 100644 components/wpa_supplicant/include/wpa2/eap_peer/eap_defs.h create mode 100644 components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h create mode 100644 components/wpa_supplicant/include/wpa2/eap_peer/eap_tls.h create mode 100644 components/wpa_supplicant/include/wpa2/eap_peer/eap_tls_common.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/asn1.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/bignum.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/libtommath.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/pkcs1.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/pkcs5.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/pkcs8.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/rsa.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tls.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tlsv1_client.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tlsv1_client_i.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tlsv1_common.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tlsv1_cred.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tlsv1_record.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tlsv1_server.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/tlsv1_server_i.h create mode 100644 components/wpa_supplicant/include/wpa2/tls/x509v3.h create mode 100644 components/wpa_supplicant/include/wpa2/utils/base64.h create mode 100644 components/wpa_supplicant/include/wpa2/utils/ext_password.h create mode 100644 components/wpa_supplicant/include/wpa2/utils/ext_password_i.h create mode 100644 components/wpa_supplicant/port/include/byteswap.h create mode 100644 components/wpa_supplicant/port/include/endian.h create mode 100644 components/wpa_supplicant/port/include/os.h create mode 100644 components/wpa_supplicant/src/crypto/aes-cbc.c create mode 100644 components/wpa_supplicant/src/crypto/aes-internal-dec.c create mode 100644 components/wpa_supplicant/src/crypto/aes-internal-enc.c create mode 100644 components/wpa_supplicant/src/crypto/aes-internal.c create mode 100644 components/wpa_supplicant/src/crypto/aes-unwrap.c create mode 100644 components/wpa_supplicant/src/crypto/aes-wrap.c create mode 100644 components/wpa_supplicant/src/crypto/bignum.c create mode 100644 components/wpa_supplicant/src/crypto/bignum.h create mode 100644 components/wpa_supplicant/src/crypto/crypto_internal-cipher.c create mode 100644 components/wpa_supplicant/src/crypto/crypto_internal-modexp.c create mode 100644 components/wpa_supplicant/src/crypto/crypto_internal-rsa.c create mode 100644 components/wpa_supplicant/src/crypto/crypto_internal.c create mode 100644 components/wpa_supplicant/src/crypto/dh_group5.c create mode 100644 components/wpa_supplicant/src/crypto/dh_groups.c create mode 100644 components/wpa_supplicant/src/crypto/libtommath.h create mode 100644 components/wpa_supplicant/src/crypto/md5-internal.c create mode 100644 components/wpa_supplicant/src/crypto/md5.c create mode 100644 components/wpa_supplicant/src/crypto/rc4.c create mode 100644 components/wpa_supplicant/src/crypto/sha1-internal.c create mode 100644 components/wpa_supplicant/src/crypto/sha1-pbkdf2.c create mode 100644 components/wpa_supplicant/src/crypto/sha1.c create mode 100644 components/wpa_supplicant/src/crypto/sha256-internal.c create mode 100644 components/wpa_supplicant/src/crypto/sha256.c diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 788f880127..e91020c3e6 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -10,7 +10,7 @@ COMPONENT_SRCDIRS := . hwcrypto -LIBS := crypto core net80211 phy rtc pp wpa smartconfig +LIBS := core net80211 phy rtc pp wpa smartconfig LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld diff --git a/components/wpa_supplicant/COPYING b/components/wpa_supplicant/COPYING new file mode 100644 index 0000000000..7efce0dee1 --- /dev/null +++ b/components/wpa_supplicant/COPYING @@ -0,0 +1,22 @@ +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2016, Jouni Malinen and contributors +All Rights Reserved. + + +See the README file for the current license terms. + +This software was previously distributed under BSD/GPL v2 dual license +terms that allowed either of those license alternatives to be +selected. As of February 11, 2012, the project has chosen to use only +the BSD license option for future distribution. As such, the GPL v2 +license option is no longer used. It should be noted that the BSD +license option (the one with advertisement clause removed) is compatible +with GPL and as such, does not prevent use of this software in projects +that use GPL. + +Some of the files may still include pointers to GPL version 2 license +terms. However, such copyright and license notifications are maintained +only for attribution purposes and any distribution of this software +after February 11, 2012 is no longer under the GPL v2 option. diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk new file mode 100644 index 0000000000..6e5d7e0600 --- /dev/null +++ b/components/wpa_supplicant/component.mk @@ -0,0 +1,6 @@ +COMPONENT_ADD_INCLUDEDIRS := include port/include +COMPONENT_SRCDIRS := src/crypto + +CFLAGS += -DEMBEDDED_SUPP -D__ets__ + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/wpa_supplicant/include/crypto/aes.h b/components/wpa_supplicant/include/crypto/aes.h new file mode 100644 index 0000000000..ba384a9dae --- /dev/null +++ b/components/wpa_supplicant/include/crypto/aes.h @@ -0,0 +1,27 @@ +/* + * AES functions + * Copyright (c) 2003-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AES_H +#define AES_H + +#define AES_BLOCK_SIZE 16 + +void * aes_encrypt_init(const u8 *key, size_t len); +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +void aes_encrypt_deinit(void *ctx); +void * aes_decrypt_init(const u8 *key, size_t len); +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +void aes_decrypt_deinit(void *ctx); + +#endif /* AES_H */ diff --git a/components/wpa_supplicant/include/crypto/aes_i.h b/components/wpa_supplicant/include/crypto/aes_i.h new file mode 100644 index 0000000000..1063422a81 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/aes_i.h @@ -0,0 +1,131 @@ +/* + * AES (Rijndael) cipher + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AES_I_H +#define AES_I_H + +#include "aes.h" + +/* #define FULL_UNROLL */ +#define AES_SMALL_TABLES + +extern const u32 Te0[256]; +extern const u32 Te1[256]; +extern const u32 Te2[256]; +extern const u32 Te3[256]; +extern const u32 Te4[256]; +extern const u32 Td0[256]; +extern const u32 Td1[256]; +extern const u32 Td2[256]; +extern const u32 Td3[256]; +extern const u32 Td4[256]; +extern const u32 rcon[10]; +extern const u8 Td4s[256]; +extern const u8 rcons[10]; + +#ifndef AES_SMALL_TABLES + +#define RCON(i) rcon[(i)] + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) Te1[((i) >> 16) & 0xff] +#define TE2(i) Te2[((i) >> 8) & 0xff] +#define TE3(i) Te3[(i) & 0xff] +#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) +#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) +#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) +#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) +#define TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) (Te4[(i) & 0xff] & 0x000000ff) +#define TE4(i) (Te4[(i)] & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) Td1[((i) >> 16) & 0xff] +#define TD2(i) Td2[((i) >> 8) & 0xff] +#define TD3(i) Td3[(i) & 0xff] +#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) +#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) Td1[(i) & 0xff] +#define TD2_(i) Td2[(i) & 0xff] +#define TD3_(i) Td3[(i) & 0xff] + +#else /* AES_SMALL_TABLES */ + +#define RCON(i) (rcons[(i)] << 24) + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) +#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) +#define TE3(i) rotr(Te0[(i) & 0xff], 24) +#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) +#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) +#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) +#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) +#define TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) +#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) +#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) +#define TD3(i) rotr(Td0[(i) & 0xff], 24) +#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) +#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) +#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) +#define TD44(i) (Td4s[(i) & 0xff]) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) rotr(Td0[(i) & 0xff], 8) +#define TD2_(i) rotr(Td0[(i) & 0xff], 16) +#define TD3_(i) rotr(Td0[(i) & 0xff], 24) + +#endif /* AES_SMALL_TABLES */ + +#ifdef _MSC_VER +#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) +#define GETU32(p) SWAP(*((u32 *)(p))) +#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ +((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { \ +(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ +(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +#define AES_PRIV_SIZE (4 * 4 * 15 + 4) +#define AES_PRIV_NR_POS (4 * 15) + +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits); + +#endif /* AES_I_H */ diff --git a/components/wpa_supplicant/include/crypto/aes_wrap.h b/components/wpa_supplicant/include/crypto/aes_wrap.h new file mode 100644 index 0000000000..4b1c7b083b --- /dev/null +++ b/components/wpa_supplicant/include/crypto/aes_wrap.h @@ -0,0 +1,48 @@ +/* + * AES-based functions + * + * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * - One-Key CBC MAC (OMAC1) hash with AES-128 + * - AES-128 CTR mode encryption + * - AES-128 EAX mode encryption/decryption + * - AES-128 CBC + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AES_WRAP_H +#define AES_WRAP_H + +int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); +int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, + u8 *mac); +int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, + u8 *mac); +int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); +int __must_check aes_128_eax_encrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag); +int __must_check aes_128_eax_decrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag); +int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); + +#endif /* AES_WRAP_H */ diff --git a/components/wpa_supplicant/include/crypto/base64.h b/components/wpa_supplicant/include/crypto/base64.h new file mode 100644 index 0000000000..b87a1682f8 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/base64.h @@ -0,0 +1,23 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BASE64_H +#define BASE64_H + +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len); +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len); + +#endif /* BASE64_H */ diff --git a/components/wpa_supplicant/include/crypto/common.h b/components/wpa_supplicant/include/crypto/common.h new file mode 100644 index 0000000000..319b861e45 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/common.h @@ -0,0 +1,481 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +#include "os.h" + +#if defined(__XTENSA__) +#include +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#endif /*__XTENSA__*/ + +#if defined(__linux__) || defined(__GLIBC__) +#include +#include +#endif /* __linux__ */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) +#include +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#define __BIG_ENDIAN _BIG_ENDIAN +#ifdef __OpenBSD__ +#define bswap_16 swap16 +#define bswap_32 swap32 +#define bswap_64 swap64 +#else /* __OpenBSD__ */ +#define bswap_16 bswap16 +#define bswap_32 bswap32 +#define bswap_64 bswap64 +#endif /* __OpenBSD__ */ +#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || + * defined(__DragonFly__) || defined(__OpenBSD__) */ + +#ifdef __APPLE__ +#include +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#define __BIG_ENDIAN _BIG_ENDIAN +static inline unsigned short bswap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int bswap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} +#endif /* __APPLE__ */ + +#ifdef CONFIG_TI_COMPILER +#define __BIG_ENDIAN 4321 +#define __LITTLE_ENDIAN 1234 +#ifdef __big_endian__ +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif +#endif /* CONFIG_TI_COMPILER */ + +#ifdef __SYMBIAN32__ +#define __BIG_ENDIAN 4321 +#define __LITTLE_ENDIAN 1234 +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif /* __SYMBIAN32__ */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include + +typedef int socklen_t; + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 /* not supported */ +#endif + +#endif /* CONFIG_NATIVE_WINDOWS */ + +#ifdef _MSC_VER +#define inline __inline + +#undef vsnprintf +#define vsnprintf _vsnprintf +#undef close +#define close closesocket +#endif /* _MSC_VER */ + + +/* Define platform specific integer types */ + +#ifdef _MSC_VER +typedef UINT64 u64; +typedef UINT32 u32; +typedef UINT16 u16; +typedef UINT8 u8; +typedef INT64 s64; +typedef INT32 s32; +typedef INT16 s16; +typedef INT8 s8; +#define WPA_TYPES_DEFINED +#endif /* _MSC_VER */ + +#ifdef __vxworks +typedef unsigned long long u64; +typedef UINT32 u32; +typedef UINT16 u16; +typedef UINT8 u8; +typedef long long s64; +typedef INT32 s32; +typedef INT16 s16; +typedef INT8 s8; +#define WPA_TYPES_DEFINED +#endif /* __vxworks */ + +#ifdef CONFIG_TI_COMPILER +#ifdef _LLONG_AVAILABLE +typedef unsigned long long u64; +#else +/* + * TODO: 64-bit variable not available. Using long as a workaround to test the + * build, but this will likely not work for all operations. + */ +typedef unsigned long u64; +#endif +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +#define WPA_TYPES_DEFINED +#endif /* CONFIG_TI_COMPILER */ + +#ifdef __SYMBIAN32__ +#define __REMOVE_PLATSEC_DIAGNOSTICS__ +#include +typedef TUint64 u64; +typedef TUint32 u32; +typedef TUint16 u16; +typedef TUint8 u8; +#define WPA_TYPES_DEFINED +#endif /* __SYMBIAN32__ */ + +#ifndef WPA_TYPES_DEFINED +#ifdef CONFIG_USE_INTTYPES_H +#include +#else +#include +#endif + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; +#define WPA_TYPES_DEFINED +#endif /* !WPA_TYPES_DEFINED */ + + +/* Define platform specific byte swapping macros */ + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) + +static inline unsigned short wpa_swap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int wpa_swap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) wpa_swap_16(n) +#define host_to_be16(n) wpa_swap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) wpa_swap_32(n) +#define host_to_be32(n) wpa_swap_32(n) + +#define WPA_BYTE_SWAP_DEFINED + +#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */ + + +#ifndef WPA_BYTE_SWAP_DEFINED + +#ifndef __BYTE_ORDER +#ifndef __LITTLE_ENDIAN +#ifndef __BIG_ENDIAN +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined(sparc) +#define __BYTE_ORDER __BIG_ENDIAN +#endif +#endif /* __BIG_ENDIAN */ +#endif /* __LITTLE_ENDIAN */ +#endif /* __BYTE_ORDER */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le_to_host16(n) ((__force u16) (le16) (n)) +#define host_to_le16(n) ((__force le16) (u16) (n)) +#define be_to_host16(n) bswap_16((__force u16) (be16) (n)) +#define host_to_be16(n) ((__force be16) bswap_16((n))) +#define le_to_host32(n) ((__force u32) (le32) (n)) +#define host_to_le32(n) ((__force le32) (u32) (n)) +#define be_to_host32(n) bswap_32((__force u32) (be32) (n)) +#define host_to_be32(n) ((__force be32) bswap_32((n))) +#define le_to_host64(n) ((__force u64) (le64) (n)) +#define host_to_le64(n) ((__force le64) (u64) (n)) +#define be_to_host64(n) bswap_64((__force u64) (be64) (n)) +#define host_to_be64(n) ((__force be64) bswap_64((n))) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le_to_host16(n) bswap_16(n) +#define host_to_le16(n) bswap_16(n) +#define be_to_host16(n) (n) +#define host_to_be16(n) (n) +#define le_to_host32(n) bswap_32(n) +#define be_to_host32(n) (n) +#define host_to_be32(n) (n) +#define le_to_host64(n) bswap_64(n) +#define host_to_le64(n) bswap_64(n) +#define be_to_host64(n) (n) +#define host_to_be64(n) (n) +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN +#endif +#else +#error Could not determine CPU byte order +#endif + +#define WPA_BYTE_SWAP_DEFINED +#endif /* !WPA_BYTE_SWAP_DEFINED */ + + +/* Macros for handling unaligned memory accesses */ + +#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define WPA_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define WPA_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ + ((u32) (a)[2])) +#define WPA_PUT_BE24(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[2] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ + (((u32) (a)[2]) << 8) | ((u32) (a)[3])) +#define WPA_PUT_BE32(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[3] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ + (((u32) (a)[1]) << 8) | ((u32) (a)[0])) +#define WPA_PUT_LE32(a, val) \ + do { \ + (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[0] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ + (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ + (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ + (((u64) (a)[6]) << 8) | ((u64) (a)[7])) +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (u8) (((u64) (val)) >> 56); \ + (a)[1] = (u8) (((u64) (val)) >> 48); \ + (a)[2] = (u8) (((u64) (val)) >> 40); \ + (a)[3] = (u8) (((u64) (val)) >> 32); \ + (a)[4] = (u8) (((u64) (val)) >> 24); \ + (a)[5] = (u8) (((u64) (val)) >> 16); \ + (a)[6] = (u8) (((u64) (val)) >> 8); \ + (a)[7] = (u8) (((u64) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ + (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ + (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ + (((u64) (a)[1]) << 8) | ((u64) (a)[0])) + + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif +#ifndef ETH_P_ALL +#define ETH_P_ALL 0x0003 +#endif +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL ETH_P_PAE +#endif /* ETH_P_EAPOL */ +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif /* ETH_P_RSN_PREAUTH */ +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ + + +#ifdef __GNUC__ +#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) +#define STRUCT_PACKED __attribute__ ((packed)) +#else +#define PRINTF_FORMAT(a,b) +#define STRUCT_PACKED +#endif + +#ifdef CONFIG_ANSI_C_EXTRA + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +/* snprintf - used in number of places; sprintf() is _not_ a good replacement + * due to possible buffer overflow; see, e.g., + * http://www.ijs.si/software/snprintf/ for portable implementation of + * snprintf. */ +int snprintf(char *str, size_t size, const char *format, ...); + +/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */ +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */ + +/* getopt - only used in main.c */ +int getopt(int argc, char *const argv[], const char *optstring); +extern char *optarg; +extern int optind; + +#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF +#ifndef __socklen_t_defined +typedef int socklen_t; +#endif +#endif + +/* inline - define as __inline or just define it to be empty, if needed */ +#ifdef CONFIG_NO_INLINE +#define inline +#else +#define inline __inline +#endif + +#ifndef __func__ +#define __func__ "__func__ not defined" +#endif + +#ifndef bswap_16 +#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff)) +#endif + +#ifndef bswap_32 +#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \ + (((u32) (a) << 8) & 0xff0000) | \ + (((u32) (a) >> 8) & 0xff00) | \ + (((u32) (a) >> 24) & 0xff)) +#endif + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif + +#ifdef _WIN32_WCE +void perror(const char *s); +#endif /* _WIN32_WCE */ + +#endif /* CONFIG_ANSI_C_EXTRA */ + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +/* + * Definitions for sparse validation + * (http://kernel.org/pub/linux/kernel/people/josh/sparse/) + */ +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef u16 __bitwise be16; +typedef u16 __bitwise le16; +typedef u32 __bitwise be32; +typedef u32 __bitwise le32; +typedef u64 __bitwise be64; +typedef u64 __bitwise le64; + +#ifndef __must_check +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __must_check __attribute__((__warn_unused_result__)) +#else +#define __must_check +#endif /* __GNUC__ */ +#endif /* __must_check */ + +int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_aton2(const char *txt, u8 *addr); +int hexstr2bin(const char *hex, u8 *buf, size_t len); +void inc_byte_array(u8 *counter, size_t len); +void wpa_get_ntp_timestamp(u8 *buf); +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len); + +#ifdef CONFIG_NATIVE_WINDOWS +void wpa_unicode2ascii_inplace(TCHAR *str); +TCHAR * wpa_strdup_tchar(const char *str); +#else /* CONFIG_NATIVE_WINDOWS */ +#define wpa_unicode2ascii_inplace(s) do { } while (0) +#define wpa_strdup_tchar(s) strdup((s)) +#endif /* CONFIG_NATIVE_WINDOWS */ + +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); + +static inline int is_zero_ether_addr(const u8 *a) +{ + return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); +} + +/* + * gcc 4.4 ends up generating strict-aliasing warnings about some very common + * networking socket uses that do not really result in a real problem and + * cannot be easily avoided with union-based type-punning due to struct + * definitions including another struct in system header files. To avoid having + * to fully disable strict-aliasing warnings, provide a mechanism to hide the + * typecast from aliasing for now. A cleaner solution will hopefully be found + * in the future to handle these cases. + */ +void * __hide_aliasing_typecast(void *foo); +#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) + +#endif /* COMMON_H */ diff --git a/components/wpa_supplicant/include/crypto/crypto.h b/components/wpa_supplicant/include/crypto/crypto.h new file mode 100644 index 0000000000..f0b9f22430 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/crypto.h @@ -0,0 +1,471 @@ +/* + * WPA Supplicant / wrapper functions for crypto libraries + * Copyright (c) 2004-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file defines the cryptographic functions that need to be implemented + * for wpa_supplicant and hostapd. When TLS is not used, internal + * implementation of MD5, SHA1, and AES is used and no external libraries are + * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the + * crypto library used by the TLS implementation is expected to be used for + * non-TLS needs, too, in order to save space by not implementing these + * functions twice. + * + * Wrapper code for using each crypto library is in its own file (crypto*.c) + * and one of these files is build and linked in to provide the functions + * defined here. + */ + +#ifndef CRYPTO_H +#define CRYPTO_H + +/** + * md4_vector - MD4 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +#ifdef CONFIG_FIPS +/** + * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed) + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac); +#else /* CONFIG_FIPS */ +#define md5_vector_non_fips_allow md5_vector +#endif /* CONFIG_FIPS */ + + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF + * @seed: Seed/key for the PRF + * @seed_len: Seed length in bytes + * @x: Buffer for PRF output + * @xlen: Output length in bytes + * Returns: 0 on success, -1 on failure + * + * This function implements random number generation specified in NIST FIPS + * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to + * SHA-1, but has different message padding. + */ +int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, + size_t xlen); + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * des_encrypt - Encrypt one block with DES + * @clear: 8 octets (in) + * @key: 7 octets (in) (no parity bits included) + * @cypher: 8 octets (out) + */ +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); + +/** + * aes_encrypt_init - Initialize AES for encryption + * @key: Encryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_encrypt_init(const u8 *key, size_t len); + +/** + * aes_encrypt - Encrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @plain: Plaintext data to be encrypted (16 bytes) + * @crypt: Buffer for the encrypted data (16 bytes) + */ +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); + +/** + * aes_encrypt_deinit - Deinitialize AES encryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_encrypt_deinit(void *ctx); + +/** + * aes_decrypt_init - Initialize AES for decryption + * @key: Decryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_decrypt_init(const u8 *key, size_t len); + +/** + * aes_decrypt - Decrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @crypt: Encrypted data (16 bytes) + * @plain: Buffer for the decrypted data (16 bytes) + */ +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); + +/** + * aes_decrypt_deinit - Deinitialize AES decryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_decrypt_deinit(void *ctx); + + +enum crypto_hash_alg { + CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, + CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 +}; + + +struct crypto_hash; + +/** + * crypto_hash_init - Initialize hash/HMAC function + * @alg: Hash algorithm + * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed + * @key_len: Length of the key in bytes + * Returns: Pointer to hash context to use with other hash functions or %NULL + * on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len); + +/** + * crypto_hash_update - Add data to hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @data: Data buffer to add + * @len: Length of the buffer + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); + +/** + * crypto_hash_finish - Complete hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @hash: Buffer for hash value or %NULL if caller is just freeing the hash + * context + * @len: Pointer to length of the buffer or %NULL if caller is just freeing the + * hash context; on return, this is set to the actual length of the hash value + * Returns: 0 on success, -1 if buffer is too small (len set to needed length), + * or -2 on other failures (including failed crypto_hash_update() operations) + * + * This function calculates the hash value and frees the context buffer that + * was used for hash calculation. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); + + +enum crypto_cipher_alg { + CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, + CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4 +}; + +struct crypto_cipher; + +/** + * crypto_cipher_init - Initialize block/stream cipher function + * @alg: Cipher algorithm + * @iv: Initialization vector for block ciphers or %NULL for stream ciphers + * @key: Cipher key + * @key_len: Length of key in bytes + * Returns: Pointer to cipher context to use with other cipher functions or + * %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len); + +/** + * crypto_cipher_encrypt - Cipher encrypt + * @ctx: Context pointer from crypto_cipher_init() + * @plain: Plaintext to cipher + * @crypt: Resulting ciphertext + * @len: Length of the plaintext + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, + const u8 *plain, u8 *crypt, size_t len); + +/** + * crypto_cipher_decrypt - Cipher decrypt + * @ctx: Context pointer from crypto_cipher_init() + * @crypt: Ciphertext to decrypt + * @plain: Resulting plaintext + * @len: Length of the cipher text + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, + const u8 *crypt, u8 *plain, size_t len); + +/** + * crypto_cipher_decrypt - Free cipher context + * @ctx: Context pointer from crypto_cipher_init() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_cipher_deinit(struct crypto_cipher *ctx); + + +struct crypto_public_key; +struct crypto_private_key; + +/** + * crypto_public_key_import - Import an RSA public key + * @key: Key buffer (DER encoded RSA public key) + * @len: Key buffer length in bytes + * Returns: Pointer to the public key or %NULL on failure + * + * This function can just return %NULL if the crypto library supports X.509 + * parsing. In that case, crypto_public_key_from_cert() is used to import the + * public key from a certificate. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); + +/** + * crypto_private_key_import - Import an RSA private key + * @key: Key buffer (DER encoded RSA private key) + * @len: Key buffer length in bytes + * @passwd: Key encryption password or %NULL if key is not encrypted + * Returns: Pointer to the private key or %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd); + +/** + * crypto_public_key_from_cert - Import an RSA public key from a certificate + * @buf: DER encoded X.509 certificate + * @len: Certificate buffer length in bytes + * Returns: Pointer to public key or %NULL on failure + * + * This function can just return %NULL if the crypto library does not support + * X.509 parsing. In that case, internal code will be used to parse the + * certificate and public key is imported using crypto_public_key_import(). + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len); + +/** + * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5) + * @key: Public key + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_public_key_encrypt_pkcs1_v15( + struct crypto_public_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5) + * @key: Private key + * @in: Encrypted buffer + * @inlen: Length of encrypted buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_private_key_decrypt_pkcs1_v15( + struct crypto_private_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1) + * @key: Private key from crypto_private_key_import() + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted (signed) data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_public_key_free - Free public key + * @key: Public key + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_public_key_free(struct crypto_public_key *key); + +/** + * crypto_private_key_free - Free private key + * @key: Private key from crypto_private_key_import() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_private_key_free(struct crypto_private_key *key); + +/** + * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature + * @key: Public key + * @crypt: Encrypted signature data (using the private key) + * @crypt_len: Encrypted signature data length + * @plain: Buffer for plaintext (at least crypt_len bytes) + * @plain_len: Plaintext length (max buffer size on input, real len on output); + * Returns: 0 on success, -1 on failure + */ +int __must_check crypto_public_key_decrypt_pkcs1( + struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); + +/** + * crypto_global_init - Initialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_global_init(void); + +/** + * crypto_global_deinit - Deinitialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_global_deinit(void); + +/** + * crypto_mod_exp - Modular exponentiation of large integers + * @base: Base integer (big endian byte array) + * @base_len: Length of base integer in bytes + * @power: Power integer (big endian byte array) + * @power_len: Length of power integer in bytes + * @modulus: Modulus integer (big endian byte array) + * @modulus_len: Length of modulus integer in bytes + * @result: Buffer for the result + * @result_len: Result length (max buffer size on input, real len on output) + * Returns: 0 on success, -1 on failure + * + * This function calculates result = base ^ power mod modulus. modules_len is + * used as the maximum size of modulus buffer. It is set to the used size on + * success. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len); + +/** + * rc4_skip - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * Returns: 0 on success, -1 on failure + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); + +#endif /* CRYPTO_H */ diff --git a/components/wpa_supplicant/include/crypto/dh_group5.h b/components/wpa_supplicant/include/crypto/dh_group5.h new file mode 100644 index 0000000000..595f1114fe --- /dev/null +++ b/components/wpa_supplicant/include/crypto/dh_group5.h @@ -0,0 +1,23 @@ +/* + * Diffie-Hellman group 5 operations + * Copyright (c) 2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DH_GROUP5_H +#define DH_GROUP5_H + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private); +void dh5_free(void *ctx); + +#endif /* DH_GROUP5_H */ diff --git a/components/wpa_supplicant/include/crypto/dh_groups.h b/components/wpa_supplicant/include/crypto/dh_groups.h new file mode 100644 index 0000000000..5c61539b70 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/dh_groups.h @@ -0,0 +1,32 @@ +/* + * Diffie-Hellman groups + * Copyright (c) 2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DH_GROUPS_H +#define DH_GROUPS_H + +struct dh_group { + int id; + const u8 *generator; + size_t generator_len; + const u8 *prime; + size_t prime_len; +}; + +const struct dh_group * dh_groups_get(int id); +struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv); +struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, + const struct wpabuf *own_private, + const struct dh_group *dh); + +#endif /* DH_GROUPS_H */ diff --git a/components/wpa_supplicant/include/crypto/includes.h b/components/wpa_supplicant/include/crypto/includes.h new file mode 100644 index 0000000000..dbc65759b0 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/includes.h @@ -0,0 +1,65 @@ +/* + * wpa_supplicant/hostapd - Default include files + * Copyright (c) 2005-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This header file is included into all C files so that commonly used header + * files can be selected with OS specific ifdef blocks in one place instead of + * having to have OS/C library specific selection in many files. + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +/* Include possible build time configuration before including anything else */ +//#include "build_config.h" //don't need anymore +#ifndef __ets__ +#include +#include +#include +#include +#ifndef _WIN32_WCE +#ifndef CONFIG_TI_COMPILER +#include +#include +#endif /* CONFIG_TI_COMPILER */ +#include +#endif /* _WIN32_WCE */ +#include +#include + +#ifndef CONFIG_TI_COMPILER +#ifndef _MSC_VER +#include +#endif /* _MSC_VER */ +#endif /* CONFIG_TI_COMPILER */ + +#ifndef CONFIG_NATIVE_WINDOWS +#ifndef CONFIG_TI_COMPILER +//#include +//#include +//#include +#ifndef __vxworks +#ifndef __SYMBIAN32__ +//#include +#endif /* __SYMBIAN32__ */ +#include +#endif /* __vxworks */ +#endif /* CONFIG_TI_COMPILER */ +#endif /* CONFIG_NATIVE_WINDOWS */ + +#else + +#include "rom/ets_sys.h" + +#endif /* !__ets__ */ + +#endif /* INCLUDES_H */ diff --git a/components/wpa_supplicant/include/crypto/md5.h b/components/wpa_supplicant/include/crypto/md5.h new file mode 100644 index 0000000000..8952590782 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/md5.h @@ -0,0 +1,35 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MD5_H +#define MD5_H + +#define MD5_MAC_LEN 16 + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +#ifdef CONFIG_FIPS +int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac); +int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +#else /* CONFIG_FIPS */ +#define hmac_md5_vector_non_fips_allow hmac_md5_vector +#define hmac_md5_non_fips_allow hmac_md5 +#endif /* CONFIG_FIPS */ + +#endif /* MD5_H */ diff --git a/components/wpa_supplicant/include/crypto/md5_i.h b/components/wpa_supplicant/include/crypto/md5_i.h new file mode 100644 index 0000000000..b7f6596052 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/md5_i.h @@ -0,0 +1,29 @@ +/* + * MD5 internal definitions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MD5_I_H +#define MD5_I_H + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); + +#endif /* MD5_I_H */ diff --git a/components/wpa_supplicant/include/crypto/random.h b/components/wpa_supplicant/include/crypto/random.h new file mode 100644 index 0000000000..cbfa8773fd --- /dev/null +++ b/components/wpa_supplicant/include/crypto/random.h @@ -0,0 +1,34 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef RANDOM_H +#define RANDOM_H + +#define CONFIG_NO_RANDOM_POOL + +#ifdef CONFIG_NO_RANDOM_POOL +#define random_init(e) do { } while (0) +#define random_deinit() do { } while (0) +#define random_add_randomness(b, l) do { } while (0) +#define random_get_bytes(b, l) os_get_random((b), (l)) +#define random_pool_ready() 1 +#define random_mark_pool_ready() do { } while (0) +#else /* CONFIG_NO_RANDOM_POOL */ +void random_init(const char *entropy_file); +void random_deinit(void); +void random_add_randomness(const void *buf, size_t len); +int random_get_bytes(void *buf, size_t len); +#endif /* CONFIG_NO_RANDOM_POOL */ + +#endif /* RANDOM_H */ diff --git a/components/wpa_supplicant/include/crypto/sha1.h b/components/wpa_supplicant/include/crypto/sha1.h new file mode 100644 index 0000000000..b3d186bdbc --- /dev/null +++ b/components/wpa_supplicant/include/crypto/sha1.h @@ -0,0 +1,33 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SHA1_H +#define SHA1_H + +#define SHA1_MAC_LEN 20 + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); +//int __must_check tls_prf(const u8 *secret, size_t secret_len, +// const char *label, const u8 *seed, size_t seed_len, +// u8 *out, size_t outlen); +int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); +#endif /* SHA1_H */ diff --git a/components/wpa_supplicant/include/crypto/sha1_i.h b/components/wpa_supplicant/include/crypto/sha1_i.h new file mode 100644 index 0000000000..ec2f82f75b --- /dev/null +++ b/components/wpa_supplicant/include/crypto/sha1_i.h @@ -0,0 +1,29 @@ +/* + * SHA1 internal definitions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SHA1_I_H +#define SHA1_I_H + +struct SHA1Context { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +}; + +void SHA1Init(struct SHA1Context *context); +void SHA1Update(struct SHA1Context *context, const void *data, u32 len); +void SHA1Final(unsigned char digest[20], struct SHA1Context *context); +void SHA1Transform(u32 state[5], const unsigned char buffer[64]); + +#endif /* SHA1_I_H */ diff --git a/components/wpa_supplicant/include/crypto/sha256.h b/components/wpa_supplicant/include/crypto/sha256.h new file mode 100644 index 0000000000..dc597f09b5 --- /dev/null +++ b/components/wpa_supplicant/include/crypto/sha256.h @@ -0,0 +1,27 @@ +/* + * SHA256 hash implementation and interface functions + * Copyright (c) 2003-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SHA256_H +#define SHA256_H + +#define SHA256_MAC_LEN 32 + +void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); + +#endif /* SHA256_H */ diff --git a/components/wpa_supplicant/include/wpa/ap_config.h b/components/wpa_supplicant/include/wpa/ap_config.h new file mode 100644 index 0000000000..761becb484 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/ap_config.h @@ -0,0 +1,544 @@ +/* + * hostapd / Configuration definitions and helpers functions + * Copyright (c) 2003-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HOSTAPD_CONFIG_H +#define HOSTAPD_CONFIG_H + +#include "wpa/defs.h" +//#include "ip_addr.h" +#include "wpa/wpa_common.h" +//#include "common/ieee802_11_common.h" +//#include "wps/wps.h" + +#define MAX_STA_COUNT 4 +#define MAX_VLAN_ID 4094 + +typedef u8 macaddr[ETH_ALEN]; + +struct mac_acl_entry { + macaddr addr; + int vlan_id; +}; + +struct hostapd_radius_servers; +struct ft_remote_r0kh; +struct ft_remote_r1kh; + +#define HOSTAPD_MAX_SSID_LEN 32 + +#define NUM_WEP_KEYS 4 +struct hostapd_wep_keys { + u8 idx; + u8 *key[NUM_WEP_KEYS]; + size_t len[NUM_WEP_KEYS]; + int keys_set; + size_t default_len; /* key length used for dynamic key generation */ +}; + +typedef enum hostap_security_policy { + SECURITY_PLAINTEXT = 0, + SECURITY_STATIC_WEP = 1, + SECURITY_IEEE_802_1X = 2, + SECURITY_WPA_PSK = 3, + SECURITY_WPA = 4 +} secpolicy; + +struct hostapd_ssid { + u8 ssid[HOSTAPD_MAX_SSID_LEN]; + size_t ssid_len; + unsigned int ssid_set:1; + unsigned int utf8_ssid:1; + +// char vlan[IFNAMSIZ + 1]; +// secpolicy security_policy; + + struct hostapd_wpa_psk *wpa_psk; + char *wpa_passphrase; +// char *wpa_psk_file; + + struct hostapd_wep_keys wep; + +#if 0 +#define DYNAMIC_VLAN_DISABLED 0 +#define DYNAMIC_VLAN_OPTIONAL 1 +#define DYNAMIC_VLAN_REQUIRED 2 + int dynamic_vlan; +#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0 +#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 +#define DYNAMIC_VLAN_NAMING_END 2 + int vlan_naming; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + char *vlan_tagged_interface; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + struct hostapd_wep_keys **dyn_vlan_keys; + size_t max_dyn_vlan_keys; +#endif +}; + +#if 0 +#define VLAN_ID_WILDCARD -1 + +struct hostapd_vlan { + struct hostapd_vlan *next; + int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + char ifname[IFNAMSIZ + 1]; + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 +#define DVLAN_CLEAN_WLAN_PORT 0x8 + int clean; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +}; +#endif + +#define PMK_LEN 32 +struct hostapd_sta_wpa_psk_short { + struct hostapd_sta_wpa_psk_short *next; + u8 psk[PMK_LEN]; +}; + +struct hostapd_wpa_psk { + struct hostapd_wpa_psk *next; + int group; + u8 psk[PMK_LEN]; + u8 addr[ETH_ALEN]; +}; + +#if 0 +struct hostapd_eap_user { + struct hostapd_eap_user *next; + u8 *identity; + size_t identity_len; + struct { + int vendor; + u32 method; + } methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; + int phase2; + int force_version; + unsigned int wildcard_prefix:1; + unsigned int password_hash:1; /* whether password is hashed with + * nt_password_hash() */ + int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ +}; + +struct hostapd_radius_attr { + u8 type; + struct wpabuf *val; + struct hostapd_radius_attr *next; +}; + + +#define NUM_TX_QUEUES 4 + +struct hostapd_tx_queue_params { + int aifs; + int cwmin; + int cwmax; + int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ +}; + + +#define MAX_ROAMING_CONSORTIUM_LEN 15 + +struct hostapd_roaming_consortium { + u8 len; + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; +}; + +struct hostapd_lang_string { + u8 lang[3]; + u8 name_len; + u8 name[252]; +}; + +#define MAX_NAI_REALMS 10 +#define MAX_NAI_REALMLEN 255 +#define MAX_NAI_EAP_METHODS 5 +#define MAX_NAI_AUTH_TYPES 4 +struct hostapd_nai_realm_data { + u8 encoding; + char realm_buf[MAX_NAI_REALMLEN + 1]; + char *realm[MAX_NAI_REALMS]; + u8 eap_method_count; + struct hostapd_nai_realm_eap { + u8 eap_method; + u8 num_auths; + u8 auth_id[MAX_NAI_AUTH_TYPES]; + u8 auth_val[MAX_NAI_AUTH_TYPES]; + } eap_method[MAX_NAI_EAP_METHODS]; +}; +#endif + +/** + * struct hostapd_bss_config - Per-BSS configuration + */ +struct hostapd_bss_config { +// char iface[IFNAMSIZ + 1]; +// char bridge[IFNAMSIZ + 1]; +// char wds_bridge[IFNAMSIZ + 1]; + +// enum hostapd_logger_level logger_syslog_level, logger_stdout_level; + +// unsigned int logger_syslog; /* module bitfield */ +// unsigned int logger_stdout; /* module bitfield */ + +// char *dump_log_name; /* file name for state dump (SIGUSR1) */ + + int max_num_sta; /* maximum number of STAs in station table */ + + int dtim_period; + + int ieee802_1x; /* use IEEE 802.1X */ + int eapol_version; +// int eap_server; /* Use internal EAP server instead of external +// * RADIUS server */ +// struct hostapd_eap_user *eap_user; +// char *eap_user_sqlite; +// char *eap_sim_db; +// struct hostapd_ip_addr own_ip_addr; +// char *nas_identifier; +// struct hostapd_radius_servers *radius; +// int acct_interim_interval; +// int radius_request_cui; +// struct hostapd_radius_attr *radius_auth_req_attr; +// struct hostapd_radius_attr *radius_acct_req_attr; +// int radius_das_port; +// unsigned int radius_das_time_window; +// int radius_das_require_event_timestamp; +// struct hostapd_ip_addr radius_das_client_addr; +// u8 *radius_das_shared_secret; +// size_t radius_das_shared_secret_len; + + struct hostapd_ssid ssid; + +// char *eap_req_id_text; /* optional displayable message sent with +// * EAP Request-Identity */ +// size_t eap_req_id_text_len; +// int eapol_key_index_workaround; + +// size_t default_wep_key_len; +// int individual_wep_key_len; + int wep_rekeying_period; + int broadcast_key_idx_min, broadcast_key_idx_max; +// int eap_reauth_period; + +// int ieee802_11f; /* use IEEE 802.11f (IAPP) */ +// char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast +// * frames */ + + enum { + ACCEPT_UNLESS_DENIED = 0, + DENY_UNLESS_ACCEPTED = 1, + USE_EXTERNAL_RADIUS_AUTH = 2 + } macaddr_acl; +// struct mac_acl_entry *accept_mac; +// int num_accept_mac; +// struct mac_acl_entry *deny_mac; +// int num_deny_mac; +// int wds_sta; +// int isolate; + + int auth_algs; /* bitfield of allowed IEEE 802.11 authentication + * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ + + int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ + int wpa_key_mgmt; +#ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; + /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ + unsigned int assoc_sa_query_max_timeout; + /* dot11AssociationSAQueryRetryTimeout (in TUs) */ + int assoc_sa_query_retry_timeout; +#endif /* CONFIG_IEEE80211W */ + enum { + PSK_RADIUS_IGNORED = 0, + PSK_RADIUS_ACCEPTED = 1, + PSK_RADIUS_REQUIRED = 2 + } wpa_psk_radius; + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + char *rsn_preauth_interfaces; + int peerkey; + +#ifdef CONFIG_IEEE80211R + /* IEEE 802.11r - Fast BSS Transition */ + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; + int ft_over_ds; +#endif /* CONFIG_IEEE80211R */ + +// char *ctrl_interface; /* directory for UNIX domain sockets */ +#ifndef CONFIG_NATIVE_WINDOWS +// gid_t ctrl_interface_gid; +#endif /* CONFIG_NATIVE_WINDOWS */ +// int ctrl_interface_gid_set; + +// char *ca_cert; +// char *server_cert; +// char *private_key; +// char *private_key_passwd; +// int check_crl; +// char *dh_file; +// u8 *pac_opaque_encr_key; +// u8 *eap_fast_a_id; +// size_t eap_fast_a_id_len; +// char *eap_fast_a_id_info; +// int eap_fast_prov; +// int pac_key_lifetime; +// int pac_key_refresh_time; +// int eap_sim_aka_result_ind; +// int tnc; +// int fragment_size; +// u16 pwd_group; + +// char *radius_server_clients; +// int radius_server_auth_port; +// int radius_server_ipv6; + +// char *test_socket; /* UNIX domain socket path for driver_test */ + +// int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group +// * address instead of individual address +// * (for driver_wired.c). +// */ + + int ap_max_inactivity; + int ignore_broadcast_ssid; + + int wmm_enabled; + int wmm_uapsd; + +// struct hostapd_vlan *vlan, *vlan_tail; + + macaddr bssid; + + /* + * Maximum listen interval that STAs can use when associating with this + * BSS. If a STA tries to use larger value, the association will be + * denied with status code 51. + */ + u16 max_listen_interval; + +// int disable_pmksa_caching; +// int okc; /* Opportunistic Key Caching */ + +// int wps_state; +#ifdef CONFIG_WPS + int ap_setup_locked; + u8 uuid[16]; + char *wps_pin_requests; + char *device_name; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + u8 device_type[WPS_DEV_TYPE_LEN]; + char *config_methods; + u8 os_version[4]; + char *ap_pin; + int skip_cred_build; + u8 *extra_cred; + size_t extra_cred_len; + int wps_cred_processing; + u8 *ap_settings; + size_t ap_settings_len; + char *upnp_iface; + char *friendly_name; + char *manufacturer_url; + char *model_description; + char *model_url; + char *upc; + struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; + int wps_nfc_dev_pw_id; + struct wpabuf *wps_nfc_dh_pubkey; + struct wpabuf *wps_nfc_dh_privkey; + struct wpabuf *wps_nfc_dev_pw; +#endif /* CONFIG_WPS */ +// int pbc_in_m1; + +#define P2P_ENABLED BIT(0) +#define P2P_GROUP_OWNER BIT(1) +#define P2P_GROUP_FORMATION BIT(2) +#define P2P_MANAGE BIT(3) +#define P2P_ALLOW_CROSS_CONNECTION BIT(4) +// int p2p; + +// int disassoc_low_ack; +// int skip_inactivity_poll; + +#define TDLS_PROHIBIT BIT(0) +#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) +// int tdls; +// int disable_11n; +// int disable_11ac; + + /* IEEE 802.11v */ +// int time_advertisement; +// char *time_zone; +// int wnm_sleep_mode; +// int bss_transition; + + /* IEEE 802.11u - Interworking */ +// int interworking; +// int access_network_type; +// int internet; +// int asra; +// int esr; +// int uesa; +// int venue_info_set; +// u8 venue_group; +// u8 venue_type; +// u8 hessid[ETH_ALEN]; + + /* IEEE 802.11u - Roaming Consortium list */ +// unsigned int roaming_consortium_count; +// struct hostapd_roaming_consortium *roaming_consortium; + + /* IEEE 802.11u - Venue Name duples */ +// unsigned int venue_name_count; +// struct hostapd_lang_string *venue_name; + + /* IEEE 802.11u - Network Authentication Type */ +// u8 *network_auth_type; +// size_t network_auth_type_len; + + /* IEEE 802.11u - IP Address Type Availability */ +// u8 ipaddr_type_availability; +// u8 ipaddr_type_configured; + + /* IEEE 802.11u - 3GPP Cellular Network */ +// u8 *anqp_3gpp_cell_net; +// size_t anqp_3gpp_cell_net_len; + + /* IEEE 802.11u - Domain Name */ +// u8 *domain_name; +// size_t domain_name_len; + +// unsigned int nai_realm_count; +// struct hostapd_nai_realm_data *nai_realm_data; + +// u16 gas_comeback_delay; +// int gas_frag_limit; + +#ifdef CONFIG_HS20 + int hs20; + int disable_dgaf; + unsigned int hs20_oper_friendly_name_count; + struct hostapd_lang_string *hs20_oper_friendly_name; + u8 *hs20_wan_metrics; + u8 *hs20_connection_capability; + size_t hs20_connection_capability_len; + u8 *hs20_operating_class; + u8 hs20_operating_class_len; +#endif /* CONFIG_HS20 */ + +// u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ + +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ + +// struct wpabuf *vendor_elements; +}; + + +/** + * struct hostapd_config - Per-radio interface configuration + */ +struct hostapd_config { + struct hostapd_bss_config *bss, *last_bss; + size_t num_bss; + + u16 beacon_int; + int rts_threshold; + int fragm_threshold; + u8 send_probe_response; + u8 channel; + enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + enum { + LONG_PREAMBLE = 0, + SHORT_PREAMBLE = 1 + } preamble; + + int *supported_rates; + int *basic_rates; + + const struct wpa_driver_ops *driver; + + int ap_table_max_size; + int ap_table_expiration_time; + + char country[3]; /* first two octets: country code as described in + * ISO/IEC 3166-1. Third octet: + * ' ' (ascii 32): all environments + * 'O': Outdoor environemnt only + * 'I': Indoor environment only + */ + + int ieee80211d; + +// struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; + + /* + * WMM AC parameters, in same order as 802.1D, i.e. + * 0 = BE (best effort) + * 1 = BK (background) + * 2 = VI (video) + * 3 = VO (voice) + */ +// struct hostapd_wmm_ac_params wmm_ac_params[4]; + + int ht_op_mode_fixed; + u16 ht_capab; + int ieee80211n; + int secondary_channel; + int require_ht; + u32 vht_capab; + int ieee80211ac; + int require_vht; + u8 vht_oper_chwidth; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; +}; + + +int hostapd_mac_comp(const void *a, const void *b); +int hostapd_mac_comp_empty(const void *a); +struct hostapd_config * hostapd_config_defaults(void); +void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); +void hostapd_config_free(struct hostapd_config *conf); +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id); +int hostapd_rate_found(int *list, int rate); +int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, + struct hostapd_wep_keys *b); +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *prev_psk); +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +//const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, +// int vlan_id); +//struct hostapd_radius_attr * +//hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); + +#endif /* HOSTAPD_CONFIG_H */ diff --git a/components/wpa_supplicant/include/wpa/common.h b/components/wpa_supplicant/include/wpa/common.h new file mode 100644 index 0000000000..ca80c2394f --- /dev/null +++ b/components/wpa_supplicant/include/wpa/common.h @@ -0,0 +1,324 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +#if defined(__ets__) +#endif /* ets */ +#include "os.h" + +#if defined(__XTENSA__) +#include +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#endif /*__XTENSA__*/ + +#if defined(__linux__) || defined(__GLIBC__) || defined(__ets__) +#include +#include +#endif /* __linux__ */ + +/* Define platform specific byte swapping macros */ + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) + +static inline unsigned short wpa_swap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int wpa_swap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) wpa_swap_16(n) +#define host_to_be16(n) wpa_swap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) wpa_swap_32(n) +#define host_to_be32(n) wpa_swap_32(n) + +#define WPA_BYTE_SWAP_DEFINED + +#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */ + + +#ifndef WPA_BYTE_SWAP_DEFINED + +#ifndef __BYTE_ORDER +#ifndef __LITTLE_ENDIAN +#ifndef __BIG_ENDIAN +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined(sparc) +#define __BYTE_ORDER __BIG_ENDIAN +#endif +#endif /* __BIG_ENDIAN */ +#endif /* __LITTLE_ENDIAN */ +#endif /* __BYTE_ORDER */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le_to_host16(n) ((__force u16) (le16) (n)) +#define host_to_le16(n) ((__force le16) (u16) (n)) +#define be_to_host16(n) __bswap_16((__force u16) (be16) (n)) +#define host_to_be16(n) ((__force be16) __bswap_16((n))) +#define le_to_host32(n) ((__force u32) (le32) (n)) +#define host_to_le32(n) ((__force le32) (u32) (n)) +#define be_to_host32(n) __bswap_32((__force u32) (be32) (n)) +#define host_to_be32(n) ((__force be32) __bswap_32((n))) +#define le_to_host64(n) ((__force u64) (le64) (n)) +#define host_to_le64(n) ((__force le64) (u64) (n)) +#define be_to_host64(n) __bswap_64((__force u64) (be64) (n)) +#define host_to_be64(n) ((__force be64) bswap_64((n))) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le_to_host16(n) __bswap_16(n) +#define host_to_le16(n) __bswap_16(n) +#define be_to_host16(n) (n) +#define host_to_be16(n) (n) +#define le_to_host32(n) __bswap_32(n) +#define be_to_host32(n) (n) +#define host_to_be32(n) (n) +#define le_to_host64(n) __bswap_64(n) +#define host_to_le64(n) __bswap_64(n) +#define be_to_host64(n) (n) +#define host_to_be64(n) (n) +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN +#endif +#else +#error Could not determine CPU byte order +#endif + +#define WPA_BYTE_SWAP_DEFINED +#endif /* !WPA_BYTE_SWAP_DEFINED */ + + +/* Macros for handling unaligned memory accesses */ + +#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define WPA_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define WPA_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ + ((u32) (a)[2])) +#define WPA_PUT_BE24(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[2] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ + (((u32) (a)[2]) << 8) | ((u32) (a)[3])) +#define WPA_PUT_BE32(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[3] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ + (((u32) (a)[1]) << 8) | ((u32) (a)[0])) +#define WPA_PUT_LE32(a, val) \ + do { \ + (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[0] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ + (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ + (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ + (((u64) (a)[6]) << 8) | ((u64) (a)[7])) +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (u8) (((u64) (val)) >> 56); \ + (a)[1] = (u8) (((u64) (val)) >> 48); \ + (a)[2] = (u8) (((u64) (val)) >> 40); \ + (a)[3] = (u8) (((u64) (val)) >> 32); \ + (a)[4] = (u8) (((u64) (val)) >> 24); \ + (a)[5] = (u8) (((u64) (val)) >> 16); \ + (a)[6] = (u8) (((u64) (val)) >> 8); \ + (a)[7] = (u8) (((u64) (val)) & 0xff); \ + } while (0) + +#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ + (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ + (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ + (((u64) (a)[1]) << 8) | ((u64) (a)[0])) + + +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +//#ifndef IFNAMSIZ +//#define IFNAMSIZ 16 +//#endif +#ifndef ETH_P_ALL +#define ETH_P_ALL 0x0003 +#endif +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL ETH_P_PAE +#endif /* ETH_P_EAPOL */ +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif /* ETH_P_RSN_PREAUTH */ +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ + + +#ifdef __GNUC__ +#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) +#define STRUCT_PACKED __attribute__ ((packed)) +#else +#define PRINTF_FORMAT(a,b) +#define STRUCT_PACKED +#endif + +#ifdef CONFIG_ANSI_C_EXTRA + +/* inline - define as __inline or just define it to be empty, if needed */ +#ifdef CONFIG_NO_INLINE +#define inline +#else +#define inline __inline +#endif + +#ifndef __func__ +#define __func__ "__func__ not defined" +#endif + +#ifndef bswap_16 +#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff)) +#endif + +#ifndef bswap_32 +#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \ + (((u32) (a) << 8) & 0xff0000) | \ + (((u32) (a) >> 8) & 0xff00) | \ + (((u32) (a) >> 24) & 0xff)) +#endif + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif + +#ifdef _WIN32_WCE +void perror(const char *s); +#endif /* _WIN32_WCE */ + +#endif /* CONFIG_ANSI_C_EXTRA */ + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +/* + * Definitions for sparse validation + * (http://kernel.org/pub/linux/kernel/people/josh/sparse/) + */ +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef u16 __bitwise be16; +typedef u16 __bitwise le16; +typedef u32 __bitwise be32; +typedef u32 __bitwise le32; +typedef u64 __bitwise be64; +typedef u64 __bitwise le64; + +#ifndef __must_check +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __must_check __attribute__((__warn_unused_result__)) +#else +#define __must_check +#endif /* __GNUC__ */ +#endif /* __must_check */ + +int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_aton2(const char *txt, u8 *addr); +int hexstr2bin(const char *hex, u8 *buf, size_t len); +void inc_byte_array(u8 *counter, size_t len); +void wpa_get_ntp_timestamp(u8 *buf); +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len); + +#ifdef CONFIG_NATIVE_WINDOWS +void wpa_unicode2ascii_inplace(TCHAR *str); +TCHAR * wpa_strdup_tchar(const char *str); +#else /* CONFIG_NATIVE_WINDOWS */ +#define wpa_unicode2ascii_inplace(s) do { } while (0) +#define wpa_strdup_tchar(s) strdup((s)) +#endif /* CONFIG_NATIVE_WINDOWS */ + +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); +char * wpa_config_parse_string(const char *value, size_t *len); + +static inline int is_zero_ether_addr(const u8 *a) +{ + return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); +} + +extern const struct eth_addr ethbroadcast; +#define broadcast_ether_addr ðbroadcast + +#include "wpabuf.h" +#include "wpa_debug.h" + + +/* + * gcc 4.4 ends up generating strict-aliasing warnings about some very common + * networking socket uses that do not really result in a real problem and + * cannot be easily avoided with union-based type-punning due to struct + * definitions including another struct in system header files. To avoid having + * to fully disable strict-aliasing warnings, provide a mechanism to hide the + * typecast from aliasing for now. A cleaner solution will hopefully be found + * in the future to handle these cases. + */ +void * __hide_aliasing_typecast(void *foo); +#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) + +#endif /* COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa/defs.h b/components/wpa_supplicant/include/wpa/defs.h new file mode 100644 index 0000000000..f019cee992 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/defs.h @@ -0,0 +1,307 @@ +/* + * WPA Supplicant - Common definitions + * Copyright (c) 2004-2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DEFS_H +#define DEFS_H + +#ifdef FALSE +#undef FALSE +#endif +#ifdef TRUE +#undef TRUE +#endif +typedef enum { FALSE = 0, TRUE = 1 } Boolean; + +/* +#define WPA_CIPHER_NONE BIT(0) +#define WPA_CIPHER_WEP40 BIT(1) +#define WPA_CIPHER_WEP104 BIT(2) +#define WPA_CIPHER_TKIP BIT(3) +#define WPA_CIPHER_CCMP BIT(4) +#ifdef CONFIG_IEEE80211W +#define WPA_CIPHER_AES_128_CMAC BIT(5) +#endif +*/ + +/* + * NB: these values are ordered carefully; there are lots of + * of implications in any reordering. Beware that 4 is used + * only to indicate h/w TKIP MIC support in driver capabilities; + * there is no separate cipher support (it's rolled into the + * TKIP cipher support). + */ +#define IEEE80211_CIPHER_NONE 0 /* pseudo value */ +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_OCB 2 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CIPHER_TKIPMIC 4 /* TKIP MIC capability */ +#define IEEE80211_CIPHER_CKIP 5 +#define IEEE80211_CIPHER_WEP 6 +#define IEEE80211_CIPHER_WEP40 7 +#define IEEE80211_CIPHER_WEP104 8 + + +#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+2) + +/* capability bits in ic_cryptocaps/iv_cryptocaps */ +#define IEEE80211_CRYPTO_NONE (1<wpa_state). The current state can be retrieved with + * wpa_supplicant_get_state() function and the state can be changed by calling + * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the + * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used + * to access the state variable. + */ +enum wpa_states { + /** + * WPA_DISCONNECTED - Disconnected state + * + * This state indicates that client is not associated, but is likely to + * start looking for an access point. This state is entered when a + * connection is lost. + */ + WPA_DISCONNECTED, + + /** + * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) + * + * This state is entered if there are no enabled networks in the + * configuration. wpa_supplicant is not trying to associate with a new + * network and external interaction (e.g., ctrl_iface call to add or + * enable a network) is needed to start association. + */ + WPA_INACTIVE, + + /** + * WPA_SCANNING - Scanning for a network + * + * This state is entered when wpa_supplicant starts scanning for a + * network. + */ + WPA_SCANNING, + + /** + * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to authenticate with and the driver is configured to try to + * authenticate with this BSS. This state is used only with drivers + * that use wpa_supplicant as the SME. + */ + WPA_AUTHENTICATING, + + /** + * WPA_ASSOCIATING - Trying to associate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to associate with and the driver is configured to try to associate + * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this + * state is entered when the driver is configured to try to associate + * with a network using the configured SSID and security policy. + */ + WPA_ASSOCIATING, + + /** + * WPA_ASSOCIATED - Association completed + * + * This state is entered when the driver reports that association has + * been successfully completed with an AP. If IEEE 802.1X is used + * (with or without WPA/WPA2), wpa_supplicant remains in this state + * until the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_ASSOCIATED, + + /** + * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress + * + * This state is entered when WPA/WPA2 4-Way Handshake is started. In + * case of WPA-PSK, this happens when receiving the first EAPOL-Key + * frame after association. In case of WPA-EAP, this state is entered + * when the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_FIRST_HALF_4WAY_HANDSHAKE, + + WPA_LAST_HALF_4WAY_HANDSHAKE, + + /** + * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress + * + * This state is entered when 4-Way Key Handshake has been completed + * (i.e., when the supplicant sends out message 4/4) and when Group + * Key rekeying is started by the AP (i.e., when supplicant receives + * message 1/2). + */ + WPA_GROUP_HANDSHAKE, + + /** + * WPA_COMPLETED - All authentication completed + * + * This state is entered when the full authentication process is + * completed. In case of WPA2, this happens when the 4-Way Handshake is + * successfully completed. With WPA, this state is entered after the + * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is + * completed after dynamic keys are received (or if not used, after + * the EAP authentication has been completed). With static WEP keys and + * plaintext connections, this state is entered when an association + * has been completed. + * + * This state indicates that the supplicant has completed its + * processing for the association phase and that data connection is + * fully configured. + */ + WPA_COMPLETED, + + WPA_MIC_FAILURE, // first mic_error event occur + + WPA_TKIP_COUNTERMEASURES //in countermeasure period that stop connect with ap in 60 sec +}; + +#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1 +#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3 + +#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0 +#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1 + +/** + * enum hostapd_hw_mode - Hardware mode + */ +enum hostapd_hw_mode { + HOSTAPD_MODE_IEEE80211B, + HOSTAPD_MODE_IEEE80211G, + HOSTAPD_MODE_IEEE80211A, + HOSTAPD_MODE_IEEE80211AD, + NUM_HOSTAPD_MODES +}; + +#endif /* DEFS_H */ diff --git a/components/wpa_supplicant/include/wpa/eapol_common.h b/components/wpa_supplicant/include/wpa/eapol_common.h new file mode 100644 index 0000000000..6a40ac33b3 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/eapol_common.h @@ -0,0 +1,71 @@ +/* + * EAPOL definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAPOL_COMMON_H +#define EAPOL_COMMON_H + +/* IEEE Std 802.1X-2004 */ + +struct ieee802_1x_hdr { + u8 version; + u8 type; + be16 length; + /* followed by length octets of data */ +} STRUCT_PACKED; + + +#define EAPOL_VERSION 2 + +enum { IEEE802_1X_TYPE_EAP_PACKET = 0, + IEEE802_1X_TYPE_EAPOL_START = 1, + IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, + IEEE802_1X_TYPE_EAPOL_KEY = 3, + IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 +}; + +enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, + EAPOL_KEY_TYPE_WPA = 254 }; + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +struct ieee802_1x_eapol_key { + u8 type; + /* Note: key_length is unaligned */ + u8 key_length[2]; + /* does not repeat within the life of the keying material used to + * encrypt the Key field; 64-bit NTP timestamp MAY be used here */ + u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN]; + u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as + * the key */ + u8 key_signature[IEEE8021X_KEY_SIGN_LEN]; + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} STRUCT_PACKED; + +#endif /* EAPOL_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa/hostapd.h b/components/wpa_supplicant/include/wpa/hostapd.h new file mode 100644 index 0000000000..1d52659a22 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/hostapd.h @@ -0,0 +1,312 @@ +/* + * hostapd / Initialization and configuration + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HOSTAPD_H +#define HOSTAPD_H + +#include "wpa/defs.h" +#include "wpa/ap_config.h" + +struct wpa_driver_ops; +struct wpa_ctrl_dst; +struct radius_server_data; +struct upnp_wps_device_sm; +struct hostapd_data; +struct sta_info; +struct hostap_sta_driver_data; +struct ieee80211_ht_capabilities; +struct full_dynamic_vlan; +enum wps_event; +union wps_event_data; + +struct hostapd_iface; + +struct hapd_interfaces { + int (*reload_config)(struct hostapd_iface *iface); + struct hostapd_config * (*config_read_cb)(const char *config_fname); + int (*ctrl_iface_init)(struct hostapd_data *hapd); + void (*ctrl_iface_deinit)(struct hostapd_data *hapd); + int (*for_each_interface)(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); + int (*driver_init)(struct hostapd_iface *iface); + + size_t count; + int global_ctrl_sock; + char *global_iface_path; + char *global_iface_name; + struct hostapd_iface **iface; +}; + + +struct hostapd_probereq_cb { + int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, int ssi_signal); + void *ctx; +}; + +#define HOSTAPD_RATE_BASIC 0x00000001 + +struct hostapd_rate_data { + int rate; /* rate in 100 kbps */ + int flags; /* HOSTAPD_RATE_ flags */ +}; + +struct hostapd_frame_info { + u32 channel; + u32 datarate; + int ssi_signal; /* dBm */ +}; + + +/** + * struct hostapd_data - hostapd per-BSS data structure + */ +struct hostapd_data { +// struct hostapd_iface *iface; + struct hostapd_config *iconf; + struct hostapd_bss_config *conf; + int interface_added; /* virtual interface added for this BSS */ + + u8 own_addr[ETH_ALEN]; + + int num_sta; /* number of entries in sta_list */ +// struct sta_info *sta_list; /* STA info list head */ +//#define STA_HASH_SIZE 256 +//#define STA_HASH(sta) (sta[5]) +// struct sta_info *sta_hash[STA_HASH_SIZE]; + +// /* +// * Bitfield for indicating which AIDs are allocated. Only AID values +// * 1-2007 are used and as such, the bit at index 0 corresponds to AID +// * 1. +// */ +//#define AID_WORDS ((2008 + 31) / 32) +// u32 sta_aid[AID_WORDS]; + +// const struct wpa_driver_ops *driver; +// void *drv_priv; + +// void (*new_assoc_sta_cb)(struct hostapd_data *hapd, +// struct sta_info *sta, int reassoc); + +// void *msg_ctx; /* ctx for wpa_msg() calls */ +// void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ + +// struct radius_client_data *radius; +// u32 acct_session_id_hi, acct_session_id_lo; +// struct radius_das_data *radius_das; + +// struct iapp_data *iapp; + +// struct hostapd_cached_radius_acl *acl_cache; +// struct hostapd_acl_query_data *acl_queries; + + struct wpa_authenticator *wpa_auth; +// struct eapol_authenticator *eapol_auth; + +// struct rsn_preauth_interface *preauth_iface; +// time_t michael_mic_failure; +// int michael_mic_failures; +// int tkip_countermeasures; + +// int ctrl_sock; +// struct wpa_ctrl_dst *ctrl_dst; + +// void *ssl_ctx; +// void *eap_sim_db_priv; +// struct radius_server_data *radius_srv; + +// int parameter_set_count; + + /* Time Advertisement */ +// u8 time_update_counter; +// struct wpabuf *time_adv; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + struct full_dynamic_vlan *full_dynamic_vlan; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + +// struct l2_packet_data *l2; +// struct wps_context *wps; + +// int beacon_set_done; +// struct wpabuf *wps_beacon_ie; +// struct wpabuf *wps_probe_resp_ie; +#ifdef CONFIG_WPS + unsigned int ap_pin_failures; + unsigned int ap_pin_failures_consecutive; + struct upnp_wps_device_sm *wps_upnp; + unsigned int ap_pin_lockout_time; +#endif /* CONFIG_WPS */ + +// struct hostapd_probereq_cb *probereq_cb; +// size_t num_probereq_cb; + +// void (*public_action_cb)(void *ctx, const u8 *buf, size_t len, +// int freq); +// void *public_action_cb_ctx; + +// int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len, +// int freq); +// void *vendor_action_cb_ctx; + +// void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr, +// const u8 *uuid_e); +// void *wps_reg_success_cb_ctx; + +// void (*wps_event_cb)(void *ctx, enum wps_event event, +// union wps_event_data *data); +// void *wps_event_cb_ctx; + +// void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr, +// int authorized, const u8 *p2p_dev_addr); +// void *sta_authorized_cb_ctx; + +// void (*setup_complete_cb)(void *ctx); +// void *setup_complete_cb_ctx; + +#ifdef CONFIG_P2P + struct p2p_data *p2p; + struct p2p_group *p2p_group; + struct wpabuf *p2p_beacon_ie; + struct wpabuf *p2p_probe_resp_ie; + + /* Number of non-P2P association stations */ + int num_sta_no_p2p; + + /* Periodic NoA (used only when no non-P2P clients in the group) */ + int noa_enabled; + int noa_start; + int noa_duration; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + size_t gas_frag_limit; +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + struct hostapd_eap_user tmp_eap_user; +#endif /* CONFIG_SQLITE */ +}; + +#if 0 +/** + * struct hostapd_iface - hostapd per-interface data structure + */ +struct hostapd_iface { + struct hapd_interfaces *interfaces; + void *owner; + char *config_fname; + struct hostapd_config *conf; + + size_t num_bss; + struct hostapd_data **bss; + + int num_ap; /* number of entries in ap_list */ + struct ap_info *ap_list; /* AP info list head */ + struct ap_info *ap_hash[STA_HASH_SIZE]; + struct ap_info *ap_iter_list; + + unsigned int drv_flags; + + /* + * A bitmap of supported protocols for probe response offload. See + * struct wpa_driver_capa in driver.h + */ + unsigned int probe_resp_offloads; + + struct hostapd_hw_modes *hw_features; + int num_hw_features; + struct hostapd_hw_modes *current_mode; + /* Rates that are currently used (i.e., filtered copy of + * current_mode->channels */ + int num_rates; + struct hostapd_rate_data *current_rates; + int *basic_rates; + int freq; + + u16 hw_flags; + + /* Number of associated Non-ERP stations (i.e., stations using 802.11b + * in 802.11g BSS) */ + int num_sta_non_erp; + + /* Number of associated stations that do not support Short Slot Time */ + int num_sta_no_short_slot_time; + + /* Number of associated stations that do not support Short Preamble */ + int num_sta_no_short_preamble; + + int olbc; /* Overlapping Legacy BSS Condition */ + + /* Number of HT associated stations that do not support greenfield */ + int num_sta_ht_no_gf; + + /* Number of associated non-HT stations */ + int num_sta_no_ht; + + /* Number of HT associated stations 20 MHz */ + int num_sta_ht_20mhz; + + /* Overlapping BSS information */ + int olbc_ht; + + u16 ht_op_mode; + void (*scan_cb)(struct hostapd_iface *iface); +}; +#endif + +#if 0 +/* hostapd.c */ +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); +int hostapd_reload_config(struct hostapd_iface *iface); +struct hostapd_data * +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf, + struct hostapd_bss_config *bss); +int hostapd_setup_interface(struct hostapd_iface *iface); +int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); +void hostapd_interface_deinit(struct hostapd_iface *iface); +void hostapd_interface_free(struct hostapd_iface *iface); +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc); +void hostapd_interface_deinit_free(struct hostapd_iface *iface); +int hostapd_enable_iface(struct hostapd_iface *hapd_iface); +int hostapd_reload_iface(struct hostapd_iface *hapd_iface); +int hostapd_disable_iface(struct hostapd_iface *hapd_iface); +int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); +int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); + +/* utils.c */ +int hostapd_register_probereq_cb(struct hostapd_data *hapd, + int (*cb)(void *ctx, const u8 *sa, + const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal), + void *ctx); +void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); + +/* drv_callbacks.c (TODO: move to somewhere else?) */ +int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ielen, int reassoc); +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); +int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal); +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset); + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2); +#endif + +#endif /* HOSTAPD_H */ diff --git a/components/wpa_supplicant/include/wpa/ieee80211_crypto.h b/components/wpa_supplicant/include/wpa/ieee80211_crypto.h new file mode 100644 index 0000000000..be0fb9aa12 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/ieee80211_crypto.h @@ -0,0 +1,226 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * copyright (c) 2010-2011 Espressif System + */ +#ifndef _NET80211_IEEE80211_CRYPTO_H_ +#define _NET80211_IEEE80211_CRYPTO_H_ + +//#include "pp/esf_buf.h" + +/* + * 802.11 protocol crypto-related definitions. + */ +#define IEEE80211_KEYBUF_SIZE 16 +#define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx+rx keys */ + +/* + * Old WEP-style key. Deprecated. + */ + +#if 0 +struct ieee80211_rsnparms { + uint8_t rsn_mcastcipher; /* mcast/group cipher */ + uint8_t rsn_mcastkeylen; /* mcast key length */ + uint8_t rsn_ucastcipher; /* selected unicast cipher */ + uint8_t rsn_ucastkeylen; /* unicast key length */ + uint8_t rsn_keymgmt; /* selected key mgmt algo */ + uint16_t rsn_caps; /* capabilities */ +}; +#endif //0000 + +/* + * Template for a supported cipher. Ciphers register with the + * crypto code and are typically loaded as separate modules + * (the null cipher is always present). + * XXX may need refcnts + */ + +/* + * Crypto key state. There is sufficient room for all supported + * ciphers (see below). The underlying ciphers are handled + * separately through loadable cipher modules that register with + * the generic crypto support. A key has a reference to an instance + * of the cipher; any per-key state is hung off wk_private by the + * cipher when it is attached. Ciphers are automatically called + * to detach and cleanup any such state when the key is deleted. + * + * The generic crypto support handles encap/decap of cipher-related + * frame contents for both hardware- and software-based implementations. + * A key requiring software crypto support is automatically flagged and + * the cipher is expected to honor this and do the necessary work. + * Ciphers such as TKIP may also support mixed hardware/software + * encrypt/decrypt and MIC processing. + */ +typedef uint16_t ieee80211_keyix; /* h/w key index */ + +struct ieee80211_key { + uint8_t wk_keylen; /* key length in bytes */ + uint8_t wk_pad; + uint16_t wk_flags; +#define IEEE80211_KEY_XMIT 0x0001 /* key used for xmit */ +#define IEEE80211_KEY_RECV 0x0002 /* key used for recv */ +#define IEEE80211_KEY_GROUP 0x0004 /* key used for WPA group operation */ +#define IEEE80211_KEY_SWENCRYPT 0x0010 /* host-based encrypt */ +#define IEEE80211_KEY_SWDECRYPT 0x0020 /* host-based decrypt */ +#define IEEE80211_KEY_SWENMIC 0x0040 /* host-based enmic */ +#define IEEE80211_KEY_SWDEMIC 0x0080 /* host-based demic */ +#define IEEE80211_KEY_DEVKEY 0x0100 /* device key request completed */ +#define IEEE80211_KEY_CIPHER0 0x1000 /* cipher-specific action 0 */ +#define IEEE80211_KEY_CIPHER1 0x2000 /* cipher-specific action 1 */ +#define IEEE80211_KEY_EMPTY 0x0000 + ieee80211_keyix wk_keyix; /* h/w key index */ + ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */ + uint8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; +#define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */ +#define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */ + /* key receive sequence counter */ + uint64_t wk_keyrsc[IEEE80211_TID_SIZE]; + uint64_t wk_keytsc; /* key transmit sequence counter */ + const struct ieee80211_cipher *wk_cipher; + //void *wk_private; /* private cipher state */ + //uint8_t wk_macaddr[IEEE80211_ADDR_LEN]; //JLU: no need ... +}; +#define IEEE80211_KEY_COMMON /* common flags passed in by apps */\ + (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP) +#define IEEE80211_KEY_DEVICE /* flags owned by device driver */\ + (IEEE80211_KEY_DEVKEY|IEEE80211_KEY_CIPHER0|IEEE80211_KEY_CIPHER1) + +#define IEEE80211_KEY_SWCRYPT \ + (IEEE80211_KEY_SWENCRYPT | IEEE80211_KEY_SWDECRYPT) +#define IEEE80211_KEY_SWMIC (IEEE80211_KEY_SWENMIC | IEEE80211_KEY_SWDEMIC) + +//#define IEEE80211_KEYIX_NONE ((ieee80211_keyix) -1) + +/* + * NB: these values are ordered carefully; there are lots of + * of implications in any reordering. Beware that 4 is used + * only to indicate h/w TKIP MIC support in driver capabilities; + * there is no separate cipher support (it's rolled into the + * TKIP cipher support). + */ +#define IEEE80211_CIPHER_NONE 0 /* pseudo value */ +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_OCB 2 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CIPHER_TKIPMIC 4 /* TKIP MIC capability */ +#define IEEE80211_CIPHER_CKIP 5 +#define IEEE80211_CIPHER_WEP 6 +#define IEEE80211_CIPHER_WEP40 7 +#define IEEE80211_CIPHER_WEP104 8 + + +#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+2) + +/* capability bits in ic_cryptocaps/iv_cryptocaps */ +#define IEEE80211_CRYPTO_NONE (1<wk_cipher == &ieee80211_cipher_none) + +struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211_conn *, + esf_buf *); + +struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211_conn *, + esf_buf *, int); + +#if 0 //H/W MIC +/* + * Check and remove any MIC. + */ +static INLINE int +ieee80211_crypto_demic(struct ieee80211vap *vap, struct ieee80211_key *k, + esf_buf *m, int force) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? cip->ic_demic(k, m, force) : 1); +} + +/* + * Add any MIC. + */ +static INLINE int +ieee80211_crypto_enmic(struct ieee80211vap *vap, + struct ieee80211_key *k, esf_buf *m, int force) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m, force) : 1); +} +#endif //0000 + +/* + * Setup crypto support for a device/shared instance. + */ +void ieee80211_crypto_attach(struct ieee80211com *ic); + +/* + * Reset key state to an unused state. The crypto + * key allocation mechanism insures other state (e.g. + * key data) is properly setup before a key is used. + */ +static inline void +ieee80211_crypto_resetkey(struct ieee80211_key *k) +{ + k->wk_cipher = NULL; + k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; +} + +/* + * Crypt-related notification methods. + */ +//void ieee80211_notify_replay_failure(const struct ieee80211_frame *, const struct ieee80211_key *, +// uint64_t rsc, int tid); +//void ieee80211_notify_michael_failure(const struct ieee80211_frame *, u_int keyix); + +#endif /* _NET80211_IEEE80211_CRYPTO_H_ */ diff --git a/components/wpa_supplicant/include/wpa/ieee802_11_defs.h b/components/wpa_supplicant/include/wpa/ieee802_11_defs.h new file mode 100644 index 0000000000..4881e39a01 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/ieee802_11_defs.h @@ -0,0 +1,607 @@ +/* + * IEEE 802.11 Frame type definitions + * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2007-2008 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_11_DEFS_H +#define IEEE802_11_DEFS_H + +/* IEEE 802.11 defines */ + +#define WLAN_FC_PVER 0x0003 +#define WLAN_FC_TODS 0x0100 +#define WLAN_FC_FROMDS 0x0200 +#define WLAN_FC_MOREFRAG 0x0400 +#define WLAN_FC_RETRY 0x0800 +#define WLAN_FC_PWRMGT 0x1000 +#define WLAN_FC_MOREDATA 0x2000 +#define WLAN_FC_ISWEP 0x4000 +#define WLAN_FC_ORDER 0x8000 + +#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2) +#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 +#define WLAN_FC_STYPE_ACTION 13 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 +#define WLAN_FC_STYPE_QOS_DATA 8 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FT 2 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) +#define WLAN_CAPABILITY_PBCC BIT(6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) +#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) +#define WLAN_CAPABILITY_DSSS_OFDM BIT(13) + +/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* IEEE 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11h */ +#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 +#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 +#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 +/* IEEE 802.11g */ +#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 +#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26 +#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27 +#define WLAN_STATUS_R0KH_UNREACHABLE 28 +/* IEEE 802.11w */ +#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 +#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 +#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 +#define WLAN_STATUS_REQUEST_DECLINED 37 +#define WLAN_STATUS_INVALID_PARAMETERS 38 +/* IEEE 802.11i */ +#define WLAN_STATUS_INVALID_IE 40 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 +#define WLAN_STATUS_TS_NOT_CREATED 47 +#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48 +#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 +#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 +#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 +/* IEEE 802.11r */ +#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 +#define WLAN_STATUS_INVALID_PMKID 53 +#define WLAN_STATUS_INVALID_MDIE 54 +#define WLAN_STATUS_INVALID_FTIE 55 + +/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +/* IEEE 802.11h */ +#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 +#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 +/* IEEE 802.11i */ +#define WLAN_REASON_INVALID_IE 13 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 +#define WLAN_REASON_AKMP_NOT_VALID 20 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_COUNTRY 7 +#define WLAN_EID_CHALLENGE 16 +/* EIDs defined by IEEE 802.11h - START */ +#define WLAN_EID_PWR_CONSTRAINT 32 +#define WLAN_EID_PWR_CAPABILITY 33 +#define WLAN_EID_TPC_REQUEST 34 +#define WLAN_EID_TPC_REPORT 35 +#define WLAN_EID_SUPPORTED_CHANNELS 36 +#define WLAN_EID_CHANNEL_SWITCH 37 +#define WLAN_EID_MEASURE_REQUEST 38 +#define WLAN_EID_MEASURE_REPORT 39 +#define WLAN_EID_QUITE 40 +#define WLAN_EID_IBSS_DFS 41 +/* EIDs defined by IEEE 802.11h - END */ +#define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_HT_CAP 45 +#define WLAN_EID_RSN 48 +#define WLAN_EID_EXT_SUPP_RATES 50 +#define WLAN_EID_MOBILITY_DOMAIN 54 +#define WLAN_EID_FAST_BSS_TRANSITION 55 +#define WLAN_EID_TIMEOUT_INTERVAL 56 +#define WLAN_EID_RIC_DATA 57 +#define WLAN_EID_HT_OPERATION 61 +#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_20_40_BSS_COEXISTENCE 72 +#define WLAN_EID_20_40_BSS_INTOLERANT 73 +#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 +#define WLAN_EID_MMIE 76 +#define WLAN_EID_VENDOR_SPECIFIC 221 + + +/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ +#define WLAN_ACTION_SPECTRUM_MGMT 0 +#define WLAN_ACTION_QOS 1 +#define WLAN_ACTION_DLS 2 +#define WLAN_ACTION_BLOCK_ACK 3 +#define WLAN_ACTION_PUBLIC 4 +#define WLAN_ACTION_RADIO_MEASUREMENT 5 +#define WLAN_ACTION_FT 6 +#define WLAN_ACTION_HT 7 +#define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ + +/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ +#define WLAN_SA_QUERY_REQUEST 0 +#define WLAN_SA_QUERY_RESPONSE 1 + +#define WLAN_SA_QUERY_TR_ID_LEN 2 + +/* Timeout Interval Type */ +#define WLAN_TIMEOUT_REASSOC_DEADLINE 1 +#define WLAN_TIMEOUT_KEY_LIFETIME 2 +#define WLAN_TIMEOUT_ASSOC_COMEBACK 3 + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee80211_hdr { + le16 frame_control; + le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + le16 seq_ctrl; + /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame + */ +} STRUCT_PACKED; + +#define IEEE80211_DA_FROMDS addr1 +#define IEEE80211_BSSID_FROMDS addr2 +#define IEEE80211_SA_FROMDS addr3 + +#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) + +#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) + +struct ieee80211_mgmt { + le16 frame_control; + le16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + le16 seq_ctrl; + union { + struct { + le16 auth_alg; + le16 auth_transaction; + le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } STRUCT_PACKED auth; + struct { + le16 reason_code; + } STRUCT_PACKED deauth; + struct { + le16 capab_info; + le16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } STRUCT_PACKED assoc_req; + struct { + le16 capab_info; + le16 status_code; + le16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } STRUCT_PACKED assoc_resp, reassoc_resp; + struct { + le16 capab_info; + le16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } STRUCT_PACKED reassoc_req; + struct { + le16 reason_code; + } STRUCT_PACKED disassoc; + struct { + u8 timestamp[8]; + le16 beacon_int; + le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } STRUCT_PACKED beacon; + struct { + /* only variable items: SSID, Supported rates */ + u8 variable[0]; + } STRUCT_PACKED probe_req; + struct { + u8 timestamp[8]; + le16 beacon_int; + le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params */ + u8 variable[0]; + } STRUCT_PACKED probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[0]; + } STRUCT_PACKED wmm_action; + struct{ + u8 action_code; + u8 element_id; + u8 length; + u8 switch_mode; + u8 new_chan; + u8 switch_count; + } STRUCT_PACKED chan_switch; + struct { + u8 action; + u8 sta_addr[ETH_ALEN]; + u8 target_ap_addr[ETH_ALEN]; + u8 variable[0]; /* FT Request */ + } STRUCT_PACKED ft_action_req; + struct { + u8 action; + u8 sta_addr[ETH_ALEN]; + u8 target_ap_addr[ETH_ALEN]; + le16 status_code; + u8 variable[0]; /* FT Request */ + } STRUCT_PACKED ft_action_resp; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_req; + struct { + u8 action; /* */ + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_resp; + } u; + } STRUCT_PACKED action; + } u; +} STRUCT_PACKED; + + +struct ieee80211_ht_capabilities { + le16 ht_capabilities_info; + u8 a_mpdu_params; + u8 supported_mcs_set[16]; + le16 ht_extended_capabilities; + le32 tx_bf_capability_info; + u8 asel_capabilities; +} STRUCT_PACKED; + + +struct ieee80211_ht_operation { + u8 control_chan; + u8 ht_param; + le16 operation_mode; + le16 stbc_param; + u8 basic_set[16]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define ERP_INFO_NON_ERP_PRESENT BIT(0) +#define ERP_INFO_USE_PROTECTION BIT(1) +#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) + + +#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0)) +#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1)) +#define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3))) +#define HT_CAP_INFO_SMPS_STATIC ((u16) 0) +#define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2)) +#define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3))) +#define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4)) +#define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5)) +#define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6)) +#define HT_CAP_INFO_TX_STBC ((u16) BIT(7)) +#define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9))) +#define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8)) +#define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9)) +#define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9))) +#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10)) +#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11)) +#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12)) +#define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13)) +#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14)) +#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) + + +#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) +#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 +#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 +#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10)) +#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) + + +#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0)) +#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) +#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) +#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3)) +#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4)) +#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5)) +#define TX_BEAMFORM_CAP_CALIB_OFFSET 6 +#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 +#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 +#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17 +#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 +#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 +#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 +#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 + + +#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) +#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) +#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) + +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1)) +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0)) +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1)) +#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8) BIT(2)) +#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) +#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4)) +#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5)) + + +#define OP_MODE_PURE 0 +#define OP_MODE_MAY_BE_LEGACY_STAS 1 +#define OP_MODE_20MHZ_HT_STA_ASSOCED 2 +#define OP_MODE_MIXED 3 + +#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ + ((le16) (0x0001 | 0x0002)) +#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 +#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) +#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) +#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4)) + +#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6)) +#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7)) +#define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8)) +#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9)) +#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) +#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) + + +#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) + * 00:50:F2 */ +#define WPA_IE_VENDOR_TYPE 0x0050f201 +#define WPS_IE_VENDOR_TYPE 0x0050f204 + +#define WMM_OUI_TYPE 2 +#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 +#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1 +#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2 +#define WMM_VERSION 1 + +#define WMM_ACTION_CODE_ADDTS_REQ 0 +#define WMM_ACTION_CODE_ADDTS_RESP 1 +#define WMM_ACTION_CODE_DELTS 2 + +#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0 +#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1 +/* 2 - Reserved */ +#define WMM_ADDTS_STATUS_REFUSED 3 +/* 4-255 - Reserved */ + +/* WMM TSPEC Direction Field Values */ +#define WMM_TSPEC_DIRECTION_UPLINK 0 +#define WMM_TSPEC_DIRECTION_DOWNLINK 1 +/* 2 - Reserved */ +#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 + +/* + * WMM Information Element (used in (Re)Association Request frames; may also be + * used in Beacon frames) + */ +struct wmm_information_element { + /* Element ID: 221 (0xdd); Length: 7 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 0 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + +} STRUCT_PACKED; + +#define WMM_AC_AIFSN_MASK 0x0f +#define WMM_AC_AIFNS_SHIFT 0 +#define WMM_AC_ACM 0x10 +#define WMM_AC_ACI_MASK 0x60 +#define WMM_AC_ACI_SHIFT 5 + +#define WMM_AC_ECWMIN_MASK 0x0f +#define WMM_AC_ECWMIN_SHIFT 0 +#define WMM_AC_ECWMAX_MASK 0xf0 +#define WMM_AC_ECWMAX_SHIFT 4 + +struct wmm_ac_parameter { + u8 aci_aifsn; /* AIFSN, ACM, ACI */ + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ + le16 txop_limit; +} STRUCT_PACKED; + +/* + * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association + * Response frmaes) + */ +struct wmm_parameter_element { + /* Element ID: 221 (0xdd); Length: 24 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 1 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specif QoS info */ + u8 reserved; /* 0 */ + struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ + +} STRUCT_PACKED; + +/* WMM TSPEC Element */ +struct wmm_tspec_element { + u8 eid; /* 221 = 0xdd */ + u8 length; /* 6 + 55 = 61 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 2 */ + u8 version; /* 1 */ + /* WMM TSPEC body (55 octets): */ + u8 ts_info[3]; + le16 nominal_msdu_size; + le16 maximum_msdu_size; + le32 minimum_service_interval; + le32 maximum_service_interval; + le32 inactivity_interval; + le32 suspension_interval; + le32 service_start_time; + le32 minimum_data_rate; + le32 mean_data_rate; + le32 peak_data_rate; + le32 maximum_burst_size; + le32 delay_bound; + le32 minimum_phy_rate; + le16 surplus_bandwidth_allowance; + le16 medium_time; +} STRUCT_PACKED; + + +/* Access Categories / ACI to AC coding */ +enum { + WMM_AC_BE = 0 /* Best Effort */, + WMM_AC_BK = 1 /* Background */, + WMM_AC_VI = 2 /* Video */, + WMM_AC_VO = 3 /* Voice */ +}; + + +#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ + +#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ + +/* cipher suite selectors */ +#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 +#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 +/* reserved: 0x000FAC03 */ +#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 +#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 + +/* AKM suite selectors */ +#define WLAN_AKM_SUITE_8021X 0x000FAC01 +#define WLAN_AKM_SUITE_PSK 0x000FAC02 + +#endif /* IEEE802_11_DEFS_H */ diff --git a/components/wpa_supplicant/include/wpa/ieee802_1x.h b/components/wpa_supplicant/include/wpa/ieee802_1x.h new file mode 100644 index 0000000000..e10ff7b310 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/ieee802_1x.h @@ -0,0 +1,64 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_1X_H +#define IEEE802_1X_H + +struct hostapd_data; +struct sta_info; +struct eapol_state_machine; +struct hostapd_config; +struct hostapd_bss_config; +struct hostapd_radius_attr; +struct radius_msg; + + +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len); + +#if 0 +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_free_station(struct sta_info *sta); + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); +int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_deinit(struct hostapd_data *hapd); +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, size_t len, int ack); +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *data, int len, int ack); +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx); +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm); +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len); +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, + int enabled); +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, + int valid); +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth); +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +void hostapd_get_ntp_timestamp(u8 *buf); +char *eap_type_text(u8 type); + +const char *radius_mode_txt(struct hostapd_data *hapd); +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); + +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg); +#endif + +#endif /* IEEE802_1X_H */ diff --git a/components/wpa_supplicant/include/wpa/includes.h b/components/wpa_supplicant/include/wpa/includes.h new file mode 100644 index 0000000000..993bc49941 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/includes.h @@ -0,0 +1,31 @@ +/* + * wpa_supplicant/hostapd - Default include files + * Copyright (c) 2005-2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This header file is included into all C files so that commonly used header + * files can be selected with OS specific ifdef blocks in one place instead of + * having to have OS/C library specific selection in many files. + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +/* Include possible build time configuration before including anything else */ +//#include "build_config.h" //don't need anymore + +//#include +//#include +//#include +//#include +//#include + +#endif /* INCLUDES_H */ diff --git a/components/wpa_supplicant/include/wpa/list.h b/components/wpa_supplicant/include/wpa/list.h new file mode 100644 index 0000000000..c8dccee83d --- /dev/null +++ b/components/wpa_supplicant/include/wpa/list.h @@ -0,0 +1,101 @@ +/* + * Doubly-linked list + * Copyright (c) 2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef LIST_H +#define LIST_H + +/** + * struct dl_list - Doubly-linked list + */ +struct dl_list { + struct dl_list *next; + struct dl_list *prev; +}; + +static inline void dl_list_init(struct dl_list *list) +{ + list->next = list; + list->prev = list; +} + +static inline void dl_list_add(struct dl_list *list, struct dl_list *item) +{ + item->next = list->next; + item->prev = list; + list->next->prev = item; + list->next = item; +} + +static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item) +{ + dl_list_add(list->prev, item); +} + +static inline void dl_list_del(struct dl_list *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; + item->next = NULL; + item->prev = NULL; +} + +static inline int dl_list_empty(struct dl_list *list) +{ + return list->next == list; +} + +static inline unsigned int dl_list_len(struct dl_list *list) +{ + struct dl_list *item; + int count = 0; + for (item = list->next; item != list; item = item->next) + count++; + return count; +} + +#ifndef offsetof +#define offsetof(type, member) ((long) &((type *) 0)->member) +#endif + +#define dl_list_entry(item, type, member) \ + ((type *) ((char *) item - offsetof(type, member))) + +#define dl_list_first(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->next, type, member)) + +#define dl_list_last(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->prev, type, member)) + +#define dl_list_for_each(item, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.next, type, member)) + +#define dl_list_for_each_safe(item, n, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member), \ + n = dl_list_entry(item->member.next, type, member); \ + &item->member != (list); \ + item = n, n = dl_list_entry(n->member.next, type, member)) + +#define dl_list_for_each_reverse(item, list, type, member) \ + for (item = dl_list_entry((list)->prev, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.prev, type, member)) + +#define DEFINE_DL_LIST(name) \ + struct dl_list name = { &(name), &(name) } + +#endif /* LIST_H */ diff --git a/components/wpa_supplicant/include/wpa/sta_info.h b/components/wpa_supplicant/include/wpa/sta_info.h new file mode 100644 index 0000000000..44874a2ff9 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/sta_info.h @@ -0,0 +1,194 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef STA_INFO_H +#define STA_INFO_H + +/* STA flags */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_PS BIT(2) +#define WLAN_STA_TIM BIT(3) +#define WLAN_STA_PERM BIT(4) +#define WLAN_STA_AUTHORIZED BIT(5) +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ +#define WLAN_STA_SHORT_PREAMBLE BIT(7) +#define WLAN_STA_PREAUTH BIT(8) +#define WLAN_STA_WMM BIT(9) +#define WLAN_STA_MFP BIT(10) +#define WLAN_STA_HT BIT(11) +#define WLAN_STA_WPS BIT(12) +#define WLAN_STA_MAYBE_WPS BIT(13) +#define WLAN_STA_WDS BIT(14) +#define WLAN_STA_ASSOC_REQ_OK BIT(15) +#define WLAN_STA_WPS2 BIT(16) +#define WLAN_STA_GAS BIT(17) +#define WLAN_STA_VHT BIT(18) +#define WLAN_STA_PENDING_DISASSOC_CB BIT(29) +#define WLAN_STA_PENDING_DEAUTH_CB BIT(30) +#define WLAN_STA_NONERP BIT(31) + +/* Maximum number of supported rates (from both Supported Rates and Extended + * Supported Rates IEs). */ +#define WLAN_SUPP_RATES_MAX 32 + + +struct sta_info { + struct sta_info *next; /* next entry in sta list */ + struct sta_info *hnext; /* next entry in hash table list */ + u8 addr[6]; + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u32 flags; /* Bitfield of WLAN_STA_* */ + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + int supported_rates_len; +// u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ + +// unsigned int nonerp_set:1; +// unsigned int no_short_slot_time_set:1; +// unsigned int no_short_preamble_set:1; +// unsigned int no_ht_gf_set:1; +// unsigned int no_ht_set:1; +// unsigned int ht_20mhz_set:1; +// unsigned int no_p2p_set:1; + + u16 auth_alg; +// u8 previous_ap[6]; + + enum { + STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE + } timeout_next; + +// u16 deauth_reason; +// u16 disassoc_reason; + + /* IEEE 802.1X related data */ +// struct eapol_state_machine *eapol_sm; + + /* IEEE 802.11f (IAPP) related data */ +// struct ieee80211_mgmt *last_assoc_req; + +// u32 acct_session_id_hi; +// u32 acct_session_id_lo; +// time_t acct_session_start; +// int acct_session_started; +// int acct_terminate_cause; /* Acct-Terminate-Cause */ +// int acct_interim_interval; /* Acct-Interim-Interval */ + +// unsigned long last_rx_bytes; +// unsigned long last_tx_bytes; +// u32 acct_input_gigawords; /* Acct-Input-Gigawords */ +// u32 acct_output_gigawords; /* Acct-Output-Gigawords */ + +// u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ + + struct wpa_state_machine *wpa_sm; +// struct rsn_preauth_interface *preauth_iface; + + struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ +// struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ + +// int vlan_id; + /* PSKs from RADIUS authentication server */ +// struct hostapd_sta_wpa_psk_short *psk; + +// char *identity; /* User-Name from RADIUS */ +// char *radius_cui; /* Chargeable-User-Identity from RADIUS */ + +// struct ieee80211_ht_capabilities *ht_capabilities; +// struct ieee80211_vht_capabilities *vht_capabilities; + +#ifdef CONFIG_IEEE80211W + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending SA Query + * transaction identifiers */ + struct os_time sa_query_start; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_INTERWORKING +#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ + struct gas_dialog_info *gas_dialog; + u8 gas_dialog_next; +#endif /* CONFIG_INTERWORKING */ + +// struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ +// struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ +// struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ + +// struct os_time connected_time; + +#ifdef CONFIG_SAE + enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state; + u16 sae_send_confirm; +#endif /* CONFIG_SAE */ +}; + + +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has + * passed since last received frame from the station, a nullfunc data frame is + * sent to the station. If this frame is not acknowledged and no other frames + * have been received, the station will be disassociated after + * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated + * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ +#define AP_MAX_INACTIVITY (5 * 60) +#define AP_DISASSOC_DELAY (1) +#define AP_DEAUTH_DELAY (1) +/* Number of seconds to keep STA entry with Authenticated flag after it has + * been disassociated. */ +#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30) +/* Number of seconds to keep STA entry after it has been deauthenticated. */ +#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) + + +struct hostapd_data; + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx); +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_free_stas(struct hostapd_data *hapd); +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout); +void ap_sta_no_session_timeout(struct hostapd_data *hapd, + struct sta_info *sta); +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx); +#endif /* CONFIG_WPS */ +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid); +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *addr, u16 reason); + +void ap_sta_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +static inline int ap_sta_is_authorized(struct sta_info *sta) +{ + return sta->flags & WLAN_STA_AUTHORIZED; +} + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* STA_INFO_H */ diff --git a/components/wpa_supplicant/include/wpa/state_machine.h b/components/wpa_supplicant/include/wpa/state_machine.h new file mode 100644 index 0000000000..ce8c51e770 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/state_machine.h @@ -0,0 +1,138 @@ +/* + * wpa_supplicant/hostapd - State machine definitions + * Copyright (c) 2002-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file includes a set of pre-processor macros that can be used to + * implement a state machine. In addition to including this header file, each + * file implementing a state machine must define STATE_MACHINE_DATA to be the + * data structure including state variables (enum machine_state, + * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used + * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define + * a group of state machines with shared data structure, STATE_MACHINE_ADDR + * needs to be defined to point to the MAC address used in debug output. + * SM_ENTRY_M macro can be used to define similar group of state machines + * without this additional debug info. + */ + +#ifndef STATE_MACHINE_H +#define STATE_MACHINE_H + +/** + * SM_STATE - Declaration of a state machine function + * @machine: State machine name + * @state: State machine state + * + * This macro is used to declare a state machine function. It is used in place + * of a C function definition to declare functions to be run when the state is + * entered by calling SM_ENTER or SM_ENTER_GLOBAL. + */ +#define SM_STATE(machine, state) \ +static void ICACHE_FLASH_ATTR sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \ + int global) + +/** + * SM_ENTRY - State machine function entry point + * @machine: State machine name + * @state: State machine state + * + * This macro is used inside each state machine function declared with + * SM_STATE. SM_ENTRY should be in the beginning of the function body, but + * after declaration of possible local variables. This macro prints debug + * information about state transition and update the state machine state. + */ +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \ + " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +/** + * SM_ENTRY_M - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: prefix_state) + * + * This macro is like SM_ENTRY, but for state machine groups that use a shared + * data structure for more than one state machine. Both machine and prefix + * parameters are set to "sub-state machine" name. prefix is used to allow more + * than one state variable to be stored in the same data structure. + */ +#define SM_ENTRY_M(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \ + #machine " entering state " #_state); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTRY_MA - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: prefix_state) + * + * This macro is like SM_ENTRY_M, but a MAC address is included in debug + * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to + * be included in debug. + */ +#define SM_ENTRY_MA(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \ + #machine " entering state " #_state"\n", \ + MAC2STR(STATE_MACHINE_ADDR)); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTER - Enter a new state machine state + * @machine: State machine name + * @state: State machine state + * + * This macro expands to a function call to a state machine function defined + * with SM_STATE macro. SM_ENTER is used in a state machine step function to + * move the state machine to a new state. + */ +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) + +/** + * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule + * @machine: State machine name + * @state: State machine state + * + * This macro is like SM_ENTER, but this is used when entering a new state + * based on a global (not specific to any particular state) rule. A separate + * macro is used to avoid unwanted debug message floods when the same global + * rule is forcing a state machine to remain in on state. + */ +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +/** + * SM_STEP - Declaration of a state machine step function + * @machine: State machine name + * + * This macro is used to declare a state machine step function. It is used in + * place of a C function definition to declare a function that is used to move + * state machine to a new state based on state variables. This function uses + * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state. + */ +#define SM_STEP(machine) \ +static void ICACHE_FLASH_ATTR sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm) + +/** + * SM_STEP_RUN - Call the state machine step function + * @machine: State machine name + * + * This macro expands to a function call to a state machine step function + * defined with SM_STEP macro. + */ +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + +#endif /* STATE_MACHINE_H */ diff --git a/components/wpa_supplicant/include/wpa/wpa.h b/components/wpa_supplicant/include/wpa/wpa.h new file mode 100644 index 0000000000..2a1adfc567 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa.h @@ -0,0 +1,193 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_H +#define WPA_H + +#include "c_types.h" +#include "os_type.h" +#include "common.h" +#include "ets_sys.h" +#include "wpa/defs.h" +#include "wpa/wpa_common.h" + +//#include "net80211/ieee80211_var.h" +//#include "net80211/ieee80211_node.h" + +#define WPA_SM_STATE(_sm) ((_sm)->wpa_state) + +struct wpa_sm; + +int wpa_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len); + +#define WPA_ASSERT ASSERT + +struct install_key { + int mic_errors_seen; /* Michael MIC errors with the current PTK */ + int keys_cleared; + enum wpa_alg alg; + u8 addr[ETH_ALEN]; + int key_idx; + int set_tx; + u8 seq[10]; + u8 key[32]; +}; + +/** + * struct wpa_sm - Internal WPA state machine data + */ +struct wpa_sm { + u8 pmk[PMK_LEN]; + size_t pmk_len; + +// char *passphrase; //wlan password +// u8 *ssid; //wlan network name +// size_t ssid_len; + + struct wpa_ptk ptk, tptk; + int ptk_set, tptk_set; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ + int renew_snonce; + u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int rx_replay_counter_set; + u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + +// void *network_ctx; + + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int key_mgmt; + unsigned int mgmt_group_cipher; + + int rsn_enabled; /* Whether RSN is enabled in configuration */ + + int countermeasures; /*TKIP countermeasures state flag, 1:in countermeasures state*/ + os_timer_t cm_timer; + + u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ + size_t assoc_wpa_ie_len; + + u8 eapol_version; + + int wpa_ptk_rekey; + u8 own_addr[ETH_ALEN]; + + u8 bssid[ETH_ALEN]; + + unsigned int proto; + enum wpa_states wpa_state; + + u8 *ap_wpa_ie, *ap_rsn_ie; + size_t ap_wpa_ie_len, ap_rsn_ie_len; + + struct install_key install_ptk; + struct install_key install_gtk; + int key_entry_valid; //present current avaliable entry for bssid, for pairkey:0,5,10,15,20, gtk: pairkey_no+i (i:1~4) + +// char *msg; //send eapol msg buff +// size_t msg_len; //msg length:6 + sizeof(eth) + data_len + +// struct netif *ifp; + struct pbuf *pb; + + void (* sendto) (struct pbuf *pb); + void (*config_assoc_ie) (uint8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len); + void (*install_ppkey) (enum wpa_alg alg, uint8 *addr, int key_idx, int set_tx, + uint8 *seq, size_t seq_len, uint8 *key, size_t key_len, int key_entry_valid); + void (*wpa_deauthenticate)(uint8 reason_code); + void (*wpa_neg_complete)(); + struct wpa_gtk_data gd; //used for calllback save param + uint16 key_info; //used for txcallback param +}; + +struct l2_ethhdr { + u8 h_dest[ETH_ALEN]; + u8 h_source[ETH_ALEN]; + be16 h_proto; +} STRUCT_PACKED; + +/** + * set_key - Configure encryption key + * @ifname: Interface name (for multi-SSID/VLAN support) + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); + * %WPA_ALG_NONE clears the key. + * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for + * IGTK + * @set_tx: configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16, IGTK: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + * + * Please note that TKIP keys include separate TX and RX MIC keys and + * some drivers may expect them in different order than wpa_supplicant + * is using. If the TX/RX keys are swapped, all TKIP encrypted packets + * will tricker Michael MIC errors. This can be fixed by changing the + * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key + * in driver_*.c set_key() implementation, see driver_ndis.c for an + * example on how this can be done. + */ + + +/** + * send_eapol - Optional function for sending EAPOL packets + * @priv: private driver interface data + * @dest: Destination MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Size of the EAPOL packet + * + * Returns: 0 on success, -1 on failure + * + * This optional function can be used to override l2_packet operations + * with driver specific functionality. If this function pointer is set, + * l2_packet module is not used at all and the driver interface code is + * responsible for receiving and sending all EAPOL packets. The + * received EAPOL packets are sent to core code with EVENT_EAPOL_RX + * event. The driver interface is required to implement get_mac_addr() + * handler if send_eapol() is used. + */ + +#define KEYENTRY_TABLE_MAP(key_entry_valid) ((key_entry_valid)%5) + +void pp_michael_mic_failure(uint16 isunicast); + +void wpa_sm_set_state(enum wpa_states state); + +#endif /* WPA_H */ + diff --git a/components/wpa_supplicant/include/wpa/wpa_auth.h b/components/wpa_supplicant/include/wpa/wpa_auth.h new file mode 100644 index 0000000000..c729923494 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa_auth.h @@ -0,0 +1,292 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_AUTH_H +#define WPA_AUTH_H + +#include "wpa/defs.h" +#include "wpa/eapol_common.h" +#include "wpa/wpa_common.h" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition + */ +struct ft_rrb_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ + le16 action_length; /* little endian length of action_frame */ + u8 ap_address[ETH_ALEN]; + /* + * Followed by action_length bytes of FT Action frame (from Category + * field to the end of Action Frame body. + */ +} STRUCT_PACKED; + +#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 + +#define FT_PACKET_REQUEST 0 +#define FT_PACKET_RESPONSE 1 +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ +#define FT_PACKET_R0KH_R1KH_PULL 200 +#define FT_PACKET_R0KH_R1KH_RESP 201 +#define FT_PACKET_R0KH_R1KH_PUSH 202 + +#define FT_R0KH_R1KH_PULL_DATA_LEN 44 +#define FT_R0KH_R1KH_RESP_DATA_LEN 76 +#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 + +struct ft_r0kh_r1kh_pull_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ + le16 data_length; /* little endian length of data (44) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_resp_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ + le16 data_length; /* little endian length of data (76) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; /* copied from pull */ + u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ + u8 s1kh_id[ETH_ALEN]; /* copied from pull */ + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + le16 pairwise; + u8 pad[2]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_push_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ + le16 data_length; /* little endian length of data (88) */ + u8 ap_address[ETH_ALEN]; + + /* Encrypted with AES key-wrap */ + u8 timestamp[4]; /* current time in seconds since unix epoch, little + * endian */ + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + le16 pairwise; + u8 pad[6]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* per STA state machine data */ + +struct wpa_authenticator; +struct wpa_state_machine; +struct rsn_pmksa_cache_entry; +struct eapol_state_machine; + + +struct ft_remote_r0kh { + struct ft_remote_r0kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R0KH_ID_MAX_LEN]; + size_t id_len; + u8 key[16]; +}; + + +struct ft_remote_r1kh { + struct ft_remote_r1kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R1KH_ID_LEN]; + u8 key[16]; +}; + + +struct wpa_auth_config { + int wpa; + int wpa_key_mgmt; + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + int eapol_version; + int peerkey; + int wmm_enabled; + int wmm_uapsd; + int disable_pmksa_caching; + int okc; + int tx_status; +#ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R +#define SSID_LEN 32 + u8 ssid[SSID_LEN]; + size_t ssid_len; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; + size_t r0_key_holder_len; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; + int ft_over_ds; +#endif /* CONFIG_IEEE80211R */ + int disable_gtk; + int ap_mlme; +}; + +typedef enum { + LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING +} logger_level; + +typedef enum { + WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, + WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, + WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx +} wpa_eapol_variable; + +struct wpa_auth_callbacks { + void *ctx; + void (*logger)(void *ctx, const u8 *addr, logger_level level, + const char *txt); + void (*disconnect)(void *ctx, const u8 *addr, u16 reason); + int (*mic_failure_report)(void *ctx, const u8 *addr); + void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, + int value); + int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk); + int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); + int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, + const u8 *addr, int idx, u8 *key, size_t key_len); + int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); + int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt); + int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, + void *ctx), void *cb_ctx); + int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, + void *ctx), void *cb_ctx); + int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, + size_t data_len); +#ifdef CONFIG_IEEE80211R + struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*send_ft_action)(void *ctx, const u8 *dst, + const u8 *data, size_t data_len); + int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, + size_t tspec_ielen); +#endif /* CONFIG_IEEE80211R */ +}; + +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb); +int wpa_init_keys(struct wpa_authenticator *wpa_auth); +void wpa_deinit(struct wpa_authenticator *wpa_auth); +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf); + +enum { + WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, + WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, + WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, + WPA_INVALID_MDIE, WPA_INVALID_PROTO +}; + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len/*, + const u8 *mdie, size_t mdie_len*/); +int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); +int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm); +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); +void wpa_auth_sta_deinit(struct wpa_state_machine *sm); +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + u8 *data, size_t data_len); +typedef enum { + WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, + WPA_REAUTH_EAPOL, WPA_ASSOC_FT +} wpa_event; +void wpa_remove_ptk(struct wpa_state_machine *sm); +int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +void wpa_auth_sm_notify(struct wpa_state_machine *sm); +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); +int wpa_auth_pairwise_set(struct wpa_state_machine *sm); +int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, + size_t *len); +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol); +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol); +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int ack); + +#ifdef CONFIG_IEEE80211R +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, + size_t max_len, int auth_alg, + const u8 *req_ies, size_t req_ies_len); +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len), + void *ctx); +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len); +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); +#endif /* CONFIG_IEEE80211R */ + +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); + +int wpa_auth_uses_sae(struct wpa_state_machine *sm); + +#endif /* WPA_AUTH_H */ diff --git a/components/wpa_supplicant/include/wpa/wpa_auth_i.h b/components/wpa_supplicant/include/wpa/wpa_auth_i.h new file mode 100644 index 0000000000..53ad8ea941 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa_auth_i.h @@ -0,0 +1,234 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_AUTH_I_H +#define WPA_AUTH_I_H + +/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ +#define RSNA_MAX_EAPOL_RETRIES 4 + +struct wpa_group; + +struct wpa_stsl_negotiation { + struct wpa_stsl_negotiation *next; + u8 initiator[ETH_ALEN]; + u8 peer[ETH_ALEN]; +}; + + +struct wpa_state_machine { + struct wpa_authenticator *wpa_auth; + struct wpa_group *group; + + u8 addr[ETH_ALEN]; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, + WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, + WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, + WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, + WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE + } wpa_ptk_state; + + enum { + WPA_PTK_GROUP_IDLE = 0, + WPA_PTK_GROUP_REKEYNEGOTIATING, + WPA_PTK_GROUP_REKEYESTABLISHED, + WPA_PTK_GROUP_KEYERROR + } wpa_ptk_group_state; + + Boolean Init; + Boolean DeauthenticationRequest; + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; + int TimeoutCtr; + int GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; + Boolean EAPOLKeyRequest; + Boolean MICVerified; + Boolean GUpdateStationKeys; + u8 ANonce[WPA_NONCE_LEN]; + u8 SNonce[WPA_NONCE_LEN]; + u8 PMK[PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + struct wpa_key_replay_counter { + u8 counter[WPA_REPLAY_COUNTER_LEN]; + Boolean valid; + } key_replay[RSNA_MAX_EAPOL_RETRIES], + prev_key_replay[RSNA_MAX_EAPOL_RETRIES]; + Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ + Boolean PTKRequest; /* not in IEEE 802.11i state machine */ + Boolean has_GTK; + Boolean PtkGroupInit; /* init request for PTK Group state machine */ + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + unsigned int changed:1; + unsigned int in_step_loop:1; + unsigned int pending_deinit:1; + unsigned int started:1; + unsigned int mgmt_frame_prot:1; + unsigned int rx_eapol_key_secure:1; + unsigned int update_snonce:1; +#ifdef CONFIG_IEEE80211R + unsigned int ft_completed:1; + unsigned int pmk_r1_name_valid:1; +#endif /* CONFIG_IEEE80211R */ + unsigned int is_wnmsleep:1; + + u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int req_replay_counter_used; + + u8 *wpa_ie; + size_t wpa_ie_len; + + enum { + WPA_VERSION_NO_WPA = 0 /* WPA not used */, + WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, + WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ + } wpa; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ +// struct rsn_pmksa_cache_entry *pmksa; + +// u32 dot11RSNAStatsTKIPLocalMICFailures; +// u32 dot11RSNAStatsTKIPRemoteMICFailures; + +#ifdef CONFIG_IEEE80211R + u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + size_t xxkey_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth + * Request */ + u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ + size_t r0kh_id_len; + u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key + * message 2/4 */ + u8 *assoc_resp_ftie; +#endif /* CONFIG_IEEE80211R */ + + int pending_1_of_4_timeout; +}; + + +/* per group key state machine data */ +struct wpa_group { + struct wpa_group *next; + int vlan_id; + + Boolean GInit; + int GKeyDoneStations; + Boolean GTKReKey; + int GTK_len; + int GN, GM; + Boolean GTKAuthenticator; + u8 Counter[WPA_NONCE_LEN]; + + enum { + WPA_GROUP_GTK_INIT = 0, + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + } wpa_group_state; + + u8 GMK[WPA_GMK_LEN]; + u8 GTK[2][WPA_GTK_MAX_LEN]; + u8 GNonce[WPA_NONCE_LEN]; + Boolean changed; + Boolean first_sta_seen; + Boolean reject_4way_hs_for_entropy; +#ifdef CONFIG_IEEE80211W + u8 IGTK[2][WPA_IGTK_LEN]; + int GN_igtk, GM_igtk; +#endif /* CONFIG_IEEE80211W */ +}; + + +struct wpa_ft_pmk_cache; + +/* per authenticator data */ +struct wpa_authenticator { + struct wpa_group *group; + +// unsigned int dot11RSNAStatsTKIPRemoteMICFailures; +// u32 dot11RSNAAuthenticationSuiteSelected; +// u32 dot11RSNAPairwiseCipherSelected; +// u32 dot11RSNAGroupCipherSelected; +// u8 dot11RSNAPMKIDUsed[PMKID_LEN]; +// u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */ +// u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */ +// u32 dot11RSNAGroupCipherRequested; /* FIX: update */ +// unsigned int dot11RSNATKIPCounterMeasuresInvoked; +// unsigned int dot11RSNA4WayHandshakeFailures; + +// struct wpa_stsl_negotiation *stsl_negotiations; + + struct wpa_auth_config conf; +// struct wpa_auth_callbacks cb; + + u8 *wpa_ie; + size_t wpa_ie_len; + + u8 addr[ETH_ALEN]; + +// struct rsn_pmksa_cache *pmksa; +// struct wpa_ft_pmk_cache *ft_pmk_cache; +}; + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid); +#if 0 +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt); +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...); +#endif +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version); +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx); +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx); + +#ifdef CONFIG_PEERKEY +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg); +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); +int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, + size_t r0kh_id_len, + const u8 *anonce, const u8 *snonce, + u8 *buf, size_t len, const u8 *subelem, + size_t subelem_len); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk, size_t ptk_len); +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); +void wpa_ft_install_ptk(struct wpa_state_machine *sm); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_I_H */ diff --git a/components/wpa_supplicant/include/wpa/wpa_auth_ie.h b/components/wpa_supplicant/include/wpa/wpa_auth_ie.h new file mode 100644 index 0000000000..4999139510 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa_auth_ie.h @@ -0,0 +1,50 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_AUTH_IE_H +#define WPA_AUTH_IE_H + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; +#endif /* CONFIG_IEEE80211R */ +}; + +int wpa_parse_kde_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len); +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth); + +#endif /* WPA_AUTH_IE_H */ diff --git a/components/wpa_supplicant/include/wpa/wpa_common.h b/components/wpa_supplicant/include/wpa/wpa_common.h new file mode 100644 index 0000000000..480cf0e27e --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa_common.h @@ -0,0 +1,332 @@ +/* + * WPA definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "os.h" +#ifndef WPA_COMMON_H +#define WPA_COMMON_H + +#define WPA_MAX_SSID_LEN 32 + +/* IEEE 802.11i */ +#define PMKID_LEN 16 +#define PMK_LEN 32 +#define WPA_REPLAY_COUNTER_LEN 8 +#define WPA_NONCE_LEN 32 +#define WPA_KEY_RSC_LEN 8 +#define WPA_GMK_LEN 32 +#define WPA_GTK_MAX_LEN 32 + +#define WPA_SELECTOR_LEN 4 +#define WPA_VERSION 1 +#define RSN_SELECTOR_LEN 4 +#define RSN_VERSION 1 + +#define RSN_SELECTOR(a, b, c, d) \ + ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \ + (u32) (d)) + +#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) +#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) +#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) +#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1) +#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#if 0 +#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) +#endif +#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) +#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) + + +#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#ifdef CONFIG_IEEE80211R +#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#endif /* CONFIG_IEEE80211R */ +#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) + +#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) +#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#if 0 +#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#endif +#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#ifdef CONFIG_IEEE80211W +#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#endif /* CONFIG_IEEE80211W */ +#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) + +/* EAPOL-Key Key Data Encapsulation + * GroupKey and PeerKey require encryption, otherwise, encryption is optional. + */ +#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#if 0 +#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#endif +#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#ifdef CONFIG_PEERKEY +#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W +#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#endif /* CONFIG_IEEE80211W */ + +#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) + +#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val)) +#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a)) + +#define RSN_NUM_REPLAY_COUNTERS_1 0 +#define RSN_NUM_REPLAY_COUNTERS_2 1 +#define RSN_NUM_REPLAY_COUNTERS_4 2 +#define RSN_NUM_REPLAY_COUNTERS_16 3 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#ifdef CONFIG_IEEE80211W +#define WPA_IGTK_LEN 16 +#endif /* CONFIG_IEEE80211W */ + + +/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */ +#define WPA_CAPABILITY_PREAUTH BIT(0) +#define WPA_CAPABILITY_NO_PAIRWISE BIT(1) +/* B2-B3: PTKSA Replay Counter */ +/* B4-B5: GTKSA Replay Counter */ +#define WPA_CAPABILITY_MFPR BIT(6) +#define WPA_CAPABILITY_MFPC BIT(7) +#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) + + +/* IEEE 802.11r */ +#define MOBILITY_DOMAIN_ID_LEN 2 +#define FT_R0KH_ID_MAX_LEN 48 +#define FT_R1KH_ID_LEN 6 +#define WPA_PMK_NAME_LEN 16 + + +/* IEEE 802.11, 8.5.2 EAPOL-Key frames */ +#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2))) +#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) +#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) +#define WPA_KEY_INFO_TYPE_AES_128_CMAC 3 +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ +#define WPA_KEY_INFO_TXRX BIT(6) /* group */ +#define WPA_KEY_INFO_ACK BIT(7) +#define WPA_KEY_INFO_MIC BIT(8) +#define WPA_KEY_INFO_SECURE BIT(9) +#define WPA_KEY_INFO_ERROR BIT(10) +#define WPA_KEY_INFO_REQUEST BIT(11) +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ +#define WPA_KEY_INFO_SMK_MESSAGE BIT(13) + + +struct wpa_eapol_key { + u8 type; + /* Note: key_info, key_length, and key_data_length are unaligned */ + u8 key_info[2]; /* big endian */ + u8 key_length[2]; /* big endian */ + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + u8 key_nonce[WPA_NONCE_LEN]; + u8 key_iv[16]; + u8 key_rsc[WPA_KEY_RSC_LEN]; + u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ + u8 key_mic[16]; + u8 key_data_length[2]; /* big endian */ + /* followed by key_data_length bytes of key_data */ +} STRUCT_PACKED; + +/** + * struct wpa_ptk - WPA Pairwise Transient Key + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + */ +struct wpa_ptk { + u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */ + u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */ + u8 tk1[16]; /* Temporal Key 1 (TK1) */ + union { + u8 tk2[16]; /* Temporal Key 2 (TK2) */ + struct { + u8 tx_mic_key[8]; + u8 rx_mic_key[8]; + } auth; + } u; +} STRUCT_PACKED; + +struct wpa_gtk_data { + enum wpa_alg alg; + int tx, key_rsc_len, keyidx; + u8 gtk[32]; + int gtk_len; +}; + + +/* WPA IE version 1 + * 00-50-f2:1 (OUI:OUI type) + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: TKIP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: TKIP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * WPA Capabilities (2 octets, little endian) (default: 0) + */ + +struct wpa_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */ + u8 version[2]; /* little endian */ +} STRUCT_PACKED; + + +/* 1/4: PMKID + * 2/4: RSN IE + * 3/4: one or two RSN IEs + GTK IE (encrypted) + * 4/4: empty + * 1/2: GTK IE (encrypted) + * 2/2: empty + */ + +/* RSN IE version 1 + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: CCMP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: CCMP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * RSN Capabilities (2 octets, little endian) (default: 0) + * PMKID Count (2 octets) (default: 0) + * PMKID List (16 * n octets) + * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) + */ + +struct rsn_ie_hdr { + u8 elem_id; /* WLAN_EID_RSN */ + u8 len; + u8 version[2]; /* little endian */ +} STRUCT_PACKED; + + +#ifdef CONFIG_PEERKEY +enum { + STK_MUI_4WAY_STA_AP = 1, + STK_MUI_4WAY_STAT_STA = 2, + STK_MUI_GTK = 3, + STK_MUI_SMK = 4 +}; + +enum { + STK_ERR_STA_NR = 1, + STK_ERR_STA_NRSN = 2, + STK_ERR_CPHR_NS = 3, + STK_ERR_NO_STSL = 4 +}; +#endif /* CONFIG_PEERKEY */ + +struct rsn_error_kde { + be16 mui; + be16 error_type; +} STRUCT_PACKED; + +#ifdef CONFIG_IEEE80211W +struct wpa_igtk_kde { + u8 keyid[2]; + u8 pn[6]; + u8 igtk[WPA_IGTK_LEN]; +} STRUCT_PACKED; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R +struct rsn_mdie { + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 ft_capab; +} STRUCT_PACKED; + +#define RSN_FT_CAPAB_FT_OVER_DS BIT(0) +#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1) + +struct rsn_ftie { + u8 mic_control[2]; + u8 mic[16]; + u8 anonce[WPA_NONCE_LEN]; + u8 snonce[WPA_NONCE_LEN]; + /* followed by optional parameters */ +} STRUCT_PACKED; + +#define FTIE_SUBELEM_R1KH_ID 1 +#define FTIE_SUBELEM_GTK 2 +#define FTIE_SUBELEM_R0KH_ID 3 +#define FTIE_SUBELEM_IGTK 4 + +struct rsn_rdie { + u8 id; + u8 descr_count; + le16 status_code; +} STRUCT_PACKED; + +#endif /* CONFIG_IEEE80211R */ + +struct wpa_ie_data { + int proto; + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + size_t num_pmkid; + const u8 *pmkid; + int mgmt_group_cipher; +}; + +const char * wpa_cipher_txt(int cipher); + +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data); + +int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, + u8 *mic); +int wpa_compare_rsn_ie(int ft_initial_assoc, + const u8 *ie1, size_t ie1len, + const u8 *ie2, size_t ie2len); + +void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len, int use_sha256); + +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid, int use_sha256); + +#endif /* WPA_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa/wpa_debug.h b/components/wpa_supplicant/include/wpa/wpa_debug.h new file mode 100644 index 0000000000..b78a657e05 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa_debug.h @@ -0,0 +1,193 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_DEBUG_H +#define WPA_DEBUG_H + + +enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; + +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " + +int wpa_debug_open_file(const char *path); +void wpa_debug_close_file(void); + +/** + * wpa_debug_printf_timestamp - Print timestamp for debug output + * + * This function prints a timestamp in seconds_from_1970.microsoconds + * format if debug output has been configured to include timestamps in debug + * messages. + */ +void wpa_debug_print_timestamp(void); + +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +//#define DEBUG_PRINT +//#define MSG_PRINT + +/** + * wpa_hexdump - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. + */ +#ifdef DEBUG_PRINT +#define wpa_printf(level,fmt, args...) ets_printf(fmt,## args) + +static inline void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +{ + +} + +static inline void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, size_t len) +{ +} + + +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); + +static inline void wpa_hexdump_buf(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf)); +} + +/** + * wpa_hexdump_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. This works + * like wpa_hexdump(), but by default, does not include secret keys (passwords, + * etc.) in debug output. + */ +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); + + +static inline void wpa_hexdump_buf_key(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf)); +} + +/** + * wpa_hexdump_ascii - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. + */ +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len); + +/** + * wpa_hexdump_ascii_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump with both + * the hex numbers and ASCII characters (for printable range) are shown. 16 + * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by + * default, does not include secret keys (passwords, etc.) in debug output. + */ +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len); +#else +#define wpa_printf(level,fmt, args...) +#define wpa_hexdump(...) +#define wpa_hexdump_buf(...) +#define wpa_hexdump_key(...) +#define wpa_hexdump_buf_key(...) +#define wpa_hexdump_ascii(...) +#define wpa_hexdump_ascii_key(...) +#endif + +#define wpa_auth_logger +#define wpa_auth_vlogger + +/** + * wpa_msg - Conditional printf for default target and ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. This function is like wpa_printf(), but it also sends the + * same message to all attached ctrl_iface monitors. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it sends the output only to the + * attached ctrl_iface monitors. In other words, it can be used for frequent + * events that do not need to be sent to syslog. + */ +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt, + size_t len); + +typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); + +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data); + + +#endif /* WPA_DEBUG_H */ diff --git a/components/wpa_supplicant/include/wpa/wpa_i.h b/components/wpa_supplicant/include/wpa/wpa_i.h new file mode 100644 index 0000000000..a43c33d332 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa_i.h @@ -0,0 +1,89 @@ +/* + * Internal WPA/RSN supplicant state machine definitions + * Copyright (c) 2004-2010, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_I_H +#define WPA_I_H + +/** + * set_key - Configure encryption key + * @ifname: Interface name (for multi-SSID/VLAN support) + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); + * %WPA_ALG_NONE clears the key. + * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for + * IGTK + * @set_tx: configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16, IGTK: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + * + * Please note that TKIP keys include separate TX and RX MIC keys and + * some drivers may expect them in different order than wpa_supplicant + * is using. If the TX/RX keys are swapped, all TKIP encrypted packets + * will tricker Michael MIC errors. This can be fixed by changing the + * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key + * in driver_*.c set_key() implementation, see driver_ndis.c for an + * example on how this can be done. + */ + +typedef void (* WPA_SEND_FUNC)(struct pbuf *pb); + +typedef void (* WPA_SET_ASSOC_IE)(uint8 proto, u8 *assoc_buf, u32 assoc_wpa_ie_len); + +typedef void (*WPA_INSTALL_KEY) (enum wpa_alg alg, uint8 *addr, int key_idx, int set_tx, + uint8 *seq, size_t seq_len, uint8 *key, size_t key_len, int key_entry_valid); + +typedef void (*WPA_DEAUTH)(uint8 reason_code); + +typedef void (*WPA_NEG_COMPLETE)(); + +void wpa_register(char * payload, WPA_SEND_FUNC snd_func, \ + WPA_SET_ASSOC_IE set_assoc_ie_func, \ + WPA_INSTALL_KEY ppinstallkey, \ + WPA_DEAUTH wpa_deauth, \ + WPA_NEG_COMPLETE wpa_neg_complete); + +#include "pp/esf_buf.h" +void eapol_txcb(esf_buf_t *eb); + +void wpa_set_profile(uint32 wpa_proto); + +void wpa_set_bss(char *macddr, char * bssid, uint8 pairwise_cipher, uint8 group_cipher, char *passphrase, u8 *ssid, size_t ssid_len); + +int wpa_sm_rx_eapol(u8 *src_addr, u8 *buf, u32 len); +#endif /* WPA_I_H */ + diff --git a/components/wpa_supplicant/include/wpa/wpa_ie.h b/components/wpa_supplicant/include/wpa/wpa_ie.h new file mode 100644 index 0000000000..94518d8457 --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpa_ie.h @@ -0,0 +1,56 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_IE_H +#define WPA_IE_H + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *reassoc_deadline; + const u8 *key_lifetime; +#endif /* CONFIG_IEEE80211R */ +}; + +int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len); + +#endif /* WPA_IE_H */ diff --git a/components/wpa_supplicant/include/wpa/wpabuf.h b/components/wpa_supplicant/include/wpa/wpabuf.h new file mode 100644 index 0000000000..cccfcc80ef --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpabuf.h @@ -0,0 +1,168 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPABUF_H +#define WPABUF_H + +/* + * Internal data structure for wpabuf. Please do not touch this directly from + * elsewhere. This is only defined in header file to allow inline functions + * from this file to access data. + */ +struct wpabuf { + size_t size; /* total size of the allocated buffer */ + size_t used; /* length of data in the buffer */ + u8 *ext_data; /* pointer to external data; NULL if data follows + * struct wpabuf */ + /* optionally followed by the allocated buffer */ +}; + + +int wpabuf_resize(struct wpabuf **buf, size_t add_len); +struct wpabuf * wpabuf_alloc(size_t len); +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len); +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len); +struct wpabuf * wpabuf_dup(const struct wpabuf *src); +void wpabuf_free(struct wpabuf *buf); +void * wpabuf_put(struct wpabuf *buf, size_t len); +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); + + +/** + * wpabuf_size - Get the currently allocated size of a wpabuf buffer + * @buf: wpabuf buffer + * Returns: Currently allocated size of the buffer + */ +static inline size_t wpabuf_size(const struct wpabuf *buf) +{ + return buf->size; +} + +/** + * wpabuf_len - Get the current length of a wpabuf buffer data + * @buf: wpabuf buffer + * Returns: Currently used length of the buffer + */ +static inline size_t wpabuf_len(const struct wpabuf *buf) +{ + return buf->used; +} + +/** + * wpabuf_tailroom - Get size of available tail room in the end of the buffer + * @buf: wpabuf buffer + * Returns: Tail room (in bytes) of available space in the end of the buffer + */ +static inline size_t wpabuf_tailroom(const struct wpabuf *buf) +{ + return buf->size - buf->used; +} + +/** + * wpabuf_head - Get pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline const void * wpabuf_head(const struct wpabuf *buf) +{ + if (buf->ext_data) + return buf->ext_data; + return buf + 1; +} + +static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) +{ + return wpabuf_head(buf); +} + +/** + * wpabuf_mhead - Get modifiable pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline void * wpabuf_mhead(struct wpabuf *buf) +{ + if (buf->ext_data) + return buf->ext_data; + return buf + 1; +} + +static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) +{ + return wpabuf_mhead(buf); +} + +static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) +{ + u8 *pos = wpabuf_put(buf, 1); + *pos = data; +} + +static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_LE16(pos, data); +} + +static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_LE32(pos, data); +} + +static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_BE16(pos, data); +} + +static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 3); + WPA_PUT_BE24(pos, data); +} + +static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_BE32(pos, data); +} + +static inline void wpabuf_put_data(struct wpabuf *buf, const void *data, + size_t len) +{ + if (data) + os_memcpy(wpabuf_put(buf, len), data, len); +} + +static inline void wpabuf_put_buf(struct wpabuf *dst, + const struct wpabuf *src) +{ + wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src)); +} + +static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) +{ + buf->ext_data = (u8 *) data; + buf->size = buf->used = len; +} + +static inline void wpabuf_put_str(struct wpabuf *dst, const char *str) +{ + wpabuf_put_data(dst, str, os_strlen(str)); +} + +#endif /* WPABUF_H */ diff --git a/components/wpa_supplicant/include/wpa/wpas_glue.h b/components/wpa_supplicant/include/wpa/wpas_glue.h new file mode 100644 index 0000000000..7e254a2d7d --- /dev/null +++ b/components/wpa_supplicant/include/wpa/wpas_glue.h @@ -0,0 +1,31 @@ +/* + * WPA Supplicant - Glue code to setup EAPOL and RSN modules + * Copyright (c) 2003-2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPAS_GLUE_H +#define WPAS_GLUE_H + +u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos); + +int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, + int protect_type, int key_type); + +void wpa_sm_deauthenticate(struct wpa_sm *sm, uint8 reason_code); + +void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code); + +int wpa_sm_get_beacon_ie(struct wpa_sm *sm); + +#endif /* WPAS_GLUE_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap.h new file mode 100644 index 0000000000..e2cd2dd81d --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap.h @@ -0,0 +1,24 @@ +/* + * EAP peer state machine functions (RFC 4137) + * Copyright (c) 2004-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "wpa/defs.h" +#include "eap/eap_defs.h" + +struct eap_sm; + +struct eap_method_type { + int vendor; + u32 method; +}; + +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len); + +#endif /* EAP_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_common.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_common.h new file mode 100644 index 0000000000..38c5710058 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_common.h @@ -0,0 +1,23 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_COMMON_H +#define EAP_COMMON_H + +#include "wpa/wpabuf.h" + +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen); +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier); +void eap_update_len(struct wpabuf *msg); +u8 eap_get_id(const struct wpabuf *msg); +EapType eap_get_type(const struct wpabuf *msg); + +#endif /* EAP_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h new file mode 100644 index 0000000000..f35cbf43d7 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_config.h @@ -0,0 +1,220 @@ +/* + * EAP peer configuration data + * Copyright (c) 2003-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_CONFIG_H +#define EAP_CONFIG_H + +/** + * struct eap_peer_config - EAP peer configuration/credentials + */ +struct eap_peer_config { + /** + * identity - EAP Identity + * + * This field is used to set the real user identity or NAI (for + * EAP-PSK/PAX/SAKE/GPSK). + */ + u8 *identity; + + /** + * identity_len - EAP Identity length + */ + size_t identity_len; + + /** + * password - Password string for EAP + * + * This field can include either the plaintext password (default + * option) or a NtPasswordHash (16-byte MD4 hash of the unicode + * presentation of the password) if flags field has + * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can + * only be used with authentication mechanism that use this hash as the + * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2, + * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). + * + * In addition, this field is used to configure a pre-shared key for + * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK + * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length + * PSK. + */ + u8 *password; + + /** + * password_len - Length of password field + */ + size_t password_len; + + /** + * ca_cert - File path to CA certificate file (PEM/DER) + * + * This file can have one or more trusted CA certificates. If ca_cert + * and ca_path are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + * + * Alternatively, this can be used to only perform matching of the + * server certificate (SHA-256 hash of the DER encoded X.509 + * certificate). In this case, the possible CA certificates in the + * server certificate chain are ignored and only the server certificate + * is verified. This is configured with the following format: + * hash:://server/sha256/cert_hash_in_hex + * For example: "hash://server/sha256/ + * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a" + * + * On Windows, trusted CA certificates can be loaded from the system + * certificate store by setting this to cert_store://name, e.g., + * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + */ + u8 *ca_cert; + + /** + * ca_path - Directory path for CA certificate files (PEM) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + */ + u8 *ca_path; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with EAP method that use TLS authentication. + * Usually, this is only configured for EAP-TLS, even though this could + * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *private_key; + + /** + * private_key_passwd - Password for private key file + * + * If left out, this will be asked through control interface. + */ + u8 *private_key_passwd; + + char *phase1; + + /** + * pin - PIN for USIM, GSM SIM, and smartcards + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * If left out, this will be asked through control interface. + */ + char *pin; + + /** + * fragment_size - Maximum EAP fragment size in bytes (default 1398) + * + * This value limits the fragment size for EAP methods that support + * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set + * small enough to make the EAP messages fit in MTU of the network + * interface used for EAPOL. The default value is suitable for most + * cases. + */ + int fragment_size; + +#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) + /** + * flags - Network configuration flags (bitfield) + * + * This variable is used for internal flags to describe further details + * for the network parameters. + * bit 0 = password is represented as a 16-byte NtPasswordHash value + * instead of plaintext password + * bit 1 = password is stored in external storage; the value in the + * password field is the name of that external entry + */ + u32 flags; + + /** + * ocsp - Whether to use/require OCSP to check server certificate + * + * 0 = do not use OCSP stapling (TLS certificate status extension) + * 1 = try to use OCSP stapling, but not require response + * 2 = require valid OCSP stapling response + */ + int ocsp; +}; + + +/** + * struct wpa_config_blob - Named configuration blob + * + * This data structure is used to provide storage for binary objects to store + * abstract information like certificates and private keys inlined with the + * configuration data. + */ +struct wpa_config_blob { + /** + * name - Blob name + */ + char *name; + + /** + * data - Pointer to binary data + */ + u8 *data; + + /** + * len - Length of binary data + */ + size_t len; + + /** + * next - Pointer to next blob in the configuration + */ + struct wpa_config_blob *next; +}; + +#endif /* EAP_CONFIG_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_defs.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_defs.h new file mode 100644 index 0000000000..10995d3868 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_defs.h @@ -0,0 +1,92 @@ +/* + * EAP server/peer: Shared EAP definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_DEFS_H +#define EAP_DEFS_H + +/* RFC 3748 - Extensible Authentication Protocol (EAP) */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_hdr { + u8 code; + u8 identifier; + be16 length; /* including code and identifier; network byte order */ + /* followed by length-4 octets of data */ +} STRUCT_PACKED; + + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, + EAP_CODE_FAILURE = 4 }; + +/* EAP Request and Response data begins with one octet Type. Success and + * Failure do not have additional data. */ + +/* + * EAP Method Types as allocated by IANA: + * http://www.iana.org/assignments/eap-numbers + */ +typedef enum { + EAP_TYPE_NONE = 0, + EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, + EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, + EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, + EAP_TYPE_MD5 = 4, /* RFC 3748 */ + EAP_TYPE_OTP = 5 /* RFC 3748 */, + EAP_TYPE_GTC = 6, /* RFC 3748 */ + EAP_TYPE_TLS = 13 /* RFC 2716 */, + EAP_TYPE_LEAP = 17 /* Cisco proprietary */, + EAP_TYPE_SIM = 18 /* RFC 4186 */, + EAP_TYPE_TTLS = 21 /* RFC 5281 */, + EAP_TYPE_AKA = 23 /* RFC 4187 */, + EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, + EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, + EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, + EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment; + * type 38 has previously been allocated for + * EAP-HTTP Digest, (funk.com) */, + EAP_TYPE_FAST = 43 /* RFC 4851 */, + EAP_TYPE_PAX = 46 /* RFC 4746 */, + EAP_TYPE_PSK = 47 /* RFC 4764 */, + EAP_TYPE_SAKE = 48 /* RFC 4763 */, + EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, + EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */, + EAP_TYPE_GPSK = 51 /* RFC 5433 */, + EAP_TYPE_PWD = 52 /* RFC 5931 */, + EAP_TYPE_EKE = 53 /* RFC 6124 */, + EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ +} EapType; + + +/* SMI Network Management Private Enterprise Code for vendor specific types */ +enum { + EAP_VENDOR_IETF = 0, + EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ +}; + +struct eap_expand { + u8 vendor_id[3]; + be32 vendor_type; + u8 opcode; +} STRUCT_PACKED; + +#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE_UNAUTH_TLS 1 + +#define EAP_MSK_LEN 64 +#define EAP_EMSK_LEN 64 + +#endif /* EAP_DEFS_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h new file mode 100644 index 0000000000..a4779d13f8 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_i.h @@ -0,0 +1,88 @@ +/* + * EAP peer state machines internal structures (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_I_H +#define EAP_I_H + +#include "wpa/wpabuf.h" +#include "eap.h" +#include "eap_common.h" +#include "eap_config.h" + +/* RFC 4137 - EAP Peer state machine */ + +typedef enum { + DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC +} EapDecision; + +typedef enum { + METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE +} EapMethodState; + +/** + * struct eap_method_ret - EAP return values from struct eap_method::process() + * + * These structure contains OUT variables for the interface between peer state + * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as + * the return value of struct eap_method::process() so it is not included in + * this structure. + */ +struct eap_method_ret { + /** + * ignore - Whether method decided to drop the current packed (OUT) + */ + Boolean ignore; + + /** + * methodState - Method-specific state (IN/OUT) + */ + EapMethodState methodState; + + /** + * decision - Authentication decision (OUT) + */ + EapDecision decision; + + /** + * allowNotifications - Whether method allows notifications (OUT) + */ + Boolean allowNotifications; +}; + +#define CLIENT_CERT_NAME "CLC" +#define CA_CERT_NAME "CAC" +#define PRIVATE_KEY_NAME "PVK" +#define BLOB_NAME_LEN 3 +#define BLOB_NUM 2 + +/** + * struct eap_sm - EAP state machine data + */ +struct eap_sm { + void *eap_method_priv; + + void *ssl_ctx; + + unsigned int workaround; +///////////////////////////////////////////////// + struct pbuf *outbuf; + struct wpa_config_blob blob[BLOB_NUM]; + struct eap_peer_config config; + u8 current_identifier; + u8 ownaddr[ETH_ALEN]; +#ifdef USE_WPA2_TASK +#define SIG_WPA2_NUM 2 + u8 wpa2_sig_cnt[SIG_WPA2_NUM]; +#endif + u8 finish_state; +}; + +struct eap_peer_config * eap_get_config(struct eap_sm *sm); +const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, const char *name); + +#endif /* EAP_I_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls.h new file mode 100644 index 0000000000..a8a386f22c --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls.h @@ -0,0 +1,25 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2009, 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLS_H +#define EAP_TLS_H + +#include "eap_i.h" +#include "eap_common.h" +#include "eap.h" +#include "wpa/wpabuf.h" + +void * eap_tls_init(struct eap_sm *sm); +void eap_tls_deinit(struct eap_sm *sm, void *priv); +struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData); + +u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len); + +#endif /* EAP_TLS_H */ diff --git a/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls_common.h b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls_common.h new file mode 100644 index 0000000000..1a5e0f89e4 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/eap_peer/eap_tls_common.h @@ -0,0 +1,131 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2009, 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +/** + * struct eap_ssl_data - TLS data for EAP methods + */ +struct eap_ssl_data { + /** + * conn - TLS connection context data from tls_connection_init() + */ + struct tls_connection *conn; + + /** + * tls_out - TLS message to be sent out in fragments + */ + struct wpabuf *tls_out; + + /** + * tls_out_pos - The current position in the outgoing TLS message + */ + size_t tls_out_pos; + + /** + * tls_out_limit - Maximum fragment size for outgoing TLS messages + */ + size_t tls_out_limit; + + /** + * tls_in - Received TLS message buffer for re-assembly + */ + struct wpabuf *tls_in; + + /** + * tls_in_left - Number of remaining bytes in the incoming TLS message + */ + size_t tls_in_left; + + /** + * tls_in_total - Total number of bytes in the incoming TLS message + */ + size_t tls_in_total; + + /** + * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) + */ + int phase2; + + /** + * include_tls_length - Whether the TLS length field is included even + * if the TLS data is not fragmented + */ + int include_tls_length; + + /** + * eap - EAP state machine allocated with eap_peer_sm_init() + */ + struct eap_sm *eap; + + /** + * ssl_ctx - TLS library context to use for the connection + */ + void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_TLS_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + + +int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, + struct eap_peer_config *config, u8 eap_type); +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len); +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len); +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, + u8 id, const u8 *in_data, size_t in_len, + struct wpabuf **out_data); +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version); +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose); +const u8 * eap_peer_tls_process_init(struct eap_sm *sm, + struct eap_ssl_data *data, + EapType eap_type, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + size_t *len, u8 *flags); +void eap_peer_tls_reset_input(struct eap_ssl_data *data); +void eap_peer_tls_reset_output(struct eap_ssl_data *data); +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted); +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, u8 id, + const struct wpabuf *in_data, + struct wpabuf **out_data); +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types); +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/asn1.h b/components/wpa_supplicant/include/wpa2/tls/asn1.h new file mode 100644 index 0000000000..6342c4cc79 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/asn1.h @@ -0,0 +1,66 @@ +/* + * ASN.1 DER parsing + * Copyright (c) 2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ASN1_H +#define ASN1_H + +#define ASN1_TAG_EOC 0x00 /* not used with DER */ +#define ASN1_TAG_BOOLEAN 0x01 +#define ASN1_TAG_INTEGER 0x02 +#define ASN1_TAG_BITSTRING 0x03 +#define ASN1_TAG_OCTETSTRING 0x04 +#define ASN1_TAG_NULL 0x05 +#define ASN1_TAG_OID 0x06 +#define ASN1_TAG_OBJECT_DESCRIPTOR 0x07 /* not yet parsed */ +#define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ +#define ASN1_TAG_REAL 0x09 /* not yet parsed */ +#define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ +#define ANS1_TAG_RELATIVE_OID 0x0D +#define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ +#define ASN1_TAG_SET 0x11 +#define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */ +#define ASN1_TAG_PRINTABLESTRING 0x13 +#define ASN1_TAG_TG1STRING 0x14 /* not yet parsed */ +#define ASN1_TAG_VIDEOTEXSTRING 0x15 /* not yet parsed */ +#define ASN1_TAG_IA5STRING 0x16 +#define ASN1_TAG_UTCTIME 0x17 +#define ASN1_TAG_GENERALIZEDTIME 0x18 /* not yet parsed */ +#define ASN1_TAG_GRAPHICSTRING 0x19 /* not yet parsed */ +#define ASN1_TAG_VISIBLESTRING 0x1A +#define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ +#define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ + +#define ASN1_CLASS_UNIVERSAL 0 +#define ASN1_CLASS_APPLICATION 1 +#define ASN1_CLASS_CONTEXT_SPECIFIC 2 +#define ASN1_CLASS_PRIVATE 3 + + +struct asn1_hdr { + const u8 *payload; + u8 identifier, class, constructed; + unsigned int tag, length; +}; + +#define ASN1_MAX_OID_LEN 20 +struct asn1_oid { + unsigned long oid[ASN1_MAX_OID_LEN]; + size_t len; +}; + + +int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid); +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next); +void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); +unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len); + +#endif /* ASN1_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/bignum.h b/components/wpa_supplicant/include/wpa2/tls/bignum.h new file mode 100644 index 0000000000..f25e26783a --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/bignum.h @@ -0,0 +1,38 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BIGNUM_H +#define BIGNUM_H + +struct bignum; + +struct bignum * bignum_init(void); +void bignum_deinit(struct bignum *n); +size_t bignum_get_unsigned_bin_len(struct bignum *n); +int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len); +int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len); +int bignum_cmp(const struct bignum *a, const struct bignum *b); +int bignum_cmp_d(const struct bignum *a, unsigned long b); +int bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); +int bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); + +#endif /* BIGNUM_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/libtommath.h b/components/wpa_supplicant/include/wpa2/tls/libtommath.h new file mode 100644 index 0000000000..c0409b5e33 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/libtommath.h @@ -0,0 +1,3443 @@ +/* + * Minimal code for RSA support from LibTomMath 0.41 + * http://libtom.org/ + * http://libtom.org/files/ltm-0.41.tar.bz2 + * This library was released in public domain by Tom St Denis. + * + * The combination in this file may not use all of the optimized algorithms + * from LibTomMath and may be considerable slower than the LibTomMath with its + * default settings. The main purpose of having this version here is to make it + * easier to build bignum.c wrapper without having to install and build an + * external library. + * + * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this + * libtommath.c file instead of using the external LibTomMath library. + */ +#include "c_types.h" +#include "os.h" +#include "stdarg.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#define BN_MP_INVMOD_C +#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would + * require BN_MP_EXPTMOD_FAST_C instead */ +#define BN_S_MP_MUL_DIGS_C +#define BN_MP_INVMOD_SLOW_C +#define BN_S_MP_SQR_C +#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this + * would require other than mp_reduce */ + +#ifdef LTM_FAST + +/* Use faster div at the cost of about 1 kB */ +#define BN_MP_MUL_D_C + +/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MUL_2_C + +/* Include faster sqr at the cost of about 0.5 kB in code */ +#define BN_FAST_S_MP_SQR_C + +#else /* LTM_FAST */ + +#define BN_MP_DIV_SMALL +#define BN_MP_INIT_MULTI_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_ABS_C +#endif /* LTM_FAST */ + +/* Current uses do not require support for negative exponent in exptmod, so we + * can save about 1.5 kB in leaving out invmod. */ +#define LTM_NO_NEG_EXP + +/* from tommath.h */ + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#define OPT_CAST(x) (x *) + +typedef unsigned long mp_digit; +typedef u64 mp_word; + +#define DIGIT_BIT 28 +#define MP_28BIT + + +#define XMALLOC os_malloc +#define XFREE os_free +#define XREALLOC os_realloc + + +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) + +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +typedef int mp_err; + +/* define this to use lower memory usage routines (exptmods mostly) */ +#define MP_LOW_MEM + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + + +/* prototypes for copied functions */ +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +static int s_mp_sqr(mp_int * a, mp_int * b); +static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); + +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); + +#ifdef BN_MP_INIT_MULTI_C +static int mp_init_multi(mp_int *mp, ...); +#endif +#ifdef BN_MP_CLEAR_MULTI_C +static void mp_clear_multi(mp_int *mp, ...); +#endif +static int mp_lshd(mp_int * a, int b); +static void mp_set(mp_int * a, mp_digit b); +static void mp_clamp(mp_int * a); +static void mp_exch(mp_int * a, mp_int * b); +static void mp_rshd(mp_int * a, int b); +static void mp_zero(mp_int * a); +static int mp_mod_2d(mp_int * a, int b, mp_int * c); +static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); +static int mp_init_copy(mp_int * a, mp_int * b); +static int mp_mul_2d(mp_int * a, int b, mp_int * c); +#ifndef LTM_NO_NEG_EXP +static int mp_div_2(mp_int * a, mp_int * b); +static int mp_invmod(mp_int * a, mp_int * b, mp_int * c); +static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); +#endif /* LTM_NO_NEG_EXP */ +static int mp_copy(mp_int * a, mp_int * b); +static int mp_count_bits(mp_int * a); +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); +static int mp_mod(mp_int * a, mp_int * b, mp_int * c); +static int mp_grow(mp_int * a, int size); +static int mp_cmp_mag(mp_int * a, mp_int * b); +#ifdef BN_MP_ABS_C +static int mp_abs(mp_int * a, mp_int * b); +#endif +static int mp_sqr(mp_int * a, mp_int * b); +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); +static int mp_2expt(mp_int * a, int b); +static int mp_reduce_setup(mp_int * a, mp_int * b); +static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); +static int mp_init_size(mp_int * a, int size); +#ifdef BN_MP_EXPTMOD_FAST_C +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +#endif /* BN_MP_EXPTMOD_FAST_C */ +#ifdef BN_FAST_S_MP_SQR_C +static int fast_s_mp_sqr (mp_int * a, mp_int * b); +#endif /* BN_FAST_S_MP_SQR_C */ +#ifdef BN_MP_MUL_D_C +static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); +#endif /* BN_MP_MUL_D_C */ + + + +/* functions from bn_.c */ + + +/* reverse an array, used for radix code */ +static void ICACHE_FLASH_ATTR +bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} + + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +static int ICACHE_FLASH_ATTR +s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +static int ICACHE_FLASH_ATTR +s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* init a new mp_int */ +static int ICACHE_FLASH_ATTR +mp_init (mp_int * a) +{ + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} + + +/* clear one (frees) */ +static void ICACHE_FLASH_ATTR +mp_clear (mp_int * a) +{ + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} + + +/* high level addition (handles signs) */ +static int ICACHE_FLASH_ATTR +mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + + +/* high level subtraction (handles signs) */ +static int ICACHE_FLASH_ATTR +mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + + +/* high level multiplication (handles sign) */ +static int ICACHE_FLASH_ATTR +mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else +#endif +#ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else +#endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ +#ifdef BN_FAST_S_MP_MUL_DIGS_C + int digs = a->used + b->used + 1; + + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else +#endif +#ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else +#error mp_mul could fail + res = MP_VAL; +#endif + + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} + + +/* d = a * b (mod c) */ +static int ICACHE_FLASH_ATTR +mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} + + +/* c = a mod b, 0 <= c < b */ +static int ICACHE_FLASH_ATTR +mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted a lot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +static int ICACHE_FLASH_ATTR +mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { +#ifdef LTM_NO_NEG_EXP + return MP_VAL; +#else /* LTM_NO_NEG_EXP */ +#ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; +#else +#error mp_exptmod would always fail + /* no invmod */ + return MP_VAL; +#endif +#endif /* LTM_NO_NEG_EXP */ + } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); +#else + /* default to no */ + dr = 0; +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } +#endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y, 0); +#else +#error mp_exptmod could fail + /* no exptmod for evens */ + return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C + } +#endif +} + + +/* compare two ints (signed)*/ +static int ICACHE_FLASH_ATTR +mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} + + +/* compare a digit */ +static int ICACHE_FLASH_ATTR +mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int ICACHE_FLASH_ATTR +mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + +#ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); +#endif + +#ifndef BN_FAST_MP_INVMOD_C +#ifndef BN_MP_INVMOD_SLOW_C +#error mp_invmod would always fail +#endif +#endif + return MP_VAL; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* get the size for an unsigned equivalent */ +static int ICACHE_FLASH_ATTR +mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int ICACHE_FLASH_ATTR +mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&A, 1); + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* compare maginitude of two ints (unsigned) */ +static int ICACHE_FLASH_ATTR +mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} + + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +static int ICACHE_FLASH_ATTR +mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} + + +/* store in unsigned [big endian] format */ +static int ICACHE_FLASH_ATTR +mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} + + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +static int ICACHE_FLASH_ATTR +mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} + + +static int ICACHE_FLASH_ATTR +mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + return mp_copy (b, a); +} + + +/* set to zero */ +static void ICACHE_FLASH_ATTR +mp_zero (mp_int * a) +{ + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} + + +/* copy, b = a */ +static int ICACHE_FLASH_ATTR +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} + + +/* shift right a certain amount of digits */ +static void ICACHE_FLASH_ATTR +mp_rshd (mp_int * a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero (a); + return; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} + + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +static void ICACHE_FLASH_ATTR +mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} + + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +static void ICACHE_FLASH_ATTR +mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} + + +/* grow as required */ +static int ICACHE_FLASH_ATTR +mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} + + +#ifdef BN_MP_ABS_C +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +static int ICACHE_FLASH_ATTR +mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + + +/* set to a digit */ +static void ICACHE_FLASH_ATTR +mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} + + +#ifndef LTM_NO_NEG_EXP +/* b = a/2 */ +static int ICACHE_FLASH_ATTR +mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* shift left by a certain bit count */ +static int ICACHE_FLASH_ATTR +mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_INIT_MULTI_C +static int ICACHE_FLASH_ATTR +mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} +#endif + + +#ifdef BN_MP_CLEAR_MULTI_C +static void ICACHE_FLASH_ATTR +mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} +#endif + + +/* shift left a certain amount of digits */ +static int ICACHE_FLASH_ATTR +mp_lshd (mp_int * a, int b) +{ + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} + + +/* returns the number of bits in an int */ +static int ICACHE_FLASH_ATTR +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} + + +/* calc a value mod 2**b */ +static int ICACHE_FLASH_ATTR +mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +static int ICACHE_FLASH_ATTR +mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + +#else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. +*/ +static int ICACHE_FLASH_ATTR +mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = x.used == 0 ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_exch (&x, d); + } + + res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); + return res; +} + +#endif + + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +static int ICACHE_FLASH_ATTR +s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + int (*redux)(mp_int*,mp_int*,mp_int*); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} + + +/* computes b = a*a */ +static int ICACHE_FLASH_ATTR +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + +#ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else +#endif + { +#ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else +#endif +#ifdef BN_S_MP_SQR_C + res = s_mp_sqr (a, b); +#else +#error mp_sqr could fail + res = MP_VAL; +#endif + } + b->sign = MP_ZPOS; + return res; +} + + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +static int ICACHE_FLASH_ATTR +mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + + +/* determines the setup value */ +static int ICACHE_FLASH_ATTR +mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} + + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +static int ICACHE_FLASH_ATTR +mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accommodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} + + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +static int ICACHE_FLASH_ATTR +mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div (a, b, a, NULL); +} + + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +static int ICACHE_FLASH_ATTR +mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#else + { +#error mp_reduce would always fail + res = MP_VAL; + goto CLEANUP; + } +#endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + if ((res = mp_add (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} + + +/* multiplies |a| * |b| and only computes up to digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +static int ICACHE_FLASH_ATTR +s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + if ((res = mp_init_size (&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} + + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +static int ICACHE_FLASH_ATTR +fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + register mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < pa+1; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +/* init an mp_init for a given size */ +static int ICACHE_FLASH_ATTR +mp_init_size (mp_int * a, int size) +{ + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} + + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +static int ICACHE_FLASH_ATTR +s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*ix]) + + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + (2*ix + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) *tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} + + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +static int ICACHE_FLASH_ATTR +s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } +#endif + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} + + +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* setups the montgomery reduction stuff */ +static int ICACHE_FLASH_ATTR +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + + +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int ICACHE_FLASH_ATTR +fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + register mp_digit mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + register int iy; + register mp_digit *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_2_C +/* b = a*2 */ +static int ICACHE_FLASH_ATTR +mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accommodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally up to just under + * the leading bit of b. This saves a lot of multiple precision shifting. + */ +static int ICACHE_FLASH_ATTR +mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_EXPTMOD_FAST_C +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +static int ICACHE_FLASH_ATTR +mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto LBL_M; + } +#else + err = MP_VAL; + goto LBL_M; +#endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else +#endif + { +#ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto LBL_RES; + } +#else + err = MP_VAL; + goto LBL_RES; +#endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + + +#ifdef BN_FAST_S_MP_SQR_C +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +static int ICACHE_FLASH_ATTR +fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow (b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, (ty-tx+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix&1) == 0) { + _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used+a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp (b); + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_D_C +/* multiply by a digit */ +static int ICACHE_FLASH_ATTR +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif diff --git a/components/wpa_supplicant/include/wpa2/tls/pkcs1.h b/components/wpa_supplicant/include/wpa2/tls/pkcs1.h new file mode 100644 index 0000000000..ed64defaaf --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/pkcs1.h @@ -0,0 +1,22 @@ +/* + * PKCS #1 (RSA Encryption) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PKCS1_H +#define PKCS1_H + +int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, + int use_private, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); +int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); +int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); + +#endif /* PKCS1_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/pkcs5.h b/components/wpa_supplicant/include/wpa2/tls/pkcs5.h new file mode 100644 index 0000000000..20ddadc457 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/pkcs5.h @@ -0,0 +1,16 @@ +/* + * PKCS #5 (Password-based Encryption) + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PKCS5_H +#define PKCS5_H + +u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, + const u8 *enc_data, size_t enc_data_len, + const char *passwd, size_t *data_len); + +#endif /* PKCS5_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/pkcs8.h b/components/wpa_supplicant/include/wpa2/tls/pkcs8.h new file mode 100644 index 0000000000..bebf840ba7 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/pkcs8.h @@ -0,0 +1,16 @@ +/* + * PKCS #8 (Private-key information syntax) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PKCS8_H +#define PKCS8_H + +struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len); +struct crypto_private_key * +pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd); + +#endif /* PKCS8_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/rsa.h b/components/wpa_supplicant/include/wpa2/tls/rsa.h new file mode 100644 index 0000000000..c236a9df44 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/rsa.h @@ -0,0 +1,23 @@ +/* + * RSA + * Copyright (c) 2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RSA_H +#define RSA_H + +struct crypto_rsa_key; + +struct crypto_rsa_key * +crypto_rsa_import_public_key(const u8 *buf, size_t len); +struct crypto_rsa_key * +crypto_rsa_import_private_key(const u8 *buf, size_t len); +size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key); +int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, + struct crypto_rsa_key *key, int use_private); +void crypto_rsa_free(struct crypto_rsa_key *key); + +#endif /* RSA_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tls.h b/components/wpa_supplicant/include/wpa2/tls/tls.h new file mode 100644 index 0000000000..983999b51d --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tls.h @@ -0,0 +1,537 @@ +/* + * SSL/TLS interface definition + * Copyright (c) 2004-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLS_H +#define TLS_H + +struct tls_connection; + +struct tls_keys { + const u8 *master_key; /* TLS master secret */ + size_t master_key_len; + const u8 *client_random; + size_t client_random_len; + const u8 *server_random; + size_t server_random_len; +}; + +enum tls_event { + TLS_CERT_CHAIN_SUCCESS, + TLS_CERT_CHAIN_FAILURE, + TLS_PEER_CERTIFICATE, + TLS_ALERT +}; + +/* + * Note: These are used as identifier with external programs and as such, the + * values must not be changed. + */ +enum tls_fail_reason { + TLS_FAIL_UNSPECIFIED = 0, + TLS_FAIL_UNTRUSTED = 1, + TLS_FAIL_REVOKED = 2, + TLS_FAIL_NOT_YET_VALID = 3, + TLS_FAIL_EXPIRED = 4, + TLS_FAIL_SUBJECT_MISMATCH = 5, + TLS_FAIL_ALTSUBJECT_MISMATCH = 6, + TLS_FAIL_BAD_CERTIFICATE = 7, + TLS_FAIL_SERVER_CHAIN_PROBE = 8 +}; + +union tls_event_data { + struct { + int depth; + const char *subject; + enum tls_fail_reason reason; + const char *reason_txt; + const struct wpabuf *cert; + } cert_fail; + + struct { + int depth; + const char *subject; + const struct wpabuf *cert; + const u8 *hash; + size_t hash_len; + } peer_cert; + + struct { + int is_local; + const char *type; + const char *description; + } alert; +}; + +struct tls_config { + const char *opensc_engine_path; + const char *pkcs11_engine_path; + const char *pkcs11_module_path; + int fips_mode; + int cert_in_cb; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; +}; + +#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) +#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1) +#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) +#define TLS_CONN_REQUEST_OCSP BIT(3) +#define TLS_CONN_REQUIRE_OCSP BIT(4) + +/** + * struct tls_connection_params - Parameters for TLS connection + * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER + * format + * @ca_cert_blob: ca_cert as inlined data or %NULL if not used + * @ca_cert_blob_len: ca_cert_blob length + * @ca_path: Path to CA certificates (OpenSSL specific) + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * @altsubject_match: String to match in the alternative subject of the peer + * certificate or %NULL to allow all alternative subjects + * @client_cert: File or reference name for client X.509 certificate in PEM or + * DER format + * @client_cert_blob: client_cert as inlined data or %NULL if not used + * @client_cert_blob_len: client_cert_blob length + * @private_key: File or reference name for client private key in PEM or DER + * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used + * @dh_blob: dh_file as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * @engine: 1 = use engine (e.g., a smartcard) for private key operations + * (this is OpenSSL specific for now) + * @engine_id: engine id string (this is OpenSSL specific for now) + * @ppin: pointer to the pin variable in the configuration + * (this is OpenSSL specific for now) + * @key_id: the private key's id when using engine (this is OpenSSL + * specific for now) + * @cert_id: the certificate's id when using engine + * @ca_cert_id: the CA certificate's id when using engine + * @flags: Parameter options (TLS_CONN_*) + * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response + * or %NULL if OCSP is not enabled + * + * TLS connection parameters to be configured with tls_connection_set_params() + * and tls_global_set_params(). + * + * Certificates and private key can be configured either as a reference name + * (file path or reference to certificate store) or by providing the same data + * as a pointer to the data in memory. Only one option will be used for each + * field. + */ +struct tls_connection_params { + const char *ca_cert; + const u8 *ca_cert_blob; + size_t ca_cert_blob_len; + const char *ca_path; + const char *subject_match; + const char *altsubject_match; + const char *client_cert; + const u8 *client_cert_blob; + size_t client_cert_blob_len; + const char *private_key; + const u8 *private_key_blob; + size_t private_key_blob_len; + const char *private_key_passwd; + const char *dh_file; + const u8 *dh_blob; + size_t dh_blob_len; + + /* OpenSSL specific variables */ + int engine; + const char *engine_id; + const char *pin; + const char *key_id; + const char *cert_id; + const char *ca_cert_id; + + unsigned int flags; + const char *ocsp_stapling_response; +}; + + +/** + * tls_init - Initialize TLS library + * @conf: Configuration data for TLS library + * Returns: Context data to be used as tls_ctx in calls to other functions, + * or %NULL on failure. + * + * Called once during program startup and once for each RSN pre-authentication + * session. In other words, there can be two concurrent TLS contexts. If global + * library initialization is needed (i.e., one that is shared between both + * authentication types), the TLS library wrapper should maintain a reference + * counter and do global initialization only when moving from 0 to 1 reference. + */ +void * tls_init(void); + +/** + * tls_deinit - Deinitialize TLS library + * @tls_ctx: TLS context data from tls_init() + * + * Called once during program shutdown and once for each RSN pre-authentication + * session. If global library deinitialization is needed (i.e., one that is + * shared between both authentication types), the TLS library wrapper should + * maintain a reference counter and do global deinitialization only when moving + * from 1 to 0 references. + */ +void tls_deinit(void *tls_ctx); + +/** + * tls_get_errors - Process pending errors + * @tls_ctx: TLS context data from tls_init() + * Returns: Number of found error, 0 if no errors detected. + * + * Process all pending TLS errors. + */ +int tls_get_errors(void *tls_ctx); + +/** + * tls_connection_init - Initialize a new TLS connection + * @tls_ctx: TLS context data from tls_init() + * Returns: Connection context data, conn for other function calls + */ +struct tls_connection * tls_connection_init(void *tls_ctx); + +/** + * tls_connection_deinit - Free TLS connection data + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Release all resources allocated for TLS connection. + */ +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_established - Has the TLS connection been completed? + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if TLS connection has been completed, 0 if not. + */ +int tls_connection_established(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_shutdown - Shutdown TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 0 on success, -1 on failure + * + * Shutdown current TLS connection without releasing all resources. New + * connection can be started by using the same conn without having to call + * tls_connection_init() or setting certificates etc. again. The new + * connection should try to use session resumption. + */ +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, + TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 +}; + +/** + * tls_connection_set_params - Set TLS connection parameters + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @params: Connection parameters + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. + */ +int __must_check +tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params); + +/** + * tls_global_set_params - Set TLS parameters for all TLS connection + * @tls_ctx: TLS context data from tls_init() + * @params: Global TLS parameters + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. + */ +int __must_check tls_global_set_params( + void *tls_ctx, const struct tls_connection_params *params); + +/** + * tls_global_set_verify - Set global certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, + * 2 = verify CRL for all certificates + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); + +/** + * tls_connection_set_verify - Set certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @verify_peer: 1 = verify peer certificate + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_verify(void *tls_ctx, + struct tls_connection *conn, + int verify_peer); + +/** + * tls_connection_get_keys - Get master key and random data from TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_get_keys(void *tls_ctx, + struct tls_connection *conn, + struct tls_keys *keys); + +/** + * tls_connection_prf - Use TLS-PRF to derive keying material + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + * + * This function is optional to implement if tls_connection_get_keys() provides + * access to master secret and server/client random values. If these values are + * not exported from the TLS library, tls_connection_prf() is required so that + * further keying material can be derived from the master secret. If not + * implemented, the function will still need to be defined, but it can just + * return -1. Example implementation of this function is in tls_prf_sha1_md5() + * when it is called with seed set to client_random|server_random (or + * server_random|client_random). + */ +int __must_check tls_connection_prf(void *tls_ctx, + struct tls_connection *conn, + const char *label, + int server_random_first, + u8 *out, size_t out_len); + +/** + * tls_connection_handshake - Process TLS handshake (client side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS server + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. If the final + * handshake message includes application data, this is decrypted and + * appl_data (if not %NULL) is set to point this data. The caller is + * responsible for freeing appl_data. + * + * This function is used during TLS handshake. The first call is done with + * in_data == %NULL and the library is expected to return ClientHello packet. + * This packet is then send to the server and a response from server is given + * to TLS library by calling this function again with in_data pointing to the + * TLS message from the server. + * + * If the TLS handshake fails, this function may return %NULL. However, if the + * TLS library has a TLS alert to send out, that should be returned as the + * output data. In this case, tls_connection_get_failed() must return failure + * (> 0). + * + * tls_connection_established() should return 1 once the TLS handshake has been + * completed successfully. + */ +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *more_data_needed); + +/** + * tls_connection_server_handshake - Process TLS handshake (server side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS peer + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. + */ +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +/** + * tls_connection_encrypt - Encrypt data into TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Plaintext data to be encrypted + * Returns: Encrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. The caller is responsible for freeing the + * returned output data. + */ +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +/** + * tls_connection_decrypt - Decrypt data from TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Encrypted TLS data + * Returns: Decrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. The caller is responsible for + * freeing the returned output data. + */ +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *more_data_needed); + +/** + * tls_connection_resumed - Was session resumption used + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_CIPHER_NONE, + TLS_CIPHER_RC4_SHA /* 0x0005 */, + TLS_CIPHER_AES128_SHA /* 0x002f */, + TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ +}; + +/** + * tls_connection_set_cipher_list - Configure acceptable cipher suites + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_cipher_list(void *tls_ctx, + struct tls_connection *conn, + u8 *ciphers); + +/** + * tls_get_cipher - Get current cipher name + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** + * tls_connection_enable_workaround - Enable TLS workaround options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 0 on success, -1 on failure + * + * This function is used to enable connection-specific workaround options for + * buffer SSL/TLS implementations. + */ +int __must_check tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_client_hello_ext - Set TLS extension for ClientHello + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ext_type: Extension type + * @data: Extension payload (%NULL to remove extension) + * @data_len: Extension payload length + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_client_hello_ext(void *tls_ctx, + struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len); + +/** + * tls_connection_get_failed - Get connection failure status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns >0 if connection has failed, 0 if not. + */ +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_read_alerts - Get connection read alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Number of times a fatal read (remote end reported error) has + * happened during this connection. + */ +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_write_alerts - Get connection write alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Number of times a fatal write (locally detected error) has happened + * during this connection. + */ +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_get_keyblock_size - Get TLS key_block size + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_capabilities - Get supported TLS capabilities + * @tls_ctx: TLS context data from tls_init() + * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) + */ +unsigned int tls_capabilities(void *tls_ctx); + +typedef int (*tls_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +int __must_check tls_connection_set_session_ticket_cb( + void *tls_ctx, struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx); + +int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen); + +#endif /* TLS_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_client.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client.h new file mode 100644 index 0000000000..8ec85f1a91 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client.h @@ -0,0 +1,54 @@ +/* + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CLIENT_H +#define TLSV1_CLIENT_H + +#include "tlsv1_cred.h" + +struct tlsv1_client; + +int tlsv1_client_global_init(void); +void tlsv1_client_global_deinit(void); +struct tlsv1_client * tlsv1_client_init(void); +void tlsv1_client_deinit(struct tlsv1_client *conn); +int tlsv1_client_established(struct tlsv1_client *conn); +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_client_handshake(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len, int *need_more_data); +int tlsv1_client_encrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data); +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, + size_t buflen); +int tlsv1_client_shutdown(struct tlsv1_client *conn); +int tlsv1_client_resumed(struct tlsv1_client *conn); +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len); +int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred); +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); + +typedef int (*tlsv1_client_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, + tlsv1_client_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_CLIENT_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_client_i.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client_i.h new file mode 100644 index 0000000000..55fdcf8d04 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_client_i.h @@ -0,0 +1,84 @@ +/* + * TLSv1 client - internal structures + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CLIENT_I_H +#define TLSV1_CLIENT_I_H + +struct tlsv1_client { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + unsigned int certificate_requested:1; + unsigned int session_resumed:1; + unsigned int session_ticket_included:1; + unsigned int use_session_ticket:1; + unsigned int disable_time_checks:1; + + struct crypto_public_key *server_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 prev_cipher_suite; + + u8 *client_hello_ext; + size_t client_hello_ext_len; + + /* The prime modulus used for Diffie-Hellman */ + u8 *dh_p; + size_t dh_p_len; + /* The generator used for Diffie-Hellman */ + u8 *dh_g; + size_t dh_g_len; + /* The server's Diffie-Hellman public value */ + u8 *dh_ys; + size_t dh_ys_len; + + struct tlsv1_credentials *cred; + + tlsv1_client_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + struct wpabuf *partial_input; +}; + + +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description); +void tlsv1_client_free_dh(struct tlsv1_client *conn); +int tls_derive_pre_master_secret(u8 *pre_master_secret); +int tls_derive_keys(struct tlsv1_client *conn, + const u8 *pre_master_secret, size_t pre_master_secret_len); +u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len); +u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, + u8 description, size_t *out_len); +u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, + int no_appl_data); +int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, + const u8 *buf, size_t *len, + u8 **out_data, size_t *out_len); + +#endif /* TLSV1_CLIENT_I_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_common.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_common.h new file mode 100644 index 0000000000..f28c0cdc47 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_common.h @@ -0,0 +1,261 @@ +/* + * TLSv1 common definitions + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_COMMON_H +#define TLSV1_COMMON_H + +#include "crypto/crypto.h" + +#define TLS_VERSION_1 0x0301 /* TLSv1 */ +#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ +#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */ +#ifdef CONFIG_TLSV12 +#define TLS_VERSION TLS_VERSION_1_2 +#else /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 +#define TLS_VERSION TLS_VERSION_1_1 +#else /* CONFIG_TLSV11 */ +#define TLS_VERSION TLS_VERSION_1 +#endif /* CONFIG_TLSV11 */ +#endif /* CONFIG_TLSV12 */ +#define TLS_RANDOM_LEN 32 +#define TLS_PRE_MASTER_SECRET_LEN 48 +#define TLS_MASTER_SECRET_LEN 48 +#define TLS_SESSION_ID_MAX_LEN 32 +#define TLS_VERIFY_DATA_LEN 12 + +/* HandshakeType */ +enum { + TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0, + TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1, + TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2, + TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE = 11, + TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12, + TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13, + TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14, + TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15, + TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16, + TLS_HANDSHAKE_TYPE_FINISHED = 20, + TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */ +}; + +/* CipherSuite */ +#define TLS_NULL_WITH_NULL_NULL 0x0000 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_MD5 0x0001 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_SHA 0x0002 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_MD5 0x0004 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_SHA 0x0005 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006 /* RFC 2246 */ +#define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008 /* RFC 2246 */ +#define TLS_RSA_WITH_DES_CBC_SHA 0x0009 /* RFC 2246 */ +#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A /* RFC 2246 */ +#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B /* RFC 2246 */ +#define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C /* RFC 2246 */ +#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D /* RFC 2246 */ +#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E /* RFC 2246 */ +#define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F /* RFC 2246 */ +#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 /* RFC 2246 */ +#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 /* RFC 2246 */ +#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017 /* RFC 2246 */ +#define TLS_DH_anon_WITH_RC4_128_MD5 0x0018 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019 /* RFC 2246 */ +#define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A /* RFC 2246 */ +#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B /* RFC 2246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 /* RFC 3268 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */ +#define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */ + +/* CompressionMethod */ +#define TLS_COMPRESSION_NULL 0 + +/* HashAlgorithm */ +enum { + TLS_HASH_ALG_NONE = 0, + TLS_HASH_ALG_MD5 = 1, + TLS_HASH_ALG_SHA1 = 2, + TLS_HASH_ALG_SHA224 = 3, + TLS_HASH_ALG_SHA256 = 4, + TLS_HASH_ALG_SHA384 = 5, + TLS_HASH_ALG_SHA512 = 6 +}; + +/* SignatureAlgorithm */ +enum { + TLS_SIGN_ALG_ANONYMOUS = 0, + TLS_SIGN_ALG_RSA = 1, + TLS_SIGN_ALG_DSA = 2, + TLS_SIGN_ALG_ECDSA = 3, +}; + +/* AlertLevel */ +#define TLS_ALERT_LEVEL_WARNING 1 +#define TLS_ALERT_LEVEL_FATAL 2 + +/* AlertDescription */ +#define TLS_ALERT_CLOSE_NOTIFY 0 +#define TLS_ALERT_UNEXPECTED_MESSAGE 10 +#define TLS_ALERT_BAD_RECORD_MAC 20 +#define TLS_ALERT_DECRYPTION_FAILED 21 +#define TLS_ALERT_RECORD_OVERFLOW 22 +#define TLS_ALERT_DECOMPRESSION_FAILURE 30 +#define TLS_ALERT_HANDSHAKE_FAILURE 40 +#define TLS_ALERT_BAD_CERTIFICATE 42 +#define TLS_ALERT_UNSUPPORTED_CERTIFICATE 43 +#define TLS_ALERT_CERTIFICATE_REVOKED 44 +#define TLS_ALERT_CERTIFICATE_EXPIRED 45 +#define TLS_ALERT_CERTIFICATE_UNKNOWN 46 +#define TLS_ALERT_ILLEGAL_PARAMETER 47 +#define TLS_ALERT_UNKNOWN_CA 48 +#define TLS_ALERT_ACCESS_DENIED 49 +#define TLS_ALERT_DECODE_ERROR 50 +#define TLS_ALERT_DECRYPT_ERROR 51 +#define TLS_ALERT_EXPORT_RESTRICTION 60 +#define TLS_ALERT_PROTOCOL_VERSION 70 +#define TLS_ALERT_INSUFFICIENT_SECURITY 71 +#define TLS_ALERT_INTERNAL_ERROR 80 +#define TLS_ALERT_USER_CANCELED 90 +#define TLS_ALERT_NO_RENEGOTIATION 100 +#define TLS_ALERT_UNSUPPORTED_EXTENSION 110 /* RFC 4366 */ +#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE 111 /* RFC 4366 */ +#define TLS_ALERT_UNRECOGNIZED_NAME 112 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE 113 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE 114 /* RFC 4366 */ + +/* ChangeCipherSpec */ +enum { + TLS_CHANGE_CIPHER_SPEC = 1 +}; + +/* TLS Extensions */ +#define TLS_EXT_SERVER_NAME 0 /* RFC 4366 */ +#define TLS_EXT_MAX_FRAGMENT_LENGTH 1 /* RFC 4366 */ +#define TLS_EXT_CLIENT_CERTIFICATE_URL 2 /* RFC 4366 */ +#define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ +#define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ +#define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ + +#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ + + +typedef enum { + TLS_KEY_X_NULL, + TLS_KEY_X_RSA, + TLS_KEY_X_RSA_EXPORT, + TLS_KEY_X_DH_DSS_EXPORT, + TLS_KEY_X_DH_DSS, + TLS_KEY_X_DH_RSA_EXPORT, + TLS_KEY_X_DH_RSA, + TLS_KEY_X_DHE_DSS_EXPORT, + TLS_KEY_X_DHE_DSS, + TLS_KEY_X_DHE_RSA_EXPORT, + TLS_KEY_X_DHE_RSA, + TLS_KEY_X_DH_anon_EXPORT, + TLS_KEY_X_DH_anon +} tls_key_exchange; + +typedef enum { + TLS_CIPHER_NULL, + TLS_CIPHER_RC4_40, + TLS_CIPHER_RC4_128, + TLS_CIPHER_RC2_CBC_40, + TLS_CIPHER_IDEA_CBC, + TLS_CIPHER_DES40_CBC, + TLS_CIPHER_DES_CBC, + TLS_CIPHER_3DES_EDE_CBC, + TLS_CIPHER_AES_128_CBC, + TLS_CIPHER_AES_256_CBC +} tls_cipher; + +typedef enum { + TLS_HASH_NULL, + TLS_HASH_MD5, + TLS_HASH_SHA, + TLS_HASH_SHA256 +} tls_hash; + +struct tls_cipher_suite { + u16 suite; + tls_key_exchange key_exchange; + tls_cipher cipher; + tls_hash hash; +}; + +typedef enum { + TLS_CIPHER_STREAM, + TLS_CIPHER_BLOCK +} tls_cipher_type; + +struct tls_cipher_data { + tls_cipher cipher; + tls_cipher_type type; + size_t key_material; + size_t expanded_key_material; + size_t block_size; /* also iv_size */ + enum crypto_cipher_alg alg; +}; + + +struct tls_verify_hash { + struct crypto_hash *md5_client; + struct crypto_hash *sha1_client; + struct crypto_hash *sha256_client; + struct crypto_hash *md5_server; + struct crypto_hash *sha1_server; + struct crypto_hash *sha256_server; + struct crypto_hash *md5_cert; + struct crypto_hash *sha1_cert; + struct crypto_hash *sha256_cert; +}; + + +const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite); +const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher); +int tls_server_key_exchange_allowed(tls_cipher cipher); +int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk); +int tls_verify_hash_init(struct tls_verify_hash *verify); +void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, + size_t len); +void tls_verify_hash_free(struct tls_verify_hash *verify); +int tls_version_ok(u16 ver); +const char * tls_version_str(u16 ver); +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen); + +#endif /* TLSV1_COMMON_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_cred.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_cred.h new file mode 100644 index 0000000000..68fbdc9230 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_cred.h @@ -0,0 +1,40 @@ +/* + * TLSv1 credentials + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CRED_H +#define TLSV1_CRED_H + +struct tlsv1_credentials { + struct x509_certificate *trusted_certs; + struct x509_certificate *cert; + struct crypto_private_key *key; + + /* Diffie-Hellman parameters */ + u8 *dh_p; /* prime */ + size_t dh_p_len; + u8 *dh_g; /* generator */ + size_t dh_g_len; +}; + + +struct tlsv1_credentials * tlsv1_cred_alloc(void); +void tlsv1_cred_free(struct tlsv1_credentials *cred); +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path); +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len); +int tlsv1_set_private_key(struct tlsv1_credentials *cred, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len); +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len); + +#endif /* TLSV1_CRED_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_record.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_record.h new file mode 100644 index 0000000000..48abcb0d25 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_record.h @@ -0,0 +1,71 @@ +/* + * TLSv1 Record Protocol + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_RECORD_H +#define TLSV1_RECORD_H + +#include "crypto/crypto.h" + +#define TLS_MAX_WRITE_MAC_SECRET_LEN 32 +#define TLS_MAX_WRITE_KEY_LEN 32 +#define TLS_MAX_IV_LEN 16 +#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \ + TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN)) + +#define TLS_SEQ_NUM_LEN 8 +#define TLS_RECORD_HEADER_LEN 5 + +/* ContentType */ +enum { + TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20, + TLS_CONTENT_TYPE_ALERT = 21, + TLS_CONTENT_TYPE_HANDSHAKE = 22, + TLS_CONTENT_TYPE_APPLICATION_DATA = 23 +}; + +struct tlsv1_record_layer { + u16 tls_version; + + u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; + u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN]; + u8 write_key[TLS_MAX_WRITE_KEY_LEN]; + u8 read_key[TLS_MAX_WRITE_KEY_LEN]; + u8 write_iv[TLS_MAX_IV_LEN]; + u8 read_iv[TLS_MAX_IV_LEN]; + + size_t hash_size; + size_t key_material_len; + size_t iv_size; /* also block_size */ + + enum crypto_hash_alg hash_alg; + enum crypto_cipher_alg cipher_alg; + + u8 write_seq_num[TLS_SEQ_NUM_LEN]; + u8 read_seq_num[TLS_SEQ_NUM_LEN]; + + u16 cipher_suite; + u16 write_cipher_suite; + u16 read_cipher_suite; + + struct crypto_cipher *write_cbc; + struct crypto_cipher *read_cbc; +}; + + +int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, + u16 cipher_suite); +int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl); +int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl); +int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len); +int tlsv1_record_receive(struct tlsv1_record_layer *rl, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t *out_len, u8 *alert); + +#endif /* TLSV1_RECORD_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_server.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server.h new file mode 100644 index 0000000000..a18c69e37c --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server.h @@ -0,0 +1,48 @@ +/* + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_SERVER_H +#define TLSV1_SERVER_H + +#include "tlsv1_cred.h" + +struct tlsv1_server; + +int tlsv1_server_global_init(void); +void tlsv1_server_global_deinit(void); +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); +void tlsv1_server_deinit(struct tlsv1_server *conn); +int tlsv1_server_established(struct tlsv1_server *conn); +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, size_t *out_len); +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen); +int tlsv1_server_shutdown(struct tlsv1_server *conn); +int tlsv1_server_resumed(struct tlsv1_server *conn); +int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); + +typedef int (*tlsv1_server_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, + tlsv1_server_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_SERVER_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/tlsv1_server_i.h b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server_i.h new file mode 100644 index 0000000000..1f61533a5a --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/tlsv1_server_i.h @@ -0,0 +1,71 @@ +/* + * TLSv1 server - internal structures + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_SERVER_I_H +#define TLSV1_SERVER_I_H + +struct tlsv1_server { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE, + CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + struct crypto_public_key *client_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 cipher_suite; + + struct tlsv1_credentials *cred; + + int verify_peer; + u16 client_version; + + u8 *session_ticket; + size_t session_ticket_len; + + tlsv1_server_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + int use_session_ticket; + + u8 *dh_secret; + size_t dh_secret_len; +}; + + +void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); +int tlsv1_server_derive_keys(struct tlsv1_server *conn, + const u8 *pre_master_secret, + size_t pre_master_secret_len); +u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len); +u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, + u8 description, size_t *out_len); +int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, + const u8 *buf, size_t *len); + +#endif /* TLSV1_SERVER_I_H */ diff --git a/components/wpa_supplicant/include/wpa2/tls/x509v3.h b/components/wpa_supplicant/include/wpa2/tls/x509v3.h new file mode 100644 index 0000000000..91a35baf92 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/tls/x509v3.h @@ -0,0 +1,123 @@ +/* + * X.509v3 certificate parsing and processing + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef X509V3_H +#define X509V3_H + +#include "asn1.h" + +struct x509_algorithm_identifier { + struct asn1_oid oid; +}; + +struct x509_name_attr { + enum x509_name_attr_type { + X509_NAME_ATTR_NOT_USED, + X509_NAME_ATTR_DC, + X509_NAME_ATTR_CN, + X509_NAME_ATTR_C, + X509_NAME_ATTR_L, + X509_NAME_ATTR_ST, + X509_NAME_ATTR_O, + X509_NAME_ATTR_OU + } type; + char *value; +}; + +#define X509_MAX_NAME_ATTRIBUTES 20 + +struct x509_name { + struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES]; + size_t num_attr; + char *email; /* emailAddress */ + + /* from alternative name extension */ + char *alt_email; /* rfc822Name */ + char *dns; /* dNSName */ + char *uri; /* uniformResourceIdentifier */ + u8 *ip; /* iPAddress */ + size_t ip_len; /* IPv4: 4, IPv6: 16 */ + struct asn1_oid rid; /* registeredID */ +}; + +struct x509_certificate { + struct x509_certificate *next; + enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; + unsigned long serial_number; + struct x509_algorithm_identifier signature; + struct x509_name issuer; + struct x509_name subject; + os_time_t not_before; + os_time_t not_after; + struct x509_algorithm_identifier public_key_alg; + u8 *public_key; + size_t public_key_len; + struct x509_algorithm_identifier signature_alg; + u8 *sign_value; + size_t sign_value_len; + + /* Extensions */ + unsigned int extensions_present; +#define X509_EXT_BASIC_CONSTRAINTS (1 << 0) +#define X509_EXT_PATH_LEN_CONSTRAINT (1 << 1) +#define X509_EXT_KEY_USAGE (1 << 2) +#define X509_EXT_SUBJECT_ALT_NAME (1 << 3) +#define X509_EXT_ISSUER_ALT_NAME (1 << 4) + + /* BasicConstraints */ + int ca; /* cA */ + unsigned long path_len_constraint; /* pathLenConstraint */ + + /* KeyUsage */ + unsigned long key_usage; +#define X509_KEY_USAGE_DIGITAL_SIGNATURE (1 << 0) +#define X509_KEY_USAGE_NON_REPUDIATION (1 << 1) +#define X509_KEY_USAGE_KEY_ENCIPHERMENT (1 << 2) +#define X509_KEY_USAGE_DATA_ENCIPHERMENT (1 << 3) +#define X509_KEY_USAGE_KEY_AGREEMENT (1 << 4) +#define X509_KEY_USAGE_KEY_CERT_SIGN (1 << 5) +#define X509_KEY_USAGE_CRL_SIGN (1 << 6) +#define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) +#define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + + /* + * The DER format certificate follows struct x509_certificate. These + * pointers point to that buffer. + */ + const u8 *cert_start; + size_t cert_len; + const u8 *tbs_cert_start; + size_t tbs_cert_len; +}; + +enum { + X509_VALIDATE_OK, + X509_VALIDATE_BAD_CERTIFICATE, + X509_VALIDATE_UNSUPPORTED_CERTIFICATE, + X509_VALIDATE_CERTIFICATE_REVOKED, + X509_VALIDATE_CERTIFICATE_EXPIRED, + X509_VALIDATE_CERTIFICATE_UNKNOWN, + X509_VALIDATE_UNKNOWN_CA +}; + +void x509_certificate_free(struct x509_certificate *cert); +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_name_string(struct x509_name *name, char *buf, size_t len); +int x509_name_compare(struct x509_name *a, struct x509_name *b); +void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_certificate_check_signature(struct x509_certificate *issuer, + struct x509_certificate *cert); +int x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason, int disable_time_checks); +struct x509_certificate * +x509_certificate_get_subject(struct x509_certificate *chain, + struct x509_name *name); +int x509_certificate_self_signed(struct x509_certificate *cert); + +#endif /* X509V3_H */ diff --git a/components/wpa_supplicant/include/wpa2/utils/base64.h b/components/wpa_supplicant/include/wpa2/utils/base64.h new file mode 100644 index 0000000000..91eb874198 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/utils/base64.h @@ -0,0 +1,17 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BASE64_H +#define BASE64_H + +unsigned char * _base64_encode(const unsigned char *src, size_t len, + size_t *out_len); +unsigned char * _base64_decode(const unsigned char *src, size_t len, + size_t *out_len); + +#endif /* BASE64_H */ diff --git a/components/wpa_supplicant/include/wpa2/utils/ext_password.h b/components/wpa_supplicant/include/wpa2/utils/ext_password.h new file mode 100644 index 0000000000..e3e46ea084 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/utils/ext_password.h @@ -0,0 +1,33 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_H +#define EXT_PASSWORD_H + +struct ext_password_data; + +#ifdef CONFIG_EXT_PASSWORD + +struct ext_password_data * ext_password_init(const char *backend, + const char *params); +void ext_password_deinit(struct ext_password_data *data); + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name); +void ext_password_free(struct wpabuf *pw); + +#else /* CONFIG_EXT_PASSWORD */ + +#define ext_password_init(b, p) ((void *) 1) +#define ext_password_deinit(d) do { } while (0) +#define ext_password_get(d, n) (NULL) +#define ext_password_free(p) do { } while (0) + +#endif /* CONFIG_EXT_PASSWORD */ + +#endif /* EXT_PASSWORD_H */ diff --git a/components/wpa_supplicant/include/wpa2/utils/ext_password_i.h b/components/wpa_supplicant/include/wpa2/utils/ext_password_i.h new file mode 100644 index 0000000000..043e7312c6 --- /dev/null +++ b/components/wpa_supplicant/include/wpa2/utils/ext_password_i.h @@ -0,0 +1,23 @@ +/* + * External password backend - internal definitions + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_I_H +#define EXT_PASSWORD_I_H + +#include "ext_password.h" + +struct ext_password_backend { + const char *name; + void * (*init)(const char *params); + void (*deinit)(void *ctx); + struct wpabuf * (*get)(void *ctx, const char *name); +}; + +struct wpabuf * ext_password_alloc(size_t len); + +#endif /* EXT_PASSWORD_I_H */ diff --git a/components/wpa_supplicant/port/include/byteswap.h b/components/wpa_supplicant/port/include/byteswap.h new file mode 100644 index 0000000000..1a8bb8fd15 --- /dev/null +++ b/components/wpa_supplicant/port/include/byteswap.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010 Espressif System + */ + +#ifndef BYTESWAP_H +#define BYTESWAP_H + +/* Swap bytes in 16 bit value. */ +#ifdef __GNUC__ +# define __bswap_16(x) \ + (__extension__ \ + ({ unsigned short int __bsx = (x); \ + ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); })) +#else +static INLINE unsigned short int +__bswap_16 (unsigned short int __bsx) +{ + return ((((__bsx) >> 8) & 0xff) | (((__bsx) & 0xff) << 8)); +} +#endif + +/* Swap bytes in 32 bit value. */ +#ifdef __GNUC__ +# define __bswap_32(x) \ + (__extension__ \ + ({ unsigned int __bsx = (x); \ + ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | \ + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); })) +#else +static INLINE unsigned int +__bswap_32 (unsigned int __bsx) +{ + return ((((__bsx) & 0xff000000) >> 24) | (((__bsx) & 0x00ff0000) >> 8) | + (((__bsx) & 0x0000ff00) << 8) | (((__bsx) & 0x000000ff) << 24)); +} +#endif + +#if defined __GNUC__ && __GNUC__ >= 2 +/* Swap bytes in 64 bit value. */ +# define __bswap_constant_64(x) \ + ((((x) & 0xff00000000000000ull) >> 56) \ + | (((x) & 0x00ff000000000000ull) >> 40) \ + | (((x) & 0x0000ff0000000000ull) >> 24) \ + | (((x) & 0x000000ff00000000ull) >> 8) \ + | (((x) & 0x00000000ff000000ull) << 8) \ + | (((x) & 0x0000000000ff0000ull) << 24) \ + | (((x) & 0x000000000000ff00ull) << 40) \ + | (((x) & 0x00000000000000ffull) << 56)) + +# define __bswap_64(x) \ + (__extension__ \ + ({ union { __extension__ unsigned long long int __ll; \ + unsigned int __l[2]; } __w, __r; \ + if (__builtin_constant_p (x)) \ + __r.__ll = __bswap_constant_64 (x); \ + else \ + { \ + __w.__ll = (x); \ + __r.__l[0] = __bswap_32 (__w.__l[1]); \ + __r.__l[1] = __bswap_32 (__w.__l[0]); \ + } \ + __r.__ll; })) +#endif + +#endif /* BYTESWAP_H */ diff --git a/components/wpa_supplicant/port/include/endian.h b/components/wpa_supplicant/port/include/endian.h new file mode 100644 index 0000000000..e2df616b79 --- /dev/null +++ b/components/wpa_supplicant/port/include/endian.h @@ -0,0 +1,229 @@ +/*- + * Copyright (c) 2002 Thomas Moestl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _ENDIAN_H_ +#define _ENDIAN_H_ + +#include "byteswap.h" + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif + +#ifndef BYTE_ORDER +#ifdef __IEEE_LITTLE_ENDIAN +#define BYTE_ORDER LITTLE_ENDIAN +#else +#define BYTE_ORDER BIG_ENDIAN +#endif +#endif + +#define _UINT8_T_DECLARED +#ifndef _UINT8_T_DECLARED +typedef __uint8_t uint8_t; +#define _UINT8_T_DECLARED +#endif + +#define _UINT16_T_DECLARED +#ifndef _UINT16_T_DECLARED +typedef __uint16_t uint16_t; +#define _UINT16_T_DECLARED +#endif + +#define _UINT32_T_DECLARED +#ifndef _UINT32_T_DECLARED +typedef __uint32_t uint32_t; +#define _UINT32_T_DECLARED +#endif + +#define _UINT64_T_DECLARED +#ifndef _UINT64_T_DECLARED +typedef __uint64_t uint64_t; +#define _UINT64_T_DECLARED +#endif + +/* + * General byte order swapping functions. + */ +#define bswap16(x) __bswap16(x) +#define bswap32(x) __bswap32(x) +#define bswap64(x) __bswap64(x) + +/* + * Host to big endian, host to little endian, big endian to host, and little + * endian to host byte order functions as detailed in byteorder(9). + */ +#if 1 //BYTE_ORDER == _LITTLE_ENDIAN +#define __bswap16 __bswap_16 +#define __bswap32 __bswap_32 +#define htobe16(x) bswap16((x)) +#define htobe32(x) bswap32((x)) +#define htobe64(x) bswap64((x)) +#define htole16(x) ((uint16_t)(x)) +#define htole32(x) ((uint32_t)(x)) +#define htole64(x) ((uint64_t)(x)) + +#define be16toh(x) bswap16((x)) +#define be32toh(x) bswap32((x)) +#define be64toh(x) bswap64((x)) +#define le16toh(x) ((uint16_t)(x)) +#define le32toh(x) ((uint32_t)(x)) +#define le64toh(x) ((uint64_t)(x)) + +#ifndef htons +#define htons htobe16 +#endif //htons + +#else /* _BYTE_ORDER != _LITTLE_ENDIAN */ +#define htobe16(x) ((uint16_t)(x)) +#define htobe32(x) ((uint32_t)(x)) +#define htobe64(x) ((uint64_t)(x)) +#define htole16(x) bswap16((x)) +#define htole32(x) bswap32((x)) +#define htole64(x) bswap64((x)) + +#define be16toh(x) ((uint16_t)(x)) +#define be32toh(x) ((uint32_t)(x)) +#define be64toh(x) ((uint64_t)(x)) +#define le16toh(x) bswap16((x)) +#define le32toh(x) bswap32((x)) +#define le64toh(x) bswap64((x)) +#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */ + +/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */ + +static INLINE uint16_t +be16dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return ((p[0] << 8) | p[1]); +} + +static INLINE uint32_t +be32dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static INLINE uint64_t +be64dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4)); +} + +static INLINE uint16_t +le16dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return ((p[1] << 8) | p[0]); +} + +static INLINE uint32_t +le32dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((unsigned)p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +static INLINE uint64_t +le64dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p)); +} + +static INLINE void +be16enc(void *pp, uint16_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = (u >> 8) & 0xff; + p[1] = u & 0xff; +} + +static INLINE void +be32enc(void *pp, uint32_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = (u >> 24) & 0xff; + p[1] = (u >> 16) & 0xff; + p[2] = (u >> 8) & 0xff; + p[3] = u & 0xff; +} + +static INLINE void +be64enc(void *pp, uint64_t u) +{ + uint8_t *p = (uint8_t *)pp; + + be32enc(p, (uint32_t)(u >> 32)); + be32enc(p + 4, (uint32_t)(u & 0xffffffffU)); +} + +static INLINE void +le16enc(void *pp, uint16_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; +} + +static INLINE void +le32enc(void *pp, uint32_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; + p[3] = (u >> 24) & 0xff; +} + +static INLINE void +le64enc(void *pp, uint64_t u) +{ + uint8_t *p = (uint8_t *)pp; + + le32enc(p, (uint32_t)(u & 0xffffffffU)); + le32enc(p + 4, (uint32_t)(u >> 32)); +} + +#endif /* _ENDIAN_H_ */ diff --git a/components/wpa_supplicant/port/include/os.h b/components/wpa_supplicant/port/include/os.h new file mode 100644 index 0000000000..e6da894e92 --- /dev/null +++ b/components/wpa_supplicant/port/include/os.h @@ -0,0 +1,286 @@ +/* + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef OS_H +#define OS_H +#include "esp_types.h" +#include +#include +#include +#include "rom/ets_sys.h" +#include "lwip/mem.h" +typedef long os_time_t; + +/** + * os_sleep - Sleep (sec, usec) + * @sec: Number of seconds to sleep + * @usec: Number of microseconds to sleep + */ +void os_sleep(os_time_t sec, os_time_t usec); + +struct os_time { + os_time_t sec; + os_time_t usec; +}; + +/** + * os_get_time - Get current time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_time(struct os_time *t); + + +/* Helper macros for handling struct os_time */ + +#define os_time_before(a, b) \ + ((a)->sec < (b)->sec || \ + ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) + +#define os_time_sub(a, b, res) do { \ + (res)->sec = (a)->sec - (b)->sec; \ + (res)->usec = (a)->usec - (b)->usec; \ + if ((res)->usec < 0) { \ + (res)->sec--; \ + (res)->usec += 1000000; \ + } \ +} while (0) + +/** + * os_mktime - Convert broken-down time into seconds since 1970-01-01 + * @year: Four digit year + * @month: Month (1 .. 12) + * @day: Day of month (1 .. 31) + * @hour: Hour (0 .. 23) + * @min: Minute (0 .. 59) + * @sec: Second (0 .. 60) + * @t: Buffer for returning calendar time representation (seconds since + * 1970-01-01 00:00:00) + * Returns: 0 on success, -1 on failure + * + * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time + * which is used by POSIX mktime(). + */ +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t); + + +/** + * os_daemonize - Run in the background (detach from the controlling terminal) + * @pid_file: File name to write the process ID to or %NULL to skip this + * Returns: 0 on success, -1 on failure + */ +int os_daemonize(const char *pid_file); + +/** + * os_daemonize_terminate - Stop running in the background (remove pid file) + * @pid_file: File name to write the process ID to or %NULL to skip this + */ +void os_daemonize_terminate(const char *pid_file); + +/** + * os_get_random - Get cryptographically strong pseudo random data + * @buf: Buffer for pseudo random data + * @len: Length of the buffer + * Returns: 0 on success, -1 on failure + */ +int os_get_random(unsigned char *buf, size_t len); + +/** + * os_random - Get pseudo random value (not necessarily very strong) + * Returns: Pseudo random value + */ +unsigned long os_random(void); + +/** + * os_rel2abs_path - Get an absolute path for a file + * @rel_path: Relative path to a file + * Returns: Absolute path for the file or %NULL on failure + * + * This function tries to convert a relative path of a file to an absolute path + * in order for the file to be found even if current working directory has + * changed. The returned value is allocated and caller is responsible for + * freeing it. It is acceptable to just return the same path in an allocated + * buffer, e.g., return strdup(rel_path). This function is only used to find + * configuration files when os_daemonize() may have changed the current working + * directory and relative path would be pointing to a different location. + */ +char * os_rel2abs_path(const char *rel_path); + +/** + * os_program_init - Program initialization (called at start) + * Returns: 0 on success, -1 on failure + * + * This function is called when a programs starts. If there are any OS specific + * processing that is needed, it can be placed here. It is also acceptable to + * just return 0 if not special processing is needed. + */ +int os_program_init(void); + +/** + * os_program_deinit - Program deinitialization (called just before exit) + * + * This function is called just before a program exists. If there are any OS + * specific processing, e.g., freeing resourced allocated in os_program_init(), + * it should be done here. It is also acceptable for this function to do + * nothing. + */ +void os_program_deinit(void); + +/** + * os_setenv - Set environment variable + * @name: Name of the variable + * @value: Value to set to the variable + * @overwrite: Whether existing variable should be overwritten + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_setenv(const char *name, const char *value, int overwrite); + +/** + * os_unsetenv - Delete environent variable + * @name: Name of the variable + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_unsetenv(const char *name); + +/** + * os_readfile - Read a file to an allocated memory buffer + * @name: Name of the file to read + * @len: For returning the length of the allocated buffer + * Returns: Pointer to the allocated buffer or %NULL on failure + * + * This function allocates memory and reads the given file to this buffer. Both + * binary and text files can be read with this function. The caller is + * responsible for freeing the returned buffer with os_free(). + */ +char * os_readfile(const char *name, size_t *len); + +/* + * The following functions are wrapper for standard ANSI C or POSIX functions. + * By default, they are just defined to use the standard function name and no + * os_*.c implementation is needed for them. This avoids extra function calls + * by allowing the C pre-processor take care of the function name mapping. + * + * If the target system uses a C library that does not provide these functions, + * build_config.h can be used to define the wrappers to use a different + * function name. This can be done on function-by-function basis since the + * defines here are only used if build_config.h does not define the os_* name. + * If needed, os_*.c file can be used to implement the functions that are not + * included in the C library on the target system. Alternatively, + * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case + * these functions need to be implemented in os_*.c file for the target system. + */ + +#ifndef os_malloc +#define os_malloc(s) malloc((s)) +#endif +#ifndef os_realloc +#define os_realloc(p, s) realloc((p), (s)) +#endif +#ifndef os_zalloc +#define os_zalloc(s) calloc(1, (s)) +#endif +#ifndef os_free +#define os_free(p) free((p)) +#endif + + +#ifndef os_strdup +#ifdef _MSC_VER +#define os_strdup(s) _strdup(s) +#else +#define os_strdup(s) strdup(s) +#endif +#endif +char * ets_strdup(const char *s); + +#ifndef os_memcpy +#define os_memcpy(d, s, n) memcpy((d), (s), (n)) +#endif +#ifndef os_memmove +#define os_memmove(d, s, n) memmove((d), (s), (n)) +#endif +#ifndef os_memset +#define os_memset(s, c, n) memset(s, c, n) +#endif +#ifndef os_memcmp +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) +#endif + +#ifndef os_strlen +#define os_strlen(s) strlen(s) +#endif +#ifndef os_strcasecmp +#ifdef _MSC_VER +#define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) +#else +#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) +#endif +#endif +#ifndef os_strncasecmp +#ifdef _MSC_VER +#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) +#else +#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) +#endif +#endif +#ifndef os_strchr +#define os_strchr(s, c) strchr((s), (c)) +#endif +#ifndef os_strcmp +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#endif +#ifndef os_strncmp +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#endif +#ifndef os_strncpy +#define os_strncpy(d, s, n) strncpy((d), (s), (n)) +#endif +#ifndef os_strrchr +//hard cold +#define os_strrchr(s, c) NULL +#endif +#ifndef os_strstr +#define os_strstr(h, n) strstr((h), (n)) +#endif + +#ifndef os_snprintf +#ifdef _MSC_VER +#define os_snprintf _snprintf +#else +#define os_snprintf vsnprintf +#endif +#endif + +/** + * os_strlcpy - Copy a string with size bound and NUL-termination + * @dest: Destination + * @src: Source + * @siz: Size of the target buffer + * Returns: Total length of the target string (length of src) (not including + * NUL-termination) + * + * This function matches in behavior with the strlcpy(3) function in OpenBSD. + */ +size_t os_strlcpy(char *dest, const char *src, size_t siz); + + + +#endif /* OS_H */ diff --git a/components/wpa_supplicant/src/crypto/aes-cbc.c b/components/wpa_supplicant/src/crypto/aes-cbc.c new file mode 100644 index 0000000000..016207795e --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-cbc.c @@ -0,0 +1,88 @@ +/* + * AES-128 CBC + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" + +/** + * aes_128_cbc_encrypt - AES-128 CBC encryption + * @key: Encryption key + * @iv: Encryption IV for CBC mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int +aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + void *ctx; + u8 cbc[AES_BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + os_memcpy(cbc, iv, AES_BLOCK_SIZE); + + blocks = data_len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < AES_BLOCK_SIZE; j++) + cbc[j] ^= pos[j]; + aes_encrypt(ctx, cbc, cbc); + os_memcpy(pos, cbc, AES_BLOCK_SIZE); + pos += AES_BLOCK_SIZE; + } + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * aes_128_cbc_decrypt - AES-128 CBC decryption + * @key: Decryption key + * @iv: Decryption IV for CBC mode (16 bytes) + * @data: Data to decrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int +aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + void *ctx; + u8 cbc[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + ctx = aes_decrypt_init(key, 16); + if (ctx == NULL) + return -1; + os_memcpy(cbc, iv, AES_BLOCK_SIZE); + + blocks = data_len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, pos, AES_BLOCK_SIZE); + aes_decrypt(ctx, pos, pos); + for (j = 0; j < AES_BLOCK_SIZE; j++) + pos[j] ^= cbc[j]; + os_memcpy(cbc, tmp, AES_BLOCK_SIZE); + pos += AES_BLOCK_SIZE; + } + aes_decrypt_deinit(ctx); + return 0; +} diff --git a/components/wpa_supplicant/src/crypto/aes-internal-dec.c b/components/wpa_supplicant/src/crypto/aes-internal-dec.c new file mode 100644 index 0000000000..46371c5557 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-internal-dec.c @@ -0,0 +1,172 @@ +/* + * AES (Rijndael) cipher - decrypt + * + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * - use C pre-processor to make it easier to change S table access + * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at + * cost of reduced throughput (quite small difference on Pentium 4, + * 10-25% when using -O1 or -O2 optimization) + * + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/aes_i.h" + + + +//static unsigned char aes_priv_buf[AES_PRIV_SIZE]; + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits) +{ + int Nr, i, j; + u32 temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + if (Nr < 0) + return Nr; + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the + * first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + for (j = 0; j < 4; j++) { + rk[j] = TD0_(TE4((rk[j] >> 24) )) ^ + TD1_(TE4((rk[j] >> 16) & 0xff)) ^ + TD2_(TE4((rk[j] >> 8) & 0xff)) ^ + TD3_(TE4((rk[j] ) & 0xff)); + } + } + + return Nr; +} + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + int res; + rk = os_malloc(AES_PRIV_SIZE); + if (rk == NULL) + return NULL; + res = rijndaelKeySetupDec(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; + return rk; +} + +static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16], + u8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; + +#define ROUND(i,d,s) \ +d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ +d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ +d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ +d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] + +#ifdef FULL_UNROLL + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,t,s); + } + } + + rk += Nr << 2; + +#else /* !FULL_UNROLL */ + + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } + +#endif /* ?FULL_UNROLL */ + +#undef ROUND + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; + PUTU32(pt , s0); + s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; + PUTU32(pt + 4, s1); + s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; + PUTU32(pt + 8, s2); + s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; + PUTU32(pt + 12, s3); +} + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + u32 *rk = ctx; + rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + os_memset(ctx, 0, AES_PRIV_SIZE); + os_free(ctx); +} diff --git a/components/wpa_supplicant/src/crypto/aes-internal-enc.c b/components/wpa_supplicant/src/crypto/aes-internal-enc.c new file mode 100644 index 0000000000..7b1080c450 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-internal-enc.c @@ -0,0 +1,134 @@ +/* + * AES (Rijndael) cipher - encrypt + * + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * - use C pre-processor to make it easier to change S table access + * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at + * cost of reduced throughput (quite small difference on Pentium 4, + * 10-25% when using -O1 or -O2 optimization) + * + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/aes_i.h" + +#include "os.h" + +void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + s1 = GETU32(pt + 4) ^ rk[1]; + s2 = GETU32(pt + 8) ^ rk[2]; + s3 = GETU32(pt + 12) ^ rk[3]; + +#define ROUND(i,d,s) \ +d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ +d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ +d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ +d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] + +#ifdef FULL_UNROLL + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,t,s); + } + } + + rk += Nr << 2; + +#else /* !FULL_UNROLL */ + + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } + +#endif /* ?FULL_UNROLL */ + +#undef ROUND + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; + PUTU32(ct , s0); + s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; + PUTU32(ct + 4, s1); + s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; + PUTU32(ct + 8, s2); + s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; + PUTU32(ct + 12, s3); +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + int res; + rk = os_malloc(AES_PRIV_SIZE); + if (rk == NULL) + return NULL; + res = rijndaelKeySetupEnc(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; + return rk; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + u32 *rk = ctx; + rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + os_memset(ctx, 0, AES_PRIV_SIZE); + os_free(ctx); +} diff --git a/components/wpa_supplicant/src/crypto/aes-internal.c b/components/wpa_supplicant/src/crypto/aes-internal.c new file mode 100644 index 0000000000..9618239f93 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-internal.c @@ -0,0 +1,854 @@ +/* + * AES (Rijndael) cipher + * + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * - use C pre-processor to make it easier to change S table access + * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at + * cost of reduced throughput (quite small difference on Pentium 4, + * 10-25% when using -O1 or -O2 optimization) + * + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +//#include "wpa/common.h" +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/aes_i.h" + +/* + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define AES_SMALL_TABLES + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +const u32 Te0[256] /* ICACHE_RODATA_ATTR */ = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +#ifndef AES_SMALL_TABLES +const u32 Te1[256] /* ICACHE_RODATA_ATTR */ = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +const u32 Te2[256] /* ICACHE_RODATA_ATTR */ = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +const u32 Te3[256] /* ICACHE_RODATA_ATTR */ = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +const u32 Te4[256] /* ICACHE_RODATA_ATTR */ = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +#endif /* AES_SMALL_TABLES */ +const u32 Td0[256] /* ICACHE_RODATA_ATTR */ = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +#ifndef AES_SMALL_TABLES +const u32 Td1[256] /* ICACHE_RODATA_ATTR */ = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +const u32 Td2[256] /* ICACHE_RODATA_ATTR */ = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +const u32 Td3[256] /* ICACHE_RODATA_ATTR */ = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +const u32 Td4[256] /* ICACHE_RODATA_ATTR */ = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +const u32 rcon[] /* ICACHE_RODATA_ATTR */ = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; +#else /* AES_SMALL_TABLES */ +const u8 Td4s[256] /* ICACHE_RODATA_ATTR */ = { + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +const u8 rcons[] /* ICACHE_RODATA_ATTR */ = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; +#endif /* AES_SMALL_TABLES */ +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits) +{ + int i; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + + if (keyBits == 128) { + for (i = 0; i < 10; i++) { + temp = rk[3]; + rk[4] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + rk += 4; + } + return 10; + } + + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + + if (keyBits == 192) { + for (i = 0; i < 8; i++) { + temp = rk[5]; + rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (i == 7) + return 12; + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } + } + + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + + if (keyBits == 256) { + for (i = 0; i < 7; i++) { + temp = rk[7]; + rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (i == 6) + return 14; + temp = rk[11]; + rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^ + TE433(temp) ^ TE444(temp); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + rk += 8; + } + } + + return -1; +} diff --git a/components/wpa_supplicant/src/crypto/aes-unwrap.c b/components/wpa_supplicant/src/crypto/aes-unwrap.c new file mode 100644 index 0000000000..4a92f1cd31 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-unwrap.c @@ -0,0 +1,80 @@ +/* + * AES key unwrap (128-bit KEK, RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" + +/** + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits + * @plain: Plaintext key, n * 64 bits + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) + */ +int +aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) +{ + u8 a[8], *r, b[16]; + int i, j; + void *ctx; + + /* 1) Initialize variables. */ + os_memcpy(a, cipher, 8); + r = plain; + os_memcpy(r, cipher + 8, 8 * n); + + ctx = aes_decrypt_init(kek, 16); + if (ctx == NULL) + return -1; + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + os_memcpy(b, a, 8); + b[7] ^= n * j + i; + + os_memcpy(b + 8, r, 8); + aes_decrypt(ctx, b, b); + os_memcpy(a, b, 8); + os_memcpy(r, b + 8, 8); + r -= 8; + } + } + aes_decrypt_deinit(ctx); + + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. + */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) + return -1; + } + + return 0; +} diff --git a/components/wpa_supplicant/src/crypto/aes-wrap.c b/components/wpa_supplicant/src/crypto/aes-wrap.c new file mode 100644 index 0000000000..388dd97a82 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/aes-wrap.c @@ -0,0 +1,70 @@ +/* + * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" + +/** + * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: 16-octet Key encryption key (KEK) + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @plain: Plaintext key to be wrapped, n * 64 bits + * @cipher: Wrapped key, (n + 1) * 64 bits + * Returns: 0 on success, -1 on failure + */ +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) +{ + u8 *a, *r, b[16]; + int i, j; + void *ctx; + + a = cipher; + r = cipher + 8; + + /* 1) Initialize variables. */ + os_memset(a, 0xa6, 8); + os_memcpy(r, plain, 8 * n); + + ctx = aes_encrypt_init(kek, 16); + if (ctx == NULL) + return -1; + + /* 2) Calculate intermediate values. + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) + */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + os_memcpy(b, a, 8); + os_memcpy(b + 8, r, 8); + aes_encrypt(ctx, b, b); + os_memcpy(a, b, 8); + a[7] ^= n * j + i; + os_memcpy(r, b + 8, 8); + r += 8; + } + } + aes_encrypt_deinit(ctx); + + /* 3) Output the results. + * + * These are already in @cipher due to the location of temporary + * variables. + */ + + return 0; +} diff --git a/components/wpa_supplicant/src/crypto/bignum.c b/components/wpa_supplicant/src/crypto/bignum.c new file mode 100644 index 0000000000..7b8446c3ba --- /dev/null +++ b/components/wpa_supplicant/src/crypto/bignum.c @@ -0,0 +1,244 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" +#include "crypto/common.h" +#include "wpa/wpabuf.h" +#include "wpa/wpa_debug.h" +#include "bignum.h" + +#define CONFIG_INTERNAL_LIBTOMMATH +#ifdef CONFIG_INTERNAL_LIBTOMMATH +#include "libtommath.h" +#else /* CONFIG_INTERNAL_LIBTOMMATH */ +#include +#endif /* CONFIG_INTERNAL_LIBTOMMATH */ + + +/* + * The current version is just a wrapper for LibTomMath library, so + * struct bignum is just typecast to mp_int. + */ + +/** + * bignum_init - Allocate memory for bignum + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct bignum * +bignum_init(void) +{ + struct bignum *n = (struct bignum *)os_zalloc(sizeof(mp_int)); + if (n == NULL) + return NULL; + if (mp_init((mp_int *) n) != MP_OKAY) { + os_free(n); + n = NULL; + } + return n; +} + + +/** + * bignum_deinit - Free bignum + * @n: Bignum from bignum_init() + */ +void +bignum_deinit(struct bignum *n) +{ + if (n) { + mp_clear((mp_int *) n); + os_free(n); + } +} + + +/** + * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer + * @n: Bignum from bignum_init() + * Returns: Length of n if written to a binary buffer + */ +size_t +bignum_get_unsigned_bin_len(struct bignum *n) +{ + return mp_unsigned_bin_size((mp_int *) n); +} + + +/** + * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum + * @n: Bignum from bignum_init() + * @buf: Buffer for the binary number + * @len: Length of the buffer, can be %NULL if buffer is known to be long + * enough. Set to used buffer length on success if not %NULL. + * Returns: 0 on success, -1 on failure + */ +int +bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len) +{ + size_t need = mp_unsigned_bin_size((mp_int *) n); + if (len && need > *len) { + *len = need; + return -1; + } + if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + if (len) + *len = need; + return 0; +} + + +/** + * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer + * @n: Bignum from bignum_init(); to be set to the given value + * @buf: Buffer with unsigned binary value + * @len: Length of buf in octets + * Returns: 0 on success, -1 on failure + */ +int +bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len) +{ + if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_cmp - Signed comparison + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * Returns: 0 on success, -1 on failure + */ +int +bignum_cmp(const struct bignum *a, const struct bignum *b) +{ + return mp_cmp((mp_int *) a, (mp_int *) b); +} + + +/** + * bignum_cmd_d - Compare bignum to standard integer + * @a: Bignum from bignum_init() + * @b: Small integer + * Returns: 0 on success, -1 on failure + */ +int +bignum_cmp_d(const struct bignum *a, unsigned long b) +{ + return mp_cmp_d((mp_int *) a, b); +} + + +/** + * bignum_add - c = a + b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int +bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_sub - c = a - b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a - b + * Returns: 0 on success, -1 on failure + */ +int +bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_mul - c = a * b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a * b + * Returns: 0 on success, -1 on failure + */ +int +bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_mulmod - d = a * b (mod c) + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); modulus + * @d: Bignum from bignum_init(); used to store the result of a * b (mod c) + * Returns: 0 on success, -1 on failure + */ +int +bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d) +{ + if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) + != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_exptmod - Modular exponentiation: d = a^b (mod c) + * @a: Bignum from bignum_init(); base + * @b: Bignum from bignum_init(); exponent + * @c: Bignum from bignum_init(); modulus + * @d: Bignum from bignum_init(); used to store the result of a^b (mod c) + * Returns: 0 on success, -1 on failure + */ +int +bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d) +{ + if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) + != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} diff --git a/components/wpa_supplicant/src/crypto/bignum.h b/components/wpa_supplicant/src/crypto/bignum.h new file mode 100644 index 0000000000..f25e26783a --- /dev/null +++ b/components/wpa_supplicant/src/crypto/bignum.h @@ -0,0 +1,38 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BIGNUM_H +#define BIGNUM_H + +struct bignum; + +struct bignum * bignum_init(void); +void bignum_deinit(struct bignum *n); +size_t bignum_get_unsigned_bin_len(struct bignum *n); +int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len); +int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len); +int bignum_cmp(const struct bignum *a, const struct bignum *b); +int bignum_cmp_d(const struct bignum *a, unsigned long b); +int bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); +int bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); + +#endif /* BIGNUM_H */ diff --git a/components/wpa_supplicant/src/crypto/crypto_internal-cipher.c b/components/wpa_supplicant/src/crypto/crypto_internal-cipher.c new file mode 100644 index 0000000000..7d89795797 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_internal-cipher.c @@ -0,0 +1,268 @@ +/* + * Crypto wrapper for internal crypto implementation - Cipher wrappers + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +//#include "wpa/includes.h" + +//#include "wpa/common.h" +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/aes.h" +#if defined(CONFIG_DES) || defined(CONFIG_DES3) +#include "crypto/des_i.h" +#endif + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct { + size_t used_bytes; + u8 key[16]; + size_t keylen; + } rc4; + struct { + u8 cbc[32]; + void *ctx_enc; + void *ctx_dec; + } aes; +#ifdef CONFIG_DES3 + struct { + struct des3_key_s key; + u8 cbc[8]; + } des3; +#endif +#ifdef CONFIG_DES + struct { + u32 ek[32]; + u32 dk[32]; + u8 cbc[8]; + } des; +#endif + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = (struct crypto_cipher *)os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (key_len > sizeof(ctx->u.rc4.key)) { + os_free(ctx); + return NULL; + } + ctx->u.rc4.keylen = key_len; + os_memcpy(ctx->u.rc4.key, key, key_len); + break; + case CRYPTO_CIPHER_ALG_AES: + ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); + if (ctx->u.aes.ctx_enc == NULL) { + os_free(ctx); + return NULL; + } + ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len); + if (ctx->u.aes.ctx_dec == NULL) { + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + os_free(ctx); + return NULL; + } + os_memcpy(ctx->u.aes.cbc, iv, AES_BLOCK_SIZE); + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != 24) { + os_free(ctx); + return NULL; + } + des3_key_setup(key, &ctx->u.des3.key); + os_memcpy(ctx->u.des3.cbc, iv, 8); + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (key_len != 8) { + os_free(ctx); + return NULL; + } + des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk); + os_memcpy(ctx->u.des.cbc, iv, 8); + break; +#endif + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + size_t i, j, blocks; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(crypt, plain, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, crypt, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % AES_BLOCK_SIZE) + return -1; + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < AES_BLOCK_SIZE; j++) + ctx->u.aes.cbc[j] ^= plain[j]; + aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, + ctx->u.aes.cbc); + os_memcpy(crypt, ctx->u.aes.cbc, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; + } + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + ctx->u.des3.cbc[j] ^= plain[j]; + des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key, + ctx->u.des3.cbc); + os_memcpy(crypt, ctx->u.des3.cbc, 8); + plain += 8; + crypt += 8; + } + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + ctx->u.des3.cbc[j] ^= plain[j]; + des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek, + ctx->u.des.cbc); + os_memcpy(crypt, ctx->u.des.cbc, 8); + plain += 8; + crypt += 8; + } + break; +#endif + default: + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + size_t i, j, blocks; + u8 tmp[32]; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(plain, crypt, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, plain, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % AES_BLOCK_SIZE) + return -1; + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, AES_BLOCK_SIZE); + aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); + for (j = 0; j < AES_BLOCK_SIZE; j++) + plain[j] ^= ctx->u.aes.cbc[j]; + os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += AES_BLOCK_SIZE; + } + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des3_decrypt(crypt, &ctx->u.des3.key, plain); + for (j = 0; j < 8; j++) + plain[j] ^= ctx->u.des3.cbc[j]; + os_memcpy(ctx->u.des3.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; +#endif +#ifdef CONFIG_DES + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des_block_decrypt(crypt, ctx->u.des.dk, plain); + for (j = 0; j < 8; j++) + plain[j] ^= ctx->u.des.cbc[j]; + os_memcpy(ctx->u.des.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; +#endif + default: + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_AES: + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + aes_decrypt_deinit(ctx->u.aes.ctx_dec); + break; +#ifdef CONFIG_DES3 + case CRYPTO_CIPHER_ALG_3DES: + break; +#endif + default: + break; + } + os_free(ctx); +} diff --git a/components/wpa_supplicant/src/crypto/crypto_internal-modexp.c b/components/wpa_supplicant/src/crypto/crypto_internal-modexp.c new file mode 100644 index 0000000000..ea97857005 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_internal-modexp.c @@ -0,0 +1,56 @@ +/* + * Crypto wrapper for internal crypto implementation - modexp + * Copyright (c) 2006-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "bignum.h" +#include "crypto/crypto.h" + + +int +crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result; + int ret = -1; + + bn_base = bignum_init(); + bn_exp = bignum_init(); + bn_modulus = bignum_init(); + bn_result = bignum_init(); + + if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || + bn_result == NULL) + goto error; + + if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 || + bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 || + bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0) + goto error; + + if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0) + goto error; + + ret = bignum_get_unsigned_bin(bn_result, result, result_len); + +error: + bignum_deinit(bn_base); + bignum_deinit(bn_exp); + bignum_deinit(bn_modulus); + bignum_deinit(bn_result); + return ret; +} diff --git a/components/wpa_supplicant/src/crypto/crypto_internal-rsa.c b/components/wpa_supplicant/src/crypto/crypto_internal-rsa.c new file mode 100644 index 0000000000..19934f063b --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_internal-rsa.c @@ -0,0 +1,111 @@ +/* + * Crypto wrapper for internal crypto implementation - RSA parts + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "crypto/common.h" +#include "crypto/crypto.h" + +#include "wpa/includes.h" +#include "wpa/common.h" +#include "wpa/wpa_debug.h" + +#include "wpa2/tls/rsa.h" +#include "wpa2/tls/pkcs1.h" +#include "wpa2/tls/pkcs8.h" + +/* Dummy structures; these are just typecast to struct crypto_rsa_key */ +struct crypto_public_key; +struct crypto_private_key; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + return (struct crypto_public_key *) + crypto_rsa_import_public_key(key, len); +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd) +{ + struct crypto_private_key *res; + + /* First, check for possible PKCS #8 encoding */ + res = pkcs8_key_import(key, len); + if (res) + return res; + + if (passwd) { + /* Try to parse as encrypted PKCS #8 */ + res = pkcs8_enc_key_import(key, len, passwd); + if (res) + return res; + } + + /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */ + wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private " + "key"); + return (struct crypto_private_key *) + crypto_rsa_import_private_key(key, len); +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + /* No X.509 support in crypto_internal.c */ + return NULL; +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_encrypt(2, (struct crypto_rsa_key *) key, + 0, in, inlen, out, outlen); +} + + +int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key, + in, inlen, out, outlen); +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_encrypt(1, (struct crypto_rsa_key *) key, + 1, in, inlen, out, outlen); +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len) +{ + return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key, + crypt, crypt_len, plain, plain_len); +} diff --git a/components/wpa_supplicant/src/crypto/crypto_internal.c b/components/wpa_supplicant/src/crypto/crypto_internal.c new file mode 100644 index 0000000000..d8d59dfb9d --- /dev/null +++ b/components/wpa_supplicant/src/crypto/crypto_internal.c @@ -0,0 +1,280 @@ +/* + * Crypto wrapper for internal crypto implementation + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "crypto/includes.h" +#include "crypto/common.h" +//#include "wpa/common.h" +#include "crypto/crypto.h" +//#include "crypto/sha256_i.h" +#include "crypto/sha1_i.h" +#include "crypto/md5_i.h" + +#ifdef MEMLEAK_DEBUG +static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; +#endif + + +struct crypto_hash { + enum crypto_hash_alg alg; + union { + struct MD5Context md5; + struct SHA1Context sha1; +#ifdef CONFIG_SHA256 + struct sha256_state sha256; +#endif /* CONFIG_SHA256 */ + } u; + u8 key[64]; + size_t key_len; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + u8 k_pad[64]; + u8 tk[32]; + size_t i; + + ctx = (struct crypto_hash *)os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + MD5Init(&ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + SHA1Init(&ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + sha256_init(&ctx->u.sha256); + break; +#endif /* CONFIG_SHA256 */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (key_len > sizeof(k_pad)) { + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, key, key_len); + MD5Final(tk, &ctx->u.md5); + key = tk; + key_len = 16; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (key_len > sizeof(k_pad)) { + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, key, key_len); + SHA1Final(tk, &ctx->u.sha1); + key = tk; + key_len = 20; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (key_len > sizeof(k_pad)) { + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, key, key_len); + sha256_done(&ctx->u.sha256, tk); + key = tk; + key_len = 32; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + case CRYPTO_HASH_ALG_HMAC_MD5: + MD5Update(&ctx->u.md5, data, len); + break; + case CRYPTO_HASH_ALG_SHA1: + case CRYPTO_HASH_ALG_HMAC_SHA1: + SHA1Update(&ctx->u.sha1, data, len); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + case CRYPTO_HASH_ALG_HMAC_SHA256: + sha256_process(&ctx->u.sha256, data, len); + break; +#endif /* CONFIG_SHA256 */ + default: + break; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + u8 k_pad[64]; + size_t i; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + SHA1Final(mac, &ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + + MD5Final(mac, &ctx->u.md5); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + MD5Update(&ctx->u.md5, mac, 16); + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + + SHA1Final(mac, &ctx->u.sha1); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + SHA1Update(&ctx->u.sha1, mac, 20); + SHA1Final(mac, &ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + + sha256_done(&ctx->u.sha256, mac); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + sha256_process(&ctx->u.sha256, mac, 32); + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ + default: + os_free(ctx); + return -1; + } + + os_free(ctx); + + return 0; +} + + +int crypto_global_init(void) +{ + return 0; +} + + +void crypto_global_deinit(void) +{ +} diff --git a/components/wpa_supplicant/src/crypto/dh_group5.c b/components/wpa_supplicant/src/crypto/dh_group5.c new file mode 100644 index 0000000000..710f5c7d02 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/dh_group5.c @@ -0,0 +1,43 @@ +/* + * Diffie-Hellman group 5 operations + * Copyright (c) 2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/dh_groups.h" +#include "crypto/dh_group5.h" + + +void * +dh5_init(struct wpabuf **priv, struct wpabuf **publ) +{ + *publ = dh_init(dh_groups_get(5), priv); + if (*publ == 0) + return NULL; + return (void *) 1; +} + + +struct wpabuf * +dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private) +{ + return dh_derive_shared(peer_public, own_private, dh_groups_get(5)); +} + + +void +dh5_free(void *ctx) +{ +} diff --git a/components/wpa_supplicant/src/crypto/dh_groups.c b/components/wpa_supplicant/src/crypto/dh_groups.c new file mode 100644 index 0000000000..6ec9a36a90 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/dh_groups.c @@ -0,0 +1,641 @@ +/* + * Diffie-Hellman groups + * Copyright (c) 2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "crypto/dh_groups.h" +#include "wpa/wpabuf.h" +#include "wpa/wpa_debug.h" + +extern int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len); + +#ifdef ALL_DH_GROUPS + +/* RFC 4306, B.1. Group 1 - 768 Bit MODP + * Generator: 2 + * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } + */ +static const u8 dh_group1_generator[1] = { 0x02 }; +static const u8 dh_group1_prime[96] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 4306, B.2. Group 2 - 1024 Bit MODP + * Generator: 2 + * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } + */ +static const u8 dh_group2_generator[1] = { 0x02 }; +static const u8 dh_group2_prime[128] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +#endif /* ALL_DH_GROUPS */ + +/* RFC 3526, 2. Group 5 - 1536 Bit MODP + * Generator: 2 + * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } + */ +static const u8 dh_group5_generator[1] = { 0x02 }; +static const u8 dh_group5_prime[192] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +#ifdef ALL_DH_GROUPS + +/* RFC 3526, 3. Group 14 - 2048 Bit MODP + * Generator: 2 + * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } + */ +static const u8 dh_group14_generator[1] = { 0x02 }; +static const u8 dh_group14_prime[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 4. Group 15 - 3072 Bit MODP + * Generator: 2 + * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } + */ +static const u8 dh_group15_generator[1] = { 0x02 }; +static const u8 dh_group15_prime[384] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 5. Group 16 - 4096 Bit MODP + * Generator: 2 + * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } + */ +static const u8 dh_group16_generator[1] = { 0x02 }; +static const u8 dh_group16_prime[512] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 6. Group 17 - 6144 Bit MODP + * Generator: 2 + * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } + */ +static const u8 dh_group17_generator[1] = { 0x02 }; +static const u8 dh_group17_prime[768] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, + 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, + 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, + 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, + 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, + 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, + 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, + 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, + 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, + 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, + 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, + 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, + 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, + 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, + 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, + 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, + 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, + 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, + 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, + 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, + 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, + 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, + 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, + 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, + 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, + 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, + 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, + 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, + 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, + 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, + 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, + 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, + 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* RFC 3526, 7. Group 18 - 8192 Bit MODP + * Generator: 2 + * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } + */ +static const u8 dh_group18_generator[1] = { 0x02 }; +static const u8 dh_group18_prime[1024] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, + 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, + 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, + 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, + 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, + 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, + 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, + 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, + 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, + 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, + 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, + 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, + 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, + 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, + 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, + 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, + 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, + 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, + 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, + 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, + 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, + 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, + 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, + 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, + 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, + 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, + 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, + 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, + 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, + 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, + 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, + 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, + 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59, + 0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4, + 0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C, + 0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA, + 0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00, + 0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED, + 0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66, + 0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68, + 0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78, + 0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D, + 0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9, + 0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07, + 0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7, + 0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B, + 0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD, + 0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8, + 0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A, + 0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6, + 0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D, + 0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36, + 0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1, + 0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D, + 0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1, + 0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73, + 0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68, + 0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92, + 0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7, + 0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B, + 0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47, + 0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA, + 0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF, + 0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71, + 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +#endif /* ALL_DH_GROUPS */ + + +#define DH_GROUP(id) \ +{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ +dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) } + + +static struct dh_group dh_groups[] = { + DH_GROUP(5), +#ifdef ALL_DH_GROUPS + DH_GROUP(1), + DH_GROUP(2), + DH_GROUP(14), + DH_GROUP(15), + DH_GROUP(16), + DH_GROUP(17), + DH_GROUP(18) +#endif /* ALL_DH_GROUPS */ +}; + +#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0])) + + +const struct dh_group * +dh_groups_get(int id) +{ + size_t i; + + for (i = 0; i < NUM_DH_GROUPS; i++) { + if (dh_groups[i].id == id) + return &dh_groups[i]; + } + return NULL; +} + +/** + * dh_init - Initialize Diffie-Hellman handshake + * @dh: Selected Diffie-Hellman group + * @priv: Pointer for returning Diffie-Hellman private key + * Returns: Diffie-Hellman public value + */ +struct wpabuf * +dh_init(const struct dh_group *dh, struct wpabuf **priv) +{ + struct wpabuf *pv; + size_t pv_len; + + if (dh == NULL) + return NULL; + + wpabuf_free(*priv); + *priv = wpabuf_alloc(dh->prime_len); + if (*priv == NULL) + return NULL; + + if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) + { + wpabuf_free(*priv); + *priv = NULL; + return NULL; + } + + if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) { + /* Make sure private value is smaller than prime */ + *(wpabuf_mhead_u8(*priv)) = 0; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); + + pv_len = dh->prime_len; + pv = wpabuf_alloc(pv_len); + if (pv == NULL) + return NULL; + if (crypto_mod_exp(dh->generator, dh->generator_len, + wpabuf_head(*priv), wpabuf_len(*priv), + dh->prime, dh->prime_len, wpabuf_mhead(pv), + &pv_len) < 0) { + wpabuf_free(pv); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + wpabuf_put(pv, pv_len); + wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv); + + return pv; +} + + +/** + * dh_derive_shared - Derive shared Diffie-Hellman key + * @peer_public: Diffie-Hellman public value from peer + * @own_private: Diffie-Hellman private key from dh_init() + * @dh: Selected Diffie-Hellman group + * Returns: Diffie-Hellman shared key + */ +struct wpabuf * +dh_derive_shared(const struct wpabuf *peer_public, + const struct wpabuf *own_private, + const struct dh_group *dh) +{ + struct wpabuf *shared; + size_t shared_len; + + if (dh == NULL || peer_public == NULL || own_private == NULL) + return NULL; + + shared_len = dh->prime_len; + shared = wpabuf_alloc(shared_len); + if (shared == NULL) + return NULL; + if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), + wpabuf_head(own_private), wpabuf_len(own_private), + dh->prime, dh->prime_len, + wpabuf_mhead(shared), &shared_len) < 0) { + wpabuf_free(shared); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + wpabuf_put(shared, shared_len); + wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared); + + return shared; +} diff --git a/components/wpa_supplicant/src/crypto/libtommath.h b/components/wpa_supplicant/src/crypto/libtommath.h new file mode 100644 index 0000000000..31f9706593 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/libtommath.h @@ -0,0 +1,3440 @@ +/* + * Minimal code for RSA support from LibTomMath 0.41 + * http://libtom.org/ + * http://libtom.org/files/ltm-0.41.tar.bz2 + * This library was released in public domain by Tom St Denis. + * + * The combination in this file may not use all of the optimized algorithms + * from LibTomMath and may be considerable slower than the LibTomMath with its + * default settings. The main purpose of having this version here is to make it + * easier to build bignum.c wrapper without having to install and build an + * external library. + * + * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this + * libtommath.c file instead of using the external LibTomMath library. + */ +//#include "c_types.h" +#include "os.h" +#include "stdarg.h" + + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#define BN_MP_INVMOD_C +#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would + * require BN_MP_EXPTMOD_FAST_C instead */ +#define BN_S_MP_MUL_DIGS_C +#define BN_MP_INVMOD_SLOW_C +#define BN_S_MP_SQR_C +#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this + * would require other than mp_reduce */ + +#ifdef LTM_FAST + +/* Use faster div at the cost of about 1 kB */ +#define BN_MP_MUL_D_C + +/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MUL_2_C + +/* Include faster sqr at the cost of about 0.5 kB in code */ +#define BN_FAST_S_MP_SQR_C + +#else /* LTM_FAST */ + +#define BN_MP_DIV_SMALL +#define BN_MP_INIT_MULTI_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_ABS_C +#endif /* LTM_FAST */ + +/* Current uses do not require support for negative exponent in exptmod, so we + * can save about 1.5 kB in leaving out invmod. */ +#define LTM_NO_NEG_EXP + +/* from tommath.h */ + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#define OPT_CAST(x) (x *) + +typedef unsigned long mp_digit; +typedef u64 mp_word; + +#define DIGIT_BIT 28 +#define MP_28BIT + + +#define XMALLOC os_malloc +#define XFREE os_free +#define XREALLOC os_realloc + + +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) + +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +typedef int mp_err; + +/* define this to use lower memory usage routines (exptmods mostly) */ +#define MP_LOW_MEM + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + + +/* prototypes for copied functions */ +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +static int s_mp_sqr(mp_int * a, mp_int * b); +static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); + +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); + +#ifdef BN_MP_INIT_MULTI_C +static int mp_init_multi(mp_int *mp, ...); +#endif +#ifdef BN_MP_CLEAR_MULTI_C +static void mp_clear_multi(mp_int *mp, ...); +#endif +static int mp_lshd(mp_int * a, int b); +static void mp_set(mp_int * a, mp_digit b); +static void mp_clamp(mp_int * a); +static void mp_exch(mp_int * a, mp_int * b); +static void mp_rshd(mp_int * a, int b); +static void mp_zero(mp_int * a); +static int mp_mod_2d(mp_int * a, int b, mp_int * c); +static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); +static int mp_init_copy(mp_int * a, mp_int * b); +static int mp_mul_2d(mp_int * a, int b, mp_int * c); +#ifndef LTM_NO_NEG_EXP +static int mp_div_2(mp_int * a, mp_int * b); +static int mp_invmod(mp_int * a, mp_int * b, mp_int * c); +static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); +#endif /* LTM_NO_NEG_EXP */ +static int mp_copy(mp_int * a, mp_int * b); +static int mp_count_bits(mp_int * a); +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); +static int mp_mod(mp_int * a, mp_int * b, mp_int * c); +static int mp_grow(mp_int * a, int size); +static int mp_cmp_mag(mp_int * a, mp_int * b); +#ifdef BN_MP_ABS_C +static int mp_abs(mp_int * a, mp_int * b); +#endif +static int mp_sqr(mp_int * a, mp_int * b); +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); +static int mp_2expt(mp_int * a, int b); +static int mp_reduce_setup(mp_int * a, mp_int * b); +static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); +static int mp_init_size(mp_int * a, int size); +#ifdef BN_MP_EXPTMOD_FAST_C +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +#endif /* BN_MP_EXPTMOD_FAST_C */ +#ifdef BN_FAST_S_MP_SQR_C +static int fast_s_mp_sqr (mp_int * a, mp_int * b); +#endif /* BN_FAST_S_MP_SQR_C */ +#ifdef BN_MP_MUL_D_C +static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); +#endif /* BN_MP_MUL_D_C */ + + + +/* functions from bn_.c */ + + +/* reverse an array, used for radix code */ +static void +bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} + + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +static int +s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +static int +s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* init a new mp_int */ +static int +mp_init (mp_int * a) +{ + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} + + +/* clear one (frees) */ +static void +mp_clear (mp_int * a) +{ + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} + + +/* high level addition (handles signs) */ +static int +mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + + +/* high level subtraction (handles signs) */ +static int +mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + + +/* high level multiplication (handles sign) */ +static int +mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else +#endif +#ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else +#endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ +#ifdef BN_FAST_S_MP_MUL_DIGS_C + int digs = a->used + b->used + 1; + + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else +#endif +#ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else +#error mp_mul could fail + res = MP_VAL; +#endif + + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} + + +/* d = a * b (mod c) */ +static int +mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} + + +/* c = a mod b, 0 <= c < b */ +static int +mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted a lot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +static int +mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { +#ifdef LTM_NO_NEG_EXP + return MP_VAL; +#else /* LTM_NO_NEG_EXP */ +#ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; +#else +#error mp_exptmod would always fail + /* no invmod */ + return MP_VAL; +#endif +#endif /* LTM_NO_NEG_EXP */ + } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); +#else + /* default to no */ + dr = 0; +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } +#endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y, 0); +#else +#error mp_exptmod could fail + /* no exptmod for evens */ + return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C + } +#endif +} + + +/* compare two ints (signed)*/ +static int +mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} + + +/* compare a digit */ +static int +mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int +mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + +#ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); +#endif + +#ifndef BN_FAST_MP_INVMOD_C +#ifndef BN_MP_INVMOD_SLOW_C +#error mp_invmod would always fail +#endif +#endif + return MP_VAL; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* get the size for an unsigned equivalent */ +static int +mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int +mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&A, 1); + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* compare maginitude of two ints (unsigned) */ +static int +mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} + + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +static int +mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} + + +/* store in unsigned [big endian] format */ +static int +mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} + + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +static int +mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} + + +static int +mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + return mp_copy (b, a); +} + + +/* set to zero */ +static void +mp_zero (mp_int * a) +{ + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} + + +/* copy, b = a */ +static int +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} + + +/* shift right a certain amount of digits */ +static void +mp_rshd (mp_int * a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero (a); + return; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} + + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +static void +mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} + + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +static void +mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} + + +/* grow as required */ +static int +mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} + + +#ifdef BN_MP_ABS_C +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +static int +mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + + +/* set to a digit */ +static void +mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} + + +#ifndef LTM_NO_NEG_EXP +/* b = a/2 */ +static int +mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* shift left by a certain bit count */ +static int +mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_INIT_MULTI_C +static int +mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} +#endif + + +#ifdef BN_MP_CLEAR_MULTI_C +static void +mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} +#endif + + +/* shift left a certain amount of digits */ +static int +mp_lshd (mp_int * a, int b) +{ + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} + + +/* returns the number of bits in an int */ +static int +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} + + +/* calc a value mod 2**b */ +static int +mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +static int +mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + +#else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. +*/ +static int +mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = x.used == 0 ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_exch (&x, d); + } + + res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); + return res; +} + +#endif + + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +static int +s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + int (*redux)(mp_int*,mp_int*,mp_int*); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} + + +/* computes b = a*a */ +static int +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + +#ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else +#endif + { +#ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else +#endif +#ifdef BN_S_MP_SQR_C + res = s_mp_sqr (a, b); +#else +#error mp_sqr could fail + res = MP_VAL; +#endif + } + b->sign = MP_ZPOS; + return res; +} + + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +static int +mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + + +/* determines the setup value */ +static int +mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} + + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +static int +mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accommodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} + + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +static int +mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div (a, b, a, NULL); +} + + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +static int +mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#else + { +#error mp_reduce would always fail + res = MP_VAL; + goto CLEANUP; + } +#endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + if ((res = mp_add (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} + + +/* multiplies |a| * |b| and only computes up to digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +static int +s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + if ((res = mp_init_size (&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} + + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +static int +fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + register mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < pa+1; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +/* init an mp_init for a given size */ +static int +mp_init_size (mp_int * a, int size) +{ + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} + + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +static int +s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*ix]) + + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + (2*ix + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) *tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} + + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +static int +s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } +#endif + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} + + +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* setups the montgomery reduction stuff */ +static int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + + +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int +fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + register mp_digit mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + register int iy; + register mp_digit *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_2_C +/* b = a*2 */ +static int +mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accommodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally up to just under + * the leading bit of b. This saves a lot of multiple precision shifting. + */ +static int +mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_EXPTMOD_FAST_C +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +static int +mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto LBL_M; + } +#else + err = MP_VAL; + goto LBL_M; +#endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else +#endif + { +#ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto LBL_RES; + } +#else + err = MP_VAL; + goto LBL_RES; +#endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + + +#ifdef BN_FAST_S_MP_SQR_C +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +static int +fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow (b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, (ty-tx+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix&1) == 0) { + _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used+a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp (b); + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_D_C +/* multiply by a digit */ +static int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif diff --git a/components/wpa_supplicant/src/crypto/md5-internal.c b/components/wpa_supplicant/src/crypto/md5-internal.c new file mode 100644 index 0000000000..a430e297a5 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/md5-internal.c @@ -0,0 +1,298 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/md5.h" +#include "crypto/md5_i.h" +#include "crypto/crypto.h" + + +static void MD5Transform(u32 buf[4], u32 const in[16]); + + +typedef struct MD5Context MD5_CTX; + + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int +md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + size_t i; + + MD5Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5Update(&ctx, addr[i], len[i]); + MD5Final(mac, &ctx); + return 0; +} + + +/* ===== start - public domain MD5 implementation ===== */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + os_memcpy(p, buf, len); + return; + } + os_memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + os_memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + os_memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + os_memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + os_memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + os_memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + os_memcpy(digest, ctx->buf, 16); + os_memset(ctx, 0, sizeof(struct MD5Context)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +MD5Transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +/* ===== end - public domain MD5 implementation ===== */ diff --git a/components/wpa_supplicant/src/crypto/md5.c b/components/wpa_supplicant/src/crypto/md5.c new file mode 100644 index 0000000000..3125c98311 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/md5.c @@ -0,0 +1,113 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" + + +/** + * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int +hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + u8 k_pad[64]; /* padding - key XORd with ipad/opad */ + u8 tk[16]; + const u8 *_addr[6]; + size_t i, _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 64 bytes reset it to key = MD5(key) */ + if (key_len > 64) { + if (md5_vector(1, &key, &key_len, tk)) + return -1; + key = tk; + key_len = 16; + } + + /* the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (md5_vector(1 + num_elem, _addr, _len, mac)) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = MD5_MAC_LEN; + return md5_vector(2, _addr, _len, mac); +} + + +/** + * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int +hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/components/wpa_supplicant/src/crypto/rc4.c b/components/wpa_supplicant/src/crypto/rc4.c new file mode 100644 index 0000000000..678632297f --- /dev/null +++ b/components/wpa_supplicant/src/crypto/rc4.c @@ -0,0 +1,61 @@ +/* + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/crypto.h" + +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + +int +rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + u32 i, j, k; + u8 S[256], *pos; + size_t kpos; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= keylen) + kpos = 0; + S_SWAP(i, j); + } + + /* Skip the start of the stream */ + i = j = 0; + for (k = 0; k < skip; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data */ + pos = data; + for (k = 0; k < data_len; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + + return 0; +} diff --git a/components/wpa_supplicant/src/crypto/sha1-internal.c b/components/wpa_supplicant/src/crypto/sha1-internal.c new file mode 100644 index 0000000000..a1c255e416 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/sha1-internal.c @@ -0,0 +1,313 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/sha1.h" +#include "crypto/sha1_i.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" + +typedef struct SHA1Context SHA1_CTX; + +void SHA1Transform(u32 state[5], const unsigned char buffer[64]); + + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int +sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + SHA1_CTX ctx; + size_t i; + + SHA1Init(&ctx); + for (i = 0; i < num_elem; i++) + SHA1Update(&ctx, addr[i], len[i]); + SHA1Final(mac, &ctx); + return 0; +} + + +/* ===== start - public domain SHA1 implementation ===== */ + +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 4/01 +By Jouni Malinen +Minor changes to match the coding style used in Dynamics. + +Modified September 24, 2004 +By Jouni Malinen +Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. + +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#define SHA1HANDSOFF + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifndef WORDS_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ + (rol(block->l[i], 8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ + block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); +#define R3(v,w,x,y,z,i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w=rol(w, 30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg) +{ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void +SHA1Transform(u32 state[5], const unsigned char buffer[64]) +{ + u32 a, b, c, d, e; + typedef union { + unsigned char c[64]; + u32 l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; +#ifdef SHA1HANDSOFF + CHAR64LONG16 workspace; + block = &workspace; + os_memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *) buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + os_memset(block, 0, 64); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void +SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void +SHA1Update(SHA1_CTX* context, const void *_data, u32 len) +{ + u32 i, j; + const unsigned char *data = _data; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + os_memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + os_memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ + +void +SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ + u32 i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char) + ((context->count[(i >= 4 ? 0 : 1)] >> + ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *) "\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *) "\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() + */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & + 255); + } + /* Wipe variables */ + i = 0; + os_memset(context->buffer, 0, 64); + os_memset(context->state, 0, 20); + os_memset(context->count, 0, 8); + os_memset(finalcount, 0, 8); +} + +/* ===== end - public domain SHA1 implementation ===== */ diff --git a/components/wpa_supplicant/src/crypto/sha1-pbkdf2.c b/components/wpa_supplicant/src/crypto/sha1-pbkdf2.c new file mode 100644 index 0000000000..915a23aa78 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/sha1-pbkdf2.c @@ -0,0 +1,101 @@ +/* + * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" +#include "crypto/common.h" +#include "crypto/sha1.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" + +static int +pbkdf2_sha1_f(const char *passphrase, const char *ssid, + size_t ssid_len, int iterations, unsigned int count, + u8 *digest) +{ + unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; + int i, j; + unsigned char count_buf[4]; + const u8 *addr[2]; + size_t len[2]; + size_t passphrase_len = os_strlen(passphrase); + + addr[0] = (u8 *) ssid; + len[0] = ssid_len; + addr[1] = count_buf; + len[1] = 4; + + /* F(P, S, c, i) = U1 xor U2 xor ... Uc + * U1 = PRF(P, S || i) + * U2 = PRF(P, U1) + * Uc = PRF(P, Uc-1) + */ + + count_buf[0] = (count >> 24) & 0xff; + count_buf[1] = (count >> 16) & 0xff; + count_buf[2] = (count >> 8) & 0xff; + count_buf[3] = count & 0xff; + if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, + tmp)) + return -1; + os_memcpy(digest, tmp, SHA1_MAC_LEN); + + for (i = 1; i < iterations; i++) { + if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp, + SHA1_MAC_LEN, tmp2)) + return -1; + os_memcpy(tmp, tmp2, SHA1_MAC_LEN); + for (j = 0; j < SHA1_MAC_LEN; j++) + digest[j] ^= tmp2[j]; + } + + return 0; +} + + +/** + * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * @passphrase: ASCII passphrase + * @ssid: SSID + * @ssid_len: SSID length in bytes + * @iterations: Number of iterations to run + * @buf: Buffer for the generated key + * @buflen: Length of the buffer in bytes + * Returns: 0 on success, -1 of failure + * + * This function is used to derive PSK for WPA-PSK. For this protocol, + * iterations is set to 4096 and buflen to 32. This function is described in + * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. + */ +int +pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + unsigned int count = 0; + unsigned char *pos = buf; + size_t left = buflen, plen; + unsigned char digest[SHA1_MAC_LEN]; + + while (left > 0) { + count++; + if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, + count, digest)) + return -1; + plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; + os_memcpy(pos, digest, plen); + pos += plen; + left -= plen; + } + + return 0; +} diff --git a/components/wpa_supplicant/src/crypto/sha1.c b/components/wpa_supplicant/src/crypto/sha1.c new file mode 100644 index 0000000000..3d6da417ac --- /dev/null +++ b/components/wpa_supplicant/src/crypto/sha1.c @@ -0,0 +1,166 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/sha1.h" +#include "crypto/crypto.h" + + +/** + * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 on failure + */ +int +hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ + unsigned char tk[20]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 64 bytes reset it to key = SHA1(key) */ + if (key_len > 64) { + if (sha1_vector(1, &key, &key_len, tk)) + return -1; + key = tk; + key_len = 20; + } + + /* the HMAC_SHA1 transform looks like: + * + * SHA1(K XOR opad, SHA1(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA1 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha1_vector(1 + num_elem, _addr, _len, mac)) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA1 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA1_MAC_LEN; + return sha1_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 of failure + */ +int +hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 of failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ +int +sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u8 counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = (u8 *) label; + len[0] = label_len; + addr[1] = data; + len[1] = data_len; + addr[2] = &counter; + len[2] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + &buf[pos])) + return -1; + pos += SHA1_MAC_LEN; + } else { + if (hmac_sha1_vector(key, key_len, 3, addr, len, + hash)) + return -1; + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } + + return 0; +} + diff --git a/components/wpa_supplicant/src/crypto/sha256-internal.c b/components/wpa_supplicant/src/crypto/sha256-internal.c new file mode 100644 index 0000000000..9a1fca1c17 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/sha256-internal.c @@ -0,0 +1,249 @@ +/* + * SHA-256 hash implementation and interface functions + * Copyright (c) 2003-2011, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" + +#define SHA256_BLOCK_SIZE 64 + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +static void sha256_init(struct sha256_state *md); +static int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +static int sha256_done(struct sha256_state *md, unsigned char *out); + + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int +sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha256_state ctx; + size_t i; + + sha256_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha256_process(&ctx, addr[i], len[i])) + return -1; + if (sha256_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA256 implementation ===== */ + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +/* the K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + + +/* Various logical functions */ +#define RORc(x, y) \ +( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/* compress 512-bits */ +static int +sha256_compress(struct sha256_state *md, unsigned char *buf) +{ + u32 S[8], W[64], t0, t1; + u32 t; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE32(buf + (4 * i)); + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} + + +/* Initialize the hash state */ +static void +sha256_init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +static int +sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) { + if (sha256_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += SHA256_BLOCK_SIZE * 8; + in += SHA256_BLOCK_SIZE; + inlen -= SHA256_BLOCK_SIZE; + } else { + n = MIN(inlen, (SHA256_BLOCK_SIZE - md->curlen)); + os_memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == SHA256_BLOCK_SIZE) { + if (sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * SHA256_BLOCK_SIZE; + md->curlen = 0; + } + } + } + + return 0; +} + + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +static int +sha256_done(struct sha256_state *md, unsigned char *out) +{ + int i; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + /* increase the length of the message */ + md->length += md->curlen * 8; + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < SHA256_BLOCK_SIZE) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + + /* pad up to 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE32(out + (4 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA256 implementation ===== */ diff --git a/components/wpa_supplicant/src/crypto/sha256.c b/components/wpa_supplicant/src/crypto/sha256.c new file mode 100644 index 0000000000..f62cc11827 --- /dev/null +++ b/components/wpa_supplicant/src/crypto/sha256.c @@ -0,0 +1,160 @@ +/* + * SHA-256 hash implementation and interface functions + * Copyright (c) 2003-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "crypto/includes.h" + +#include "crypto/common.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" + + +/** + * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (32 bytes) + */ +void +hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ + unsigned char tk[32]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } + + /* if key is longer than 64 bytes reset it to key = SHA256(key) */ + if (key_len > 64) { + sha256_vector(1, &key, &key_len, tk); + key = tk; + key_len = 32; + } + + /* the HMAC_SHA256 transform looks like: + * + * SHA256(K XOR opad, SHA256(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA256 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + sha256_vector(1 + num_elem, _addr, _len, mac); + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA256 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA256_MAC_LEN; + sha256_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + */ +void +hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + + +/** + * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void +sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len * 8); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA256_MAC_LEN) { + hmac_sha256_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA256_MAC_LEN; + } else { + hmac_sha256_vector(key, key_len, 4, addr, len, hash); + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} From 8089afd45315cc8e114668d4132ca6a532972dcb Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 4 Nov 2016 15:52:45 +0800 Subject: [PATCH 130/285] build fix --- components/wpa_supplicant/component.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk index 6e5d7e0600..cb6b06652f 100644 --- a/components/wpa_supplicant/component.mk +++ b/components/wpa_supplicant/component.mk @@ -1,6 +1,6 @@ COMPONENT_ADD_INCLUDEDIRS := include port/include COMPONENT_SRCDIRS := src/crypto -CFLAGS += -DEMBEDDED_SUPP -D__ets__ +CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing include $(IDF_PATH)/make/component_common.mk From b981b195bebce18a197a79d559b1cdad43b21bfb Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:04:35 +0100 Subject: [PATCH 131/285] Fixed broken links --- CONTRIBUTING.rst | 3 ++- docs/contributor-agreement.rst | 2 +- examples/README.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 3bf43f6dbe..968826790a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -39,6 +39,7 @@ If this process passes, it will be merged onto the public github repository. Legal Part ---------- -Before a contribution can be accepted, you will need to sign our `Contributor Agreement `_. You will be prompted for this automatically as part of the Pull Request process. +Before a contribution can be accepted, you will need to sign our :doc:`contributor-agreement`. You will be prompted for this automatically as part of the Pull Request process. + diff --git a/docs/contributor-agreement.rst b/docs/contributor-agreement.rst index de294740c4..a7919da8f8 100644 --- a/docs/contributor-agreement.rst +++ b/docs/contributor-agreement.rst @@ -13,7 +13,7 @@ Framework (esp-idf) ("We" or "Us"). The purpose of this contributor agreement ("Agreement") is to clarify and document the rights granted by contributors to Us. To make this document effective, please follow the instructions at -https://github.com/espressif/esp-idf/blob/master/CONTRIBUTING.md. +https://github.com/espressif/esp-idf/blob/master/CONTRIBUTING.rst. 1. DEFINITIONS ~~~~~~~~~~~~~~ diff --git a/examples/README.md b/examples/README.md index 2cf66b9d07..e4422e91bf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -26,7 +26,7 @@ If you're looking for a more bare-bones project to start from, try [esp-idf-temp If you have a new example you think we'd like, please consider sending it to us as a Pull Request. -Please read the esp-idf CONTRIBUTING.md file which lays out general contribution rules. +Please read the esp-idf CONTRIBUTING.rst file which lays out general contribution rules. In addition, here are some tips for creating good examples: From 9c2ab4559dea66f3df1eff976cf28d06ba75b602 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:06:55 +0100 Subject: [PATCH 132/285] Fixed section header style Please follow https://docs.python.org/devguide/documenting.html#sections --- docs/deep-sleep-stub.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/deep-sleep-stub.rst b/docs/deep-sleep-stub.rst index 983f8bbf26..7711b1ce23 100644 --- a/docs/deep-sleep-stub.rst +++ b/docs/deep-sleep-stub.rst @@ -1,12 +1,12 @@ Deep Sleep Wake Stubs ---------------------- +===================== ESP32 supports running a "deep sleep wake stub" when coming out of deep sleep. This function runs immediately as soon as the chip wakes up - before any normal initialisation, bootloader, or ESP-IDF code has run. After the wake stub runs, the SoC can go back to sleep or continue to start ESP-IDF normally. Deep sleep wake stub code is loaded into "RTC Fast Memory" and any data which it uses must also be loaded into RTC memory. RTC memory regions hold their contents during deep sleep. Rules for Wake Stubs -==================== +-------------------- Wake stub code must be carefully written: @@ -23,9 +23,9 @@ Wake stub code must be carefully written: * Wake stub code is a part of the main esp-idf app. During normal running of esp-idf, functions can call the wake stub functions or access RTC memory. It is as if these were regular parts of the app. Implementing A Stub -=================== +------------------- -The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()` then this will override the default. +The wake stub in esp-idf is called ``esp_wake_deep_sleep()``. This function runs whenever the SoC wakes from deep sleep. There is a default version of this function provided in esp-idf, but the default function is weak-linked so if your app contains a function named ``esp_wake_deep_sleep()`` then this will override the default. If supplying a custom wake stub, the first thing it does should be to call ``esp_default_wake_deep_sleep()``. @@ -36,7 +36,7 @@ If you want to swap between different deep sleep stubs at runtime, it is also po All of these functions are declared in the ``esp_deepsleep.h`` header under components/esp32. Loading Code Into RTC Memory -============================ +---------------------------- Wake stub code must be resident in RTC Fast Memory. This can be done in one of two ways. @@ -53,7 +53,7 @@ The first way is simpler for very short and simple code, or for source files whe Loading Data Into RTC Memory -============================ +---------------------------- Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP. From adca98348ea3ffb110764e5fc1801ace643ce700 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:11:40 +0100 Subject: [PATCH 133/285] Resolved issue with version / release on Read the Docs Read the Docs is building documentation referencing to specific releases on GitHub. Changing version / release in this script is breaking menu in bottom left corner Now version / release should change only for local builds and not for builds on Read the Docs --- docs/conf.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8a4a275c8a..494ab3cf7f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -75,13 +75,23 @@ copyright = u'2016, Espressif' # |version| and |release|, also used in various other places throughout the # built documents. # -# This is supposed to be "the short X.Y version", but it's the only version -# visible when you open index.html. -# Display full version to make things less confusing. -# If needed, nearest tag is returned by 'git describe --abbrev=0'. -version = run_cmd_get_output('git describe') -# The full version, including alpha/beta/rc tags. -release = run_cmd_get_output('git describe') + +# Different setup depending if script is running on ReadTheDocs or elsewhere +on_rtd = os.environ.get('READTHEDOCS') == 'True' +if on_rtd: + # The short X.Y version. + # Apparently ReadTheDocs is getting confused by other version / release + # ReadTheDocs is building specific or the latest release from GitHub. + version = '1.0' + release = '1.0' +else: + # This is supposed to be "the short X.Y version", but it's the only version + # visible when you open index.html. + # Display full version to make things less confusing. + # If needed, nearest tag is returned by 'git describe --abbrev=0'. + version = run_cmd_get_output('git describe') + # The full version, including alpha/beta/rc tags. + release = run_cmd_get_output('git describe') # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 7bcd68fb30c4b9d73b66aafcea005eebb618e133 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:13:44 +0100 Subject: [PATCH 134/285] deep-sleep-stub added + TOC outline TOC outline to show overal planned structure --- docs/index.rst | 66 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index c973950615..42be69ee0c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,7 +5,6 @@ ESP32 Programming Guide Until ESP-IDF release 1.0, this documentation is a draft. It is incomplete and may have mistakes. Please mind your step! - Contents: .. toctree:: @@ -16,8 +15,6 @@ Contents: Linux Mac OS -.. Configure - TBA - .. Connect - TBA .. toctree:: @@ -35,16 +32,74 @@ Contents: build_system openocd +.. API Reference + .. + Table of Contents Outline + .. + 1. System - TBA + 1.1. Fundamentals of multiprocessor programming with FreeRTOS - TBA + 1.2. Application startup flow - TBA + 1.3. Flash encryption and secure boot: how they work and APIs - TBA + 1.4. Lower Power Coprocessor - TBA + 1.5. Watchdogs + 1.6. ... + 2. Memeory - TBA + 2.1. Memory layout of the application (IRAM/IROM, limitations of each) - TBA + 2.2. Flash layout and partitions - TBA + 2.3. Flash access APIs - TBA + 2.4. Partition APIs - TBA + 2.5. OTA mechanism (app partitions, OTA partition) and APIs - TBA + 2.6. ... + 3. Wi-Fi + 4. Bluetooth + 4.1. BT Classic - TBA + 4.2. BLE + 5. Ethernet - TBA + 6. Interfaces + 6.1. GPIO + 6.2. ADC - TBA + 6.3. DAC - TBA + 6.4. UART - TBA + 6.5. I2C - TBA + 6.6. I2S - TBA + 6.7. SPI - TBA + 6.8. CAN - TBA + 6.9. SD Controller - TBA + 6.10. Infrared - TBA + 6.11. Pulse Counter - TBA + 6.12. PWM - TBA + 6.13. LED PWM + 6.14. ... + 7. Sensors - TBA + 7.1. Hall Sensor - TBA + 7.2. Temperature Sensor - TBA + 7.3. Touch Sensor - TBA + 8. Protocols - TBA + 9. Components + 9.1. Logging + 9.2 Non-Volatile Storage + 9.3 Virtual Filesystem + 9.3. Http sever - TBA + 10. Applications - TBA + .. + API Dcoumentation Teamplate + .. + .. toctree:: :caption: API Reference :maxdepth: 1 Wi-Fi Bluetooth + GPIO + LED Control + Logging - Non-volatile storage - Virtual filesystem + Non-Volatile Storage + Virtual Filesystem + deep-sleep-stub + Template .. toctree:: @@ -76,3 +131,4 @@ Indices * :ref:`genindex` * :ref:`search` + From 3eaf1ec907ff407a098586bca80270b9dd938b02 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:18:25 +0100 Subject: [PATCH 135/285] All API Reference docs updated to match header files 1. Wi-Fi 2. Bluetooth 2. GPIO 4. LED Control 5. Logging 6. Non-Volatile Storage 7. Virtual Filesystem --- docs/Doxyfile | 2 +- docs/api/bt.rst | 16 +++-- docs/api/esp_wifi.rst | 33 +++++---- docs/api/gpio.rst | 159 ++++++++++++++++++++++++++--------------- docs/api/ledc.rst | 61 ++++++++++++++++ docs/api/log.rst | 35 +++++++++ docs/api/nvs_flash.rst | 67 +++++++++++++++++ docs/api/vfs.rst | 30 +++++--- 8 files changed, 312 insertions(+), 91 deletions(-) create mode 100644 docs/api/ledc.rst create mode 100644 docs/api/nvs_flash.rst diff --git a/docs/Doxyfile b/docs/Doxyfile index 9c53aff1f3..6ff4c45860 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,6 +1,6 @@ PROJECT_NAME = "ESP32 Programming Guide" -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/esp32/include/rom/gpio.h ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include WARN_NO_PARAMDOC = YES diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 72ab9fbd12..7cbbb9158b 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -1,20 +1,22 @@ -Bluetooth API -============= +Bluetooth +========= Overview -------- -`Instructions `_ +`Instructions`_ Application Example ------------------- -`Instructions `_ +`Instructions`_ -Reference ---------- +API Reference +------------- -`Instructions `_ +`Instructions`_ + +.. _Instructions: template.html Type Definitions ^^^^^^^^^^^^^^^^ diff --git a/docs/api/esp_wifi.rst b/docs/api/esp_wifi.rst index e4ec59fc82..e417e18ca0 100644 --- a/docs/api/esp_wifi.rst +++ b/docs/api/esp_wifi.rst @@ -1,20 +1,22 @@ -Wi-Fi API -========= +Wi-Fi +===== Overview -------- -`Instructions `_ +`Instructions`_ Application Example ------------------- -`Instructions `_ +`Instructions`_ -Reference ---------- +API Reference +------------- -`Instructions `_ +`Instructions`_ + +.. _Instructions: template.html Macros ------ @@ -22,14 +24,13 @@ Macros .. doxygendefine:: WIFI_INIT_CONFIG_DEFAULT -Typedefs --------- +Type Definitions +---------------- .. doxygentypedef:: wifi_promiscuous_cb_t .. doxygentypedef:: wifi_rxcb_t .. doxygentypedef:: esp_vendor_ie_cb_t - Functions --------- @@ -42,11 +43,12 @@ Functions .. doxygenfunction:: esp_wifi_connect .. doxygenfunction:: esp_wifi_disconnect .. doxygenfunction:: esp_wifi_clear_fast_connect -.. doxygenfunction:: esp_wifi_kick_station +.. doxygenfunction:: esp_wifi_deauth_sta .. doxygenfunction:: esp_wifi_scan_start .. doxygenfunction:: esp_wifi_scan_stop -.. doxygenfunction:: esp_wifi_get_ap_num -.. doxygenfunction:: esp_wifi_get_ap_list +.. doxygenfunction:: esp_wifi_scan_get_ap_num +.. doxygenfunction:: esp_wifi_scan_get_ap_records +.. doxygenfunction:: esp_wifi_sta_get_ap_info .. doxygenfunction:: esp_wifi_set_ps .. doxygenfunction:: esp_wifi_get_ps .. doxygenfunction:: esp_wifi_set_protocol @@ -64,11 +66,12 @@ Functions .. doxygenfunction:: esp_wifi_get_promiscuous .. doxygenfunction:: esp_wifi_set_config .. doxygenfunction:: esp_wifi_get_config -.. doxygenfunction:: esp_wifi_get_station_list -.. doxygenfunction:: esp_wifi_free_station_list +.. doxygenfunction:: esp_wifi_ap_get_sta_list .. doxygenfunction:: esp_wifi_set_storage .. doxygenfunction:: esp_wifi_reg_rxcb .. doxygenfunction:: esp_wifi_set_auto_connect .. doxygenfunction:: esp_wifi_get_auto_connect .. doxygenfunction:: esp_wifi_set_vendor_ie .. doxygenfunction:: esp_wifi_set_vendor_ie_cb + + diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 3c5c122921..72ba3e82fb 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -1,26 +1,120 @@ -GPIO API -======== +GPIO +==== Overview -------- -`Instructions `_ +`Instructions`_ Application Example ------------------- -`Instructions `_ +`Instructions`_ -Reference ---------- +API Reference +------------- -`Instructions `_ +`Instructions`_ + +.. _Instructions: template.html + +Macros +------ + +.. doxygendefine:: GPIO_SEL_0 +.. doxygendefine:: GPIO_SEL_1 +.. doxygendefine:: GPIO_SEL_2 +.. doxygendefine:: GPIO_SEL_3 +.. doxygendefine:: GPIO_SEL_4 +.. doxygendefine:: GPIO_SEL_5 +.. doxygendefine:: GPIO_SEL_6 +.. doxygendefine:: GPIO_SEL_7 +.. doxygendefine:: GPIO_SEL_8 +.. doxygendefine:: GPIO_SEL_9 +.. doxygendefine:: GPIO_SEL_10 +.. doxygendefine:: GPIO_SEL_11 +.. doxygendefine:: GPIO_SEL_12 +.. doxygendefine:: GPIO_SEL_13 +.. doxygendefine:: GPIO_SEL_14 +.. doxygendefine:: GPIO_SEL_15 +.. doxygendefine:: GPIO_SEL_16 +.. doxygendefine:: GPIO_SEL_17 +.. doxygendefine:: GPIO_SEL_18 +.. doxygendefine:: GPIO_SEL_19 +.. doxygendefine:: GPIO_SEL_21 +.. doxygendefine:: GPIO_SEL_22 +.. doxygendefine:: GPIO_SEL_23 +.. doxygendefine:: GPIO_SEL_25 +.. doxygendefine:: GPIO_SEL_26 +.. doxygendefine:: GPIO_SEL_27 +.. doxygendefine:: GPIO_SEL_32 +.. doxygendefine:: GPIO_SEL_33 +.. doxygendefine:: GPIO_SEL_34 +.. doxygendefine:: GPIO_SEL_35 +.. doxygendefine:: GPIO_SEL_36 +.. doxygendefine:: GPIO_SEL_37 +.. doxygendefine:: GPIO_SEL_38 +.. doxygendefine:: GPIO_SEL_39 +.. doxygendefine:: GPIO_PIN_REG_0 +.. doxygendefine:: GPIO_PIN_REG_1 +.. doxygendefine:: GPIO_PIN_REG_2 +.. doxygendefine:: GPIO_PIN_REG_3 +.. doxygendefine:: GPIO_PIN_REG_4 +.. doxygendefine:: GPIO_PIN_REG_5 +.. doxygendefine:: GPIO_PIN_REG_6 +.. doxygendefine:: GPIO_PIN_REG_7 +.. doxygendefine:: GPIO_PIN_REG_8 +.. doxygendefine:: GPIO_PIN_REG_9 +.. doxygendefine:: GPIO_PIN_REG_10 +.. doxygendefine:: GPIO_PIN_REG_11 +.. doxygendefine:: GPIO_PIN_REG_12 +.. doxygendefine:: GPIO_PIN_REG_13 +.. doxygendefine:: GPIO_PIN_REG_14 +.. doxygendefine:: GPIO_PIN_REG_15 +.. doxygendefine:: GPIO_PIN_REG_16 +.. doxygendefine:: GPIO_PIN_REG_17 +.. doxygendefine:: GPIO_PIN_REG_18 +.. doxygendefine:: GPIO_PIN_REG_19 +.. doxygendefine:: GPIO_PIN_REG_20 +.. doxygendefine:: GPIO_PIN_REG_21 +.. doxygendefine:: GPIO_PIN_REG_22 +.. doxygendefine:: GPIO_PIN_REG_23 +.. doxygendefine:: GPIO_PIN_REG_25 +.. doxygendefine:: GPIO_PIN_REG_26 +.. doxygendefine:: GPIO_PIN_REG_27 +.. doxygendefine:: GPIO_PIN_REG_32 +.. doxygendefine:: GPIO_PIN_REG_33 +.. doxygendefine:: GPIO_PIN_REG_34 +.. doxygendefine:: GPIO_PIN_REG_35 +.. doxygendefine:: GPIO_PIN_REG_36 +.. doxygendefine:: GPIO_PIN_REG_37 +.. doxygendefine:: GPIO_PIN_REG_38 +.. doxygendefine:: GPIO_PIN_REG_39 +.. doxygendefine:: GPIO_APP_CPU_INTR_ENA +.. doxygendefine:: GPIO_APP_CPU_NMI_INTR_ENA +.. doxygendefine:: GPIO_PRO_CPU_INTR_ENA +.. doxygendefine:: GPIO_PRO_CPU_NMI_INTR_ENA +.. doxygendefine:: GPIO_SDIO_EXT_INTR_ENA +.. doxygendefine:: GPIO_MODE_DEF_INPUT +.. doxygendefine:: GPIO_MODE_DEF_OUTPUT +.. doxygendefine:: GPIO_MODE_DEF_OD +.. doxygendefine:: GPIO_PIN_COUNT +.. doxygendefine:: GPIO_IS_VALID_GPIO +.. doxygendefine:: GPIO_IS_VALID_OUTPUT_GPIO + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: gpio_event_callback Enumerations ^^^^^^^^^^^^ +.. doxygenenum:: gpio_num_t .. doxygenenum:: gpio_int_type_t .. doxygenenum:: gpio_mode_t +.. doxygenenum:: gpio_pullup_t +.. doxygenenum:: gpio_pulldown_t .. doxygenenum:: gpio_pull_mode_t Functions @@ -37,54 +131,3 @@ Functions .. doxygenfunction:: gpio_wakeup_enable .. doxygenfunction:: gpio_wakeup_disable .. doxygenfunction:: gpio_isr_register - -*Example code:* Configuration of GPIO as an output - -.. code-block:: c - - gpio_config_t io_conf; - io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode - io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19 - io_conf.pull_down_en = 0; //disable pull-down mode - io_conf.pull_up_en = 0; //disable pull-up mode - gpio_config(&io_conf); //configure GPIO with the given settings - -*Example code:* Configuration of GPIO as an input - -.. code-block:: c - - gpio_config_t io_conf; - io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt - io_conf.mode = GPIO_MODE_INPUT; //set as input - io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5 - io_conf.pull_down_en = 0; //disable pull-down mode - io_conf.pull_up_en = 1; //enable pull-up mode - gpio_config(&io_conf); //configure GPIO with the given settings - - -ROM GPIO functions -^^^^^^^^^^^^^^^^^^ - -.. doxygenfunction:: gpio_init -.. doxygenfunction:: gpio_output_set -.. doxygenfunction:: gpio_output_set_high -.. doxygenfunction:: gpio_input_get -.. doxygenfunction:: gpio_input_get_high -.. doxygenfunction:: gpio_intr_handler_register -.. doxygenfunction:: gpio_intr_pending -.. doxygenfunction:: gpio_intr_pending_high -.. doxygenfunction:: gpio_intr_ack -.. doxygenfunction:: gpio_intr_ack_high -.. doxygenfunction:: gpio_pin_wakeup_enable -.. doxygenfunction:: gpio_pin_wakeup_disable -.. doxygenfunction:: gpio_matrix_in -.. doxygenfunction:: gpio_matrix_out -.. doxygenfunction:: gpio_pad_select_gpio -.. doxygenfunction:: gpio_pad_set_drv -.. doxygenfunction:: gpio_pad_pullup -.. doxygenfunction:: gpio_pad_pulldown -.. doxygenfunction:: gpio_pad_unhold -.. doxygenfunction:: gpio_pad_hold - - diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst new file mode 100644 index 0000000000..32b639f3f9 --- /dev/null +++ b/docs/api/ledc.rst @@ -0,0 +1,61 @@ +LED Control +=========== + +Overview +-------- + +`Instructions`_ + +Application Example +------------------- + +`Instructions`_ + +API Reference +------------- + +`Instructions`_ + +.. _Instructions: template.html + +Data Structures +^^^^^^^^^^^^^^^ + +.. doxygenstruct:: ledc_channel_config_t +.. doxygenstruct:: ledc_timer_config_t + +Macros +^^^^^^ + +.. doxygendefine:: LEDC_APB_CLK_HZ +.. doxygendefine:: LEDC_REF_CLK_HZ + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: ledc_mode_t +.. doxygenenum:: ledc_intr_type_t +.. doxygenenum:: ledc_duty_direction_t +.. doxygenenum:: ledc_clk_src_t +.. doxygenenum:: ledc_timer_t +.. doxygenenum:: ledc_channel_t +.. doxygenenum:: ledc_timer_bit_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: ledc_channel_config +.. doxygenfunction:: ledc_timer_config +.. doxygenfunction:: ledc_update_duty +.. doxygenfunction:: ledc_stop +.. doxygenfunction:: ledc_set_freq +.. doxygenfunction:: ledc_get_freq +.. doxygenfunction:: ledc_set_duty +.. doxygenfunction:: ledc_get_duty +.. doxygenfunction:: ledc_set_fade +.. doxygenfunction:: ledc_isr_register +.. doxygenfunction:: ledc_timer_set +.. doxygenfunction:: ledc_timer_rst +.. doxygenfunction:: ledc_timer_pause +.. doxygenfunction:: ledc_timer_resume +.. doxygenfunction:: ledc_bind_channel_timer diff --git a/docs/api/log.rst b/docs/api/log.rst index dc76a30470..49f97108aa 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -3,6 +3,33 @@ API Reference ------------- +Macros +^^^^^^ + +.. doxygendefine:: LOG_COLOR_E +.. doxygendefine:: LOG_COLOR_W +.. doxygendefine:: LOG_COLOR_I +.. doxygendefine:: LOG_COLOR_D +.. doxygendefine:: LOG_COLOR_V +.. doxygendefine:: LOG_RESET_COLOR +.. doxygendefine:: LOG_FORMAT +.. doxygendefine:: LOG_LOCAL_LEVEL +.. doxygendefine:: ESP_EARLY_LOGE +.. doxygendefine:: ESP_EARLY_LOGW +.. doxygendefine:: ESP_EARLY_LOGI +.. doxygendefine:: ESP_EARLY_LOGD +.. doxygendefine:: ESP_EARLY_LOGV +.. doxygendefine:: ESP_LOGE +.. doxygendefine:: ESP_LOGW +.. doxygendefine:: ESP_LOGI +.. doxygendefine:: ESP_LOGD +.. doxygendefine:: ESP_LOGV + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: vprintf_like_t + Enumerations ^^^^^^^^^^^^ @@ -17,3 +44,11 @@ Functions .. doxygenfunction:: esp_log_write + + + + + + + + diff --git a/docs/api/nvs_flash.rst b/docs/api/nvs_flash.rst new file mode 100644 index 0000000000..16c74fa530 --- /dev/null +++ b/docs/api/nvs_flash.rst @@ -0,0 +1,67 @@ +.. include:: ../../components/nvs_flash/README.rst + +Application Example +------------------- + +`Instructions `_ + +API Reference +------------- + +Macros +^^^^^^ + +.. doxygendefine:: ESP_ERR_NVS_BASE +.. doxygendefine:: ESP_ERR_NVS_NOT_INITIALIZED +.. doxygendefine:: ESP_ERR_NVS_NOT_FOUND +.. doxygendefine:: ESP_ERR_NVS_TYPE_MISMATCH +.. doxygendefine:: ESP_ERR_NVS_READ_ONLY +.. doxygendefine:: ESP_ERR_NVS_NOT_ENOUGH_SPACE +.. doxygendefine:: ESP_ERR_NVS_INVALID_NAME +.. doxygendefine:: ESP_ERR_NVS_INVALID_HANDLE +.. doxygendefine:: ESP_ERR_NVS_REMOVE_FAILED +.. doxygendefine:: ESP_ERR_NVS_KEY_TOO_LONG +.. doxygendefine:: ESP_ERR_NVS_PAGE_FULL +.. doxygendefine:: ESP_ERR_NVS_INVALID_STATE +.. doxygendefine:: ESP_ERR_NVS_INVALID_LENGTH + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: nvs_handle + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: nvs_open_mode + +Functions +^^^^^^^^^ +.. doxygenfunction:: nvs_open +.. doxygenfunction:: nvs_set_i8 +.. doxygenfunction:: nvs_set_u8 +.. doxygenfunction:: nvs_set_i16 +.. doxygenfunction:: nvs_set_u16 +.. doxygenfunction:: nvs_set_i32 +.. doxygenfunction:: nvs_set_u32 +.. doxygenfunction:: nvs_set_i64 +.. doxygenfunction:: nvs_set_u64 +.. doxygenfunction:: nvs_set_str +.. doxygenfunction:: nvs_set_blob +.. doxygenfunction:: nvs_get_i8 +.. doxygenfunction:: nvs_get_u8 +.. doxygenfunction:: nvs_get_i16 +.. doxygenfunction:: nvs_get_u16 +.. doxygenfunction:: nvs_get_i32 +.. doxygenfunction:: nvs_get_u32 +.. doxygenfunction:: nvs_get_i64 +.. doxygenfunction:: nvs_get_u64 +.. doxygenfunction:: nvs_get_str +.. doxygenfunction:: nvs_get_blob +.. doxygenfunction:: nvs_erase_key +.. doxygenfunction:: nvs_erase_all +.. doxygenfunction:: nvs_commit +.. doxygenfunction:: nvs_close +.. doxygenfunction:: nvs_flash_init +.. doxygenfunction:: nvs_flash_init_custom + diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst index 97ea1a5848..122a8671ea 100644 --- a/docs/api/vfs.rst +++ b/docs/api/vfs.rst @@ -1,15 +1,19 @@ .. include:: ../../components/vfs/README.rst +Application Example +------------------- + +`Instructions `_ + API Reference ------------- -Defines -^^^^^^^ +Macros +^^^^^^ -.. doxygendefine:: ESP_VFS_PATH_MAX +.. doxygendefine:: ESP_VFS_PATH_MAX .. doxygendefine:: ESP_VFS_FLAG_DEFAULT -.. doxygendefine:: ESP_VFS_FLAG_CONTEXT_PTR - +.. doxygendefine:: ESP_VFS_FLAG_CONTEXT_PTR Structures ^^^^^^^^^^ @@ -19,9 +23,15 @@ Structures Functions ^^^^^^^^^ -.. doxygenfunction:: esp_vfs_dev_uart_register .. doxygenfunction:: esp_vfs_register - - - - +.. doxygenfunction:: esp_vfs_write +.. doxygenfunction:: esp_vfs_lseek +.. doxygenfunction:: esp_vfs_read +.. doxygenfunction:: esp_vfs_open +.. doxygenfunction:: esp_vfs_close +.. doxygenfunction:: esp_vfs_fstat +.. doxygenfunction:: esp_vfs_stat +.. doxygenfunction:: esp_vfs_link +.. doxygenfunction:: esp_vfs_unlink +.. doxygenfunction:: esp_vfs_rename +.. doxygenfunction:: esp_vfs_dev_uart_register From c3f7d15246506cac2f948565e9c9d830e6478a9a Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sat, 5 Nov 2016 17:19:31 +0100 Subject: [PATCH 136/285] Instructions to prepare documentation --- docs/api/template.rst | 87 ++++++++++++++++++------------ docs/documenting-code.rst | 111 ++++++++++++++++++++++++-------------- 2 files changed, 126 insertions(+), 72 deletions(-) diff --git a/docs/api/template.rst b/docs/api/template.rst index 0f2623c47f..3c8bcdb62c 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -1,70 +1,91 @@ -Template API -============= +Template +======== + +.. note:: + + *INSTRUCTIONS* + + 1. Use this file as a template to document API. + 2. Change the file name to the name of the header file that represents documented API. + 3. Include respective files with descriptions from the API folder using ``..include::`` + + * README.rst + * example.rst + + 4. Optionally provide description right in this file. + 5. Once done, remove all instructions like this one and any superfluous headers. Overview -------- -INSTRUCTIONS: Provide overview where and how this API may be used. For large number of functions, break down description into groups. +.. note:: -Use the folowing heading levels: + *INSTRUCTIONS* -* # with overline, for parts -* \* with overline, for chapters -* =, for sections -* -, for subsections -* ^, for subsubsections -* ", for paragraphs + 1. Provide overview where and how this API may be used. + 2. Where applicable include code snippets to illustrate functionality of particular functions. + 3. To distinguish between sections, use the following `heading levels `_: + * ``#`` with overline, for parts + * ``*`` with overline, for chapters + * ``=``, for sections + * ``-``, for subsections + * ``^``, for subsubsections + * ``"``, for paragraphs Application Example ------------------- -INSTRUCTIONS: Provide one or more pratical examples to demonstrate functionality of this API. +.. note:: + *INSTRUCTIONS* -Reference ---------- + 1. Provide one or more practical examples to demonstrate functionality of this API. + 2. Break down the code into parts and describe functionality of each part. + 3. Provide screenshots if applicable. -INSTRUCTIONS: Provide list of API memebers divided into sections. Use coresponding **.. doxygen** directices, so member documentation is auto updated. +API Reference +------------- -* Data Structures **.. doxygenstruct** -* Macros **.. doxygendefine** -* Type Definitions **.. doxygentypedef** -* Enumerations **.. doxygenenum** -* Functions **.. doxygenfunction** -* Variables **.. doxygenvariable** +.. note:: -Include code snippotes to ilustrate functionality of particular functions where applicable. Skip section hearder if empty. + *INSTRUCTIONS* + + 1. Provide list of API members divided into sections. + 2. Use corresponding ``.. doxygen..`` directives, so member documentation is auto updated. + * Data Structures -``.. doxygenstruct::`` + * Macros - ``.. doxygendefine::`` + * Type Definitions - ``.. doxygentypedef::`` + * Enumerations - ``.. doxygenenum::`` + * Functions - ``.. doxygenfunction::`` + + See `Breathe documentation `_ for additional information. + + 3. Once done remove superfluous headers. + 4. When changes are committed and documentation is build, check how this section rendered. :doc:`Correct annotations <../documenting-code>` in respective header files, if required. Data Structures ^^^^^^^^^^^^^^^ -.. Data Structures .. doxygenstruct +``.. doxygenstruct:: name_of_structure`` Macros ^^^^^^ -.. Macros .. doxygendefine +``.. doxygendefine:: name_of_macro`` Type Definitions ^^^^^^^^^^^^^^^^ -.. Type Definitions .. doxygentypedef +``.. doxygentypedef:: name_of_type`` Enumerations ^^^^^^^^^^^^ -.. Enumerations .. doxygenenum +``.. doxygenenum:: name_of_enumeration`` Functions ^^^^^^^^^ -.. Functions .. doxygenfunction - -Variables -^^^^^^^^^ - -.. Variables .. doxygenvariable - - +``.. doxygenfunction:: name_of_function`` diff --git a/docs/documenting-code.rst b/docs/documenting-code.rst index 51a0dbf7d3..72e3cea147 100644 --- a/docs/documenting-code.rst +++ b/docs/documenting-code.rst @@ -1,10 +1,18 @@ Documenting Code ================ +The purpose of this description is to provide quick summary on documentation style used in `espressif/esp-idf`_ repository and how to add new documentation. + Introduction ------------ -When documenting code for this repository, please follow `Doxygen style `_. You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks like for example ``/* @param ratio this is oxygen to air ratio */``. +When documenting code for this repository, please follow `Doxygen style `_. You are doing it by inserting special commands, for instance ``@param``, into standard comments blocks, for example: + +:: + + /** + * @param ratio this is oxygen to air ratio + */ Doxygen is phrasing the code, extracting the commands together with subsequent text, and building documentation out of it. @@ -14,20 +22,19 @@ Typical comment block, that contains documentation of a function, looks like bel :align: center :alt: Sample inline code documentation -Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get the taste of available features please check data reach and very well organized `Doxygen Manual `_. +Doxygen supports couple of formatting styles. It also gives you great flexibility on level of details to include in documentation. To get familiar with available features, please check data reach and very well organized `Doxygen Manual `_. Why we need it? --------------- -The purpose of this description is to provide quick summary on documentation style used in `espressif/esp-idf `_ repository. +The ultimate goal is to ensure that all the code is consistently documented, so we can use tools like `Sphinx `_ and `Breathe `_ to aid preparation and automatic updates of API documentation when the code changes. -The ultimate goal is to ensure that all the code is consistently documented, so we can use tools like `Sphinx `_ and `Breathe `_ to aid preparation and automatic updates of API documentation when the code changes. The above piece of code renders in Sphinx like below: +With these tools the above piece of code renders like below: .. image:: _static/doc-code-documentation-rendered.png :align: center :alt: Sample inline code after rendering - Go for it! ---------- @@ -57,7 +64,7 @@ When writing code for this repository, please follow guidelines below. 6. To provide well formatted lists, break the line after command (like ``@return`` in example below). - :: + .. code-block:: c ... * @@ -70,53 +77,76 @@ When writing code for this repository, please follow guidelines below. * ... - - 7. Overview of functionality of documented header file, or group of files that make a library, should be placed in separate ``README.rst`` file. + 7. Overview of functionality of documented header file, or group of files that make a library, should be placed in the same directory in a separate ``README.rst`` file. If directory contains header files for different APIs, then the file name should be ``apiname-readme.rst``. Go one extra mile ----------------- -There are couple of tips how you can make your documentation even better and more useful to the reader. +There is couple of tips, how you can make your documentation even better and more useful to the reader. -Add code snippets to illustrate implementation. To do so, enclose the snippet using ``@code{c}`` and ``@endcode`` commands. + 1. Add code snippets to illustrate implementation. To do so, enclose snippet using ``@code{c}`` and ``@endcode`` commands. -:: + .. code-block:: c - ... - * - * @code{c} - * // Example of using nvs_get_i32: - * int32_t max_buffer_size = 4096; // default value - * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); - * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); - * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still - * // have its default value. - * @endcode - * - ... + ... + * + * @code{c} + * // Example of using nvs_get_i32: + * int32_t max_buffer_size = 4096; // default value + * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); + * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); + * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still + * // have its default value. + * @endcode + * + ... -To highlight some information use command ``@attention`` or ``@note``. Example below also shows how to use a numbered list. + The code snippet should be enclosed in a comment block of the function that it illustrates. -:: + 2. To highlight some important information use command ``@attention`` or ``@note``. - ... - * - * @attention - * 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode - * 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. - * - ... + .. code-block:: c + ... + * + * @attention + * 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode + * 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. + * + ... -Use markdown to make your documentation even more readable. With markdown you can add headers, links, tables and more. + Above example also shows how to use a numbered list. -:: + 3. Use markdown to make your documentation even more readable. You will add headers, links, tables and more. - ... - * - * [ESP32 Technical Reference](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf) - * - ... + .. code-block:: c + + ... + * + * [ESP32 Technical Reference](http://espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf) + * + ... + + .. note:: + + Code snippets, notes, links, etc. will not make it to the documentation, if not enclosed in a comment block associated with one of documented objects. + + 5. Prepare one or more complete code examples together with description. Place them in a separate file ``example.rst`` in the same directory as the API header files. If directory contains header files for different APIs, then the file name should be ``apiname-example.rst``. + +Put it all together +------------------- + +Once all the above steps are complete, follow instruction in :doc:`api/template` and create a single file, that will merge all individual pieces of prepared documentation. Finally add a link to this file to respective ``.. toctree::`` in ``index.rst`` file located in ``/docs`` folder. + +OK, but I am new to Sphinx! +--------------------------- + +1. No worries. All the software you need is well documented. It is also open source and free. Start by checking `Sphinx `_ documentation. If you are not clear how to write using rst markup language, see `reStructuredText Primer `_. +2. Check the source files of this documentation to understand what is behind of what you see now on the screen. Sources are maintained on GitHub in `espressif/esp-idf`_ repository in `/docs `_ folder. You can go directly to the source file of this page by scrolling up and clicking the link in the top right corner. When on GitHub, see what's really inside, open source files by clicking ``Raw`` button. +3. You will likely want to see how documentation builds and looks like before posting it on the GitHub. There are two options to do so: + + * Install `Sphinx `_, `Breathe `_ and `Doxygen `_ to build it locally. You would need a Linux machine for that. + * Set up an account on `Read the Docs `_ and build documentation in the cloud. Read the Docs provides document building and hosting for free and their service works really quick and great. Wrap up ------- @@ -124,3 +154,6 @@ Wrap up We love good code that is doing cool things. We love it even better, if it is well documented, so we can quickly make it run and also do the cool things. +Go ahead, contribute your code and documentation! + +.. _espressif/esp-idf: https://github.com/espressif/esp-idf/ From d093dcbbb721a0227cced2d62365ed9eced327cf Mon Sep 17 00:00:00 2001 From: Yinling Date: Sun, 6 Nov 2016 22:43:12 +0800 Subject: [PATCH 137/285] try to checkout same branch for submodule in CI jobs --- .gitlab-ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ad5a13ad03..b06eccb75f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,12 +12,16 @@ before_script: - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - + # CI may download a cached project. Need to re-sync submodule + - git submodule deinit -f . # if testing master branch, use github wifi libs. # if testing other branches, use gitlab wifi libs (as maybe changes aren't merged to master yet) - test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-wifi-lib%ssh://git@gitlab.espressif.cn:27227/idf/esp32-wifi-lib%" .gitmodules # fetch all submodules - git submodule update --init --recursive + # try use submodule with same branch + - SUBMODULES=`cat .gitmodules | grep path | awk '{print $3}'` + - for MODULE in $SUBMODULES;do (cd $MODULE;git fetch;git checkout ${CI_BUILD_REF_NAME} || echo "using default branch";cd $CI_PROJECT_DIR); done build_template_app: stage: build From 3051c74488e8e0f44839d03ff1faeeb8a3ff9f20 Mon Sep 17 00:00:00 2001 From: Yinling Date: Sun, 6 Nov 2016 22:53:23 +0800 Subject: [PATCH 138/285] No need to deinit submodules as use clean clone by config --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b06eccb75f..3e88ddc638 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,8 +12,6 @@ before_script: - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - # CI may download a cached project. Need to re-sync submodule - - git submodule deinit -f . # if testing master branch, use github wifi libs. # if testing other branches, use gitlab wifi libs (as maybe changes aren't merged to master yet) - test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-wifi-lib%ssh://git@gitlab.espressif.cn:27227/idf/esp32-wifi-lib%" .gitmodules From 522f83ae3d71357683a62f69a139b8c1eda8f362 Mon Sep 17 00:00:00 2001 From: Yinling Date: Sun, 6 Nov 2016 22:58:31 +0800 Subject: [PATCH 139/285] remove unnecessary "git fetch" and add echo submodule path --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3e88ddc638..6f050d1fcb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ before_script: - git submodule update --init --recursive # try use submodule with same branch - SUBMODULES=`cat .gitmodules | grep path | awk '{print $3}'` - - for MODULE in $SUBMODULES;do (cd $MODULE;git fetch;git checkout ${CI_BUILD_REF_NAME} || echo "using default branch";cd $CI_PROJECT_DIR); done + - for MODULE in $SUBMODULES;do (echo $MODULE;cd $MODULE;git checkout ${CI_BUILD_REF_NAME} || echo "using default branch";cd $CI_PROJECT_DIR); done build_template_app: stage: build From 025bb473020da4b1c2f8b53987efeb4e7ab2fea0 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Sun, 6 Nov 2016 17:41:08 +0100 Subject: [PATCH 140/285] Non-Volatile Storage (NVS) example Demonstrates how to read and write a value using NVS. The value tracks number of ESP32 module restarts. Example also shows how to use basic diagnostics if read / write operation was successful. --- examples/07_nvs_read_write/Makefile | 9 +++ examples/07_nvs_read_write/README.md | 7 +++ examples/07_nvs_read_write/main/component.mk | 10 +++ .../07_nvs_read_write/main/nvs_read_write.c | 62 +++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 examples/07_nvs_read_write/Makefile create mode 100644 examples/07_nvs_read_write/README.md create mode 100644 examples/07_nvs_read_write/main/component.mk create mode 100644 examples/07_nvs_read_write/main/nvs_read_write.c diff --git a/examples/07_nvs_read_write/Makefile b/examples/07_nvs_read_write/Makefile new file mode 100644 index 0000000000..3d6adb4d02 --- /dev/null +++ b/examples/07_nvs_read_write/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := nvs-read-write + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/07_nvs_read_write/README.md b/examples/07_nvs_read_write/README.md new file mode 100644 index 0000000000..bac8ee8d54 --- /dev/null +++ b/examples/07_nvs_read_write/README.md @@ -0,0 +1,7 @@ +# Non-Volatile Storage (NVS) Read and Write Example + +Demonstrates how to read and write a value using NVS. The value tracks number of ESP32 module restarts. + +Example also shows how to use basic diagnostics if read / write operation was successful. + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/07_nvs_read_write/main/component.mk b/examples/07_nvs_read_write/main/component.mk new file mode 100644 index 0000000000..24356f23ed --- /dev/null +++ b/examples/07_nvs_read_write/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/examples/07_nvs_read_write/main/nvs_read_write.c b/examples/07_nvs_read_write/main/nvs_read_write.c new file mode 100644 index 0000000000..40d330f62f --- /dev/null +++ b/examples/07_nvs_read_write/main/nvs_read_write.c @@ -0,0 +1,62 @@ +/* Non-Volatile Storage (NVS) Read and Write Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" + +void app_main() +{ + nvs_flash_init(); + system_init(); + + nvs_handle handle_to_settings; + esp_err_t err; + int32_t restart_counter = 0; + + // Open the NVS + printf("Opening Non-Volatile Storage (NVS) ... "); + err = nvs_open("settings", NVS_READWRITE, &handle_to_settings); + printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); + + // Read from the NVS + printf("Reading restart counter from NVS ... "); + err = nvs_get_i32(handle_to_settings, "restart_conter", &restart_counter); + switch (err) { + case ESP_OK: + printf("OK\n"); + printf("Restart counter = %d\n", restart_counter); + break; + case ESP_ERR_NVS_NOT_FOUND: + printf("The counter is not initialized yet!\n"); + break; + default : + printf("Error (%d) reading!\n", err); + } + + // Write to the NVS + printf("Updating restart counter in NVS ... "); + restart_counter++; + err = nvs_set_i32(handle_to_settings, "restart_conter", restart_counter); + printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); + + // Close the NVS + nvs_close(handle_to_settings); + + // Restart module + for (int i = 10; i >= 0; i--) { + printf("Restarting in %d seconds...\n", i); + vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("Restarting now.\n"); + fflush(stdout); + system_restart(); +} From b6dd8a55cd4d17750491f56c33d21a392bdd2840 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 7 Nov 2016 12:27:53 +0800 Subject: [PATCH 141/285] Fix build with macports/pkgconfig, silence format string warnings on OS X Extra space is needed in echo -n "-lncurses ", otherwise if pkg-config outputs a directory after that, it will result in "-lncurses-L/opt/local/lib" (without space). -Wno-format-security flag is needed on macOS to silence warnings about printf(gettext("message")) constructs. --- tools/kconfig/lxdialog/check-lxdialog.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/kconfig/lxdialog/check-lxdialog.sh b/tools/kconfig/lxdialog/check-lxdialog.sh index 48c19278a9..79df5ed4f9 100755 --- a/tools/kconfig/lxdialog/check-lxdialog.sh +++ b/tools/kconfig/lxdialog/check-lxdialog.sh @@ -6,7 +6,7 @@ ldflags() { if [ $(uname -s) == "Darwin" ]; then #OSX seems to need ncurses too - echo -n "-lncurses" + echo -n "-lncurses " fi pkg-config --libs ncursesw 2>/dev/null && exit pkg-config --libs ncurses 2>/dev/null && exit @@ -43,7 +43,7 @@ ccflags() fi if [ $(uname -s) == "Darwin" ]; then #OSX doesn't have libintl - echo -n "-DKBUILD_NO_NLS" + echo -n "-DKBUILD_NO_NLS -Wno-format-security " fi } From e452278194ff16696519444fb9132b3ffa143dc0 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 7 Nov 2016 14:16:52 +0800 Subject: [PATCH 142/285] Minor changes for driver 1. remove "\n" when calling ESP_LOGX APIs. 2. modify uart_event_t for uart rx data. 3. use MICRO for uart inverse value 4. add uart_tx_data_t for internal tx function. --- components/driver/gpio.c | 59 ++++++++++--------- components/driver/include/driver/uart.h | 39 +++++-------- components/driver/ledc.c | 78 ++++++++++++------------- components/driver/uart.c | 48 +++++++++------ 4 files changed, 114 insertions(+), 110 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 62a0e7faa7..b445d3df03 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -22,7 +22,7 @@ static const char* GPIO_TAG = "GPIO"; #define GPIO_CHECK(a, str, ret_val) if (!(a)) { \ - ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret_val); \ } @@ -71,15 +71,15 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); - GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_type = intr_type; return ESP_OK; } esp_err_t gpio_intr_enable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(xPortGetCoreID() == 0) { GPIO.pin[gpio_num].int_ena = GPIO_PRO_CPU_INTR_ENA; //enable pro cpu intr } else { @@ -90,14 +90,14 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num) esp_err_t gpio_intr_disable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].int_ena = 0; //disable GPIO intr return ESP_OK; } static esp_err_t gpio_output_disable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1tc = (0x1 << gpio_num); } else { @@ -108,7 +108,7 @@ static esp_err_t gpio_output_disable(gpio_num_t gpio_num) static esp_err_t gpio_output_enable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG); if(gpio_num < 32) { GPIO.enable_w1ts = (0x1 << gpio_num); } else { @@ -119,7 +119,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num) esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(level) { if(gpio_num < 32) { GPIO.out_w1ts = (1 << gpio_num); @@ -147,8 +147,8 @@ int gpio_get_level(gpio_num_t gpio_num) esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); - GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; switch(pull) { case GPIO_PULLUP_ONLY: @@ -168,7 +168,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); break; default: - ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull); + ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u",gpio_num,pull); ret = ESP_ERR_INVALID_ARG; break; } @@ -177,9 +177,9 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); if(gpio_num >= 34 && (mode & (GPIO_MODE_DEF_OUTPUT))) { - ESP_LOGE(GPIO_TAG, "io_num=%d can only be input\n",gpio_num); + ESP_LOGE(GPIO_TAG, "io_num=%d can only be input",gpio_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -214,53 +214,56 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) uint64_t gpio_pin_mask = (pGPIOConfig->pin_bit_mask); uint32_t io_reg = 0; uint32_t io_num = 0; - uint64_t bit_valid = 0; + uint8_t input_en = 0; + uint8_t output_en = 0; + uint8_t od_en = 0; + uint8_t pu_en = 0; + uint8_t pd_en = 0; if(pGPIOConfig->pin_bit_mask == 0 || pGPIOConfig->pin_bit_mask >= (((uint64_t) 1) << GPIO_PIN_COUNT)) { - ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error \n"); + ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error "); return ESP_ERR_INVALID_ARG; } if((pGPIOConfig->mode) & (GPIO_MODE_DEF_OUTPUT)) { //GPIO 34/35/36/37/38/39 can only be used as input mode; if((gpio_pin_mask & ( GPIO_SEL_34 | GPIO_SEL_35 | GPIO_SEL_36 | GPIO_SEL_37 | GPIO_SEL_38 | GPIO_SEL_39))) { - ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode\n"); + ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode"); return ESP_ERR_INVALID_ARG; } } do { io_reg = GPIO_PIN_MUX_REG[io_num]; if(((gpio_pin_mask >> io_num) & BIT(0)) && io_reg) { - ESP_LOGI(GPIO_TAG, "Gpio%02d |Mode:",io_num); if((pGPIOConfig->mode) & GPIO_MODE_DEF_INPUT) { - ESP_LOGI(GPIO_TAG, "INPUT "); + input_en = 1; PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[io_num]); } else { PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[io_num]); } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OD) { - ESP_LOGI(GPIO_TAG, "OD "); + od_en = 1; GPIO.pin[io_num].pad_driver = 1; /*0x01 Open-drain */ } else { GPIO.pin[io_num].pad_driver = 0; /*0x00 Normal gpio output */ } if((pGPIOConfig->mode) & GPIO_MODE_DEF_OUTPUT) { - ESP_LOGI(GPIO_TAG, "OUTPUT "); + output_en = 1; gpio_output_enable(io_num); } else { gpio_output_disable(io_num); } if(pGPIOConfig->pull_up_en) { - ESP_LOGI(GPIO_TAG, "PU "); + pu_en = 1; PIN_PULLUP_EN(io_reg); } else { PIN_PULLUP_DIS(io_reg); } if(pGPIOConfig->pull_down_en) { - ESP_LOGI(GPIO_TAG, "PD "); + pd_en = 1; PIN_PULLDWN_EN(io_reg); } else { PIN_PULLDWN_DIS(io_reg); } - ESP_LOGI(GPIO_TAG, "Intr:%d |\n",pGPIOConfig->intr_type); + ESP_LOGI(GPIO_TAG, "GPIO[%d]| InputEn: %d| OutputEn: %d| OpenDrain: %d| Pullup: %d| Pulldown: %d| Intr:%d ", io_num, input_en, output_en, od_en, pu_en, pd_en, pGPIOConfig->intr_type); gpio_set_intr_type(io_num, pGPIOConfig->intr_type); if(pGPIOConfig->intr_type) { gpio_intr_enable(io_num); @@ -268,8 +271,6 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) gpio_intr_disable(io_num); } PIN_FUNC_SELECT(io_reg, PIN_FUNC_GPIO); /*function number 2 is GPIO_FUNC for each pin */ - } else if(bit_valid && (io_reg == 0)) { - ESP_LOGW(GPIO_TAG, "io_num=%d does not exist\n",io_num); } io_num++; } while(io_num < GPIO_PIN_COUNT); @@ -278,7 +279,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg) { - GPIO_CHECK(fn, "GPIO ISR null\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); ESP_INTR_DISABLE(gpio_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num); xt_set_interrupt_handler(gpio_intr_num, fn, arg); @@ -289,13 +290,13 @@ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * ar /*only level interrupt can be used for wake-up function*/ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; if((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) { GPIO.pin[gpio_num].int_type = intr_type; GPIO.pin[gpio_num].wakeup_enable = 0x1; } else { - ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num); + ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u",gpio_num); ret = ESP_ERR_INVALID_ARG; } return ret; @@ -303,7 +304,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error\n", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); GPIO.pin[gpio_num].wakeup_enable = 0; return ESP_OK; } diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index ba487d57d5..7dccf1666c 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -38,12 +38,18 @@ extern "C" { #define UART_BITRATE_MAX 5000000 #define UART_PIN_NO_CHANGE (-1) +#define UART_INVERSE_DISABLE (0x0) /*!< Disable UART signal inverse*/ +#define UART_INVERSE_RXD (UART_RXD_INV_M) /*!< UART RXD input inverse*/ +#define UART_INVERSE_CTS (UART_CTS_INV_M) /*!< UART CTS input inverse*/ +#define UART_INVERSE_TXD (UART_TXD_INV_M) /*!< UART TXD output inverse*/ +#define UART_INVERSE_RTS (UART_RTS_INV_M) /*!< UART RTS output inverse*/ + typedef enum { UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/ UART_DATA_7_BITS = 0x2, /*!< word length: 7bits*/ UART_DATA_8_BITS = 0x3, /*!< word length: 8bits*/ - UART_DATA_MAX_BITS = 0X4, + UART_DATA_BITS_MAX = 0X4, } uart_word_length_t; typedef enum { @@ -74,14 +80,6 @@ typedef enum { UART_HW_FLOWCTRL_MAX = 0x4, } uart_hw_flowcontrol_t; -typedef enum { - UART_INVERSE_DISABLE = 0x0, /*!< Disable UART wire output inverse*/ - UART_INVERSE_RXD = (uint32_t)UART_RXD_INV_M, /*!< UART RXD input inverse*/ - UART_INVERSE_CTS = (uint32_t)UART_CTS_INV_M, /*!< UART CTS input inverse*/ - UART_INVERSE_TXD = (uint32_t)UART_TXD_INV_M, /*!< UART TXD output inverse*/ - UART_INVERSE_RTS = (uint32_t)UART_RTS_INV_M, /*!< UART RTS output inverse*/ -} uart_inverse_t; - typedef struct { int baud_rate; /*!< UART baudrate*/ uart_word_length_t data_bits; /*!< UART byte size*/ @@ -110,14 +108,8 @@ typedef enum { } uart_event_type_t; typedef struct { - uart_event_type_t type; - union { - struct { - int brk_len; - size_t size; - uint8_t data[]; - } data; - }; + uart_event_type_t type; /*!< UART event type */ + size_t size; /*!< UART data size for UART_DATA event*/ } uart_event_t; /** @@ -225,14 +217,13 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); * * @param inverse_mask Choose the wires that need to be inversed. * - * (inverse_mask should be chosen from uart_inverse_t, combine with OR-OPERATION) + * (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION) * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask) ; - +esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask); /** * @brief Set hardware flow control. @@ -701,11 +692,13 @@ esp_err_t uart_flush(uart_port_t uart_num); * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { * ESP_LOGI(TAG, "uart[%d] event:", uart_num); * switch(event.type) { + * memset(dtmp, 0, sizeof(dtmp)); * //Event of UART receving data * case UART_DATA: - * ESP_LOGI(TAG,"data, len: %d\n", event.data.size); - * int len = uart_read_bytes(uart_num, dtmp, event.data.size, 10); - * ESP_LOGI(TAG, "uart read: %d\n", len); + * ESP_LOGI(TAG,"data, len: %d", event.size); + * int len = uart_read_bytes(uart_num, dtmp, event.size, 10); + * ESP_LOGI(TAG, "uart read: %d", len); + uart_write_bytes(uart_num, (const char*)dtmp, len); * break; * //Event of HW FIFO overflow detected * case UART_FIFO_OVF: diff --git a/components/driver/ledc.c b/components/driver/ledc.c index b9039cf626..41eb82cbdd 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -23,14 +23,14 @@ static const char* LEDC_TAG = "LEDC"; static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; #define LEDC_CHECK(a, str, ret_val) if (!(a)) { \ - ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret_val); \ } esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num; LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src; @@ -58,8 +58,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx; portEXIT_CRITICAL(&ledc_spinlock); @@ -68,8 +68,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1; LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0; @@ -79,8 +79,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1; portEXIT_CRITICAL(&ledc_spinlock); @@ -89,8 +89,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel) esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0; portEXIT_CRITICAL(&ledc_spinlock); @@ -99,7 +99,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel) static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); uint32_t value; uint32_t intr_type = type; portENTER_CRITICAL(&ledc_spinlock); @@ -115,7 +115,7 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg) { - LEDC_CHECK(fn, "ledc isr null\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); ESP_INTR_DISABLE(ledc_intr_num); intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num); @@ -131,13 +131,13 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) int bit_num = timer_conf->bit_num; int timer_num = timer_conf->timer_num; int speed_mode = timer_conf->speed_mode; - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) { - ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u\n", freq_hz, bit_num); + ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u", freq_hz, bit_num); return ESP_ERR_INVALID_ARG; } if(timer_num > LEDC_TIMER_3) { - ESP_LOGE(LEDC_TAG, "Time Select %u\n", timer_num); + ESP_LOGE(LEDC_TAG, "Time Select %u", timer_num); return ESP_ERR_INVALID_ARG; } esp_err_t ret = ESP_OK; @@ -149,7 +149,7 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) /*Selet the reference tick*/ div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; if(div_param <= 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) { - ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", (uint32_t)div_param); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", (uint32_t)div_param); ret = ESP_FAIL; } timer_clk_src = LEDC_REF_TICK; @@ -166,9 +166,9 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) { - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO); gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); if(speed_mode == LEDC_HIGH_SPEED_MODE) { @@ -187,10 +187,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) uint32_t timer_select = ledc_conf->timer_sel; uint32_t intr_type = ledc_conf->intr_type; uint32_t duty = ledc_conf->duty; - LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; /*set channel parameters*/ /* channel parameters decide how the waveform looks like in one period*/ @@ -202,7 +202,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select); /*set interrupt type*/ ledc_enable_intr_type(speed_mode, ledc_channel, intr_type); - ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n", + ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u", ledc_channel, gpio_num, duty, timer_select ); /*set LEDC signal in gpio matrix*/ @@ -214,8 +214,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf) esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; @@ -225,8 +225,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1; LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0; @@ -237,11 +237,11 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error", ESP_ERR_INVALID_ARG); if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) { - ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale); + ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale); return ESP_ERR_INVALID_ARG; } ledc_duty_config(speed_mode, @@ -258,8 +258,8 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); - LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); + LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG); ledc_duty_config(speed_mode, channel, //uint32_t chan_num, 0, //uint32_t hpoint_val, @@ -274,14 +274,14 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (-1)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (-1)); uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4); return duty; } esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", ESP_ERR_INVALID_ARG); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); esp_err_t ret = ESP_OK; uint32_t div_num = 0; @@ -294,7 +294,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; } if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) { - ESP_LOGE(LEDC_TAG, "div param err,div_param=%u\n", div_num); + ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num); ret = ESP_FAIL; } LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num = div_num; @@ -304,7 +304,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num) { - LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error\n", (0)); + LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (0)); portENTER_CRITICAL(&ledc_spinlock); uint32_t freq = 0; uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel; diff --git a/components/driver/uart.c b/components/driver/uart.c index a3e0b92b2b..b961fbed71 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -31,7 +31,7 @@ static const char* UART_TAG = "UART"; #define UART_CHECK(a, str, ret) if (!(a)) { \ - ESP_LOGE(UART_TAG,"%s:%d (%s):%s\n", __FILE__, __LINE__, __FUNCTION__, str); \ + ESP_LOGE(UART_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ return (ret); \ } #define UART_EMPTY_THRESH_DEFAULT (10) @@ -42,6 +42,14 @@ static const char* UART_TAG = "UART"; #define UART_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) #define UART_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) +typedef struct { + uart_event_type_t type; /*!< UART TX data type */ + struct { + int brk_len; + size_t size; + uint8_t data[0]; + } tx_data; +} uart_tx_data_t; typedef struct { uart_port_t uart_num; /*!< UART port number*/ @@ -67,7 +75,7 @@ typedef struct { RingbufHandle_t tx_ring_buf; /*!< TX ring buffer handler*/ bool tx_waiting_fifo; /*!< this flag indicates that some task is waiting for FIFO empty interrupt, used to send all data without any data buffer*/ uint8_t* tx_ptr; /*!< TX data pointer to push to FIFO in TX buffer mode*/ - uart_event_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ + uart_tx_data_t* tx_head; /*!< TX data pointer to head of the current buffer in TX ring buffer*/ uint32_t tx_len_tot; /*!< Total length of current item in ring buffer*/ uint32_t tx_len_cur; uint8_t tx_brk_flg; /*!< Flag to indicate to send a break signal in the end of the item sending procedure */ @@ -75,6 +83,8 @@ typedef struct { uint8_t tx_waiting_brk; /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/ } uart_obj_t; + + static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; static uart_dev_t* UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; @@ -82,7 +92,7 @@ static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((data_bit < UART_DATA_MAX_BITS), "data bit error", ESP_FAIL); + UART_CHECK((data_bit < UART_DATA_BITS_MAX), "data bit error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.bit_num = data_bit; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -366,7 +376,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r esp_err_t uart_set_rts(uart_port_t uart_num, int level) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control\n", ESP_FAIL); + UART_CHECK((UART[uart_num]->conf1.rx_flow_en != 1), "disable hw flowctrl before using sw control", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->conf0.sw_rts = level & 0x1; UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); @@ -385,7 +395,7 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level) esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((uart_config), "param null\n", ESP_FAIL); + UART_CHECK((uart_config), "param null", ESP_FAIL); if(uart_num == UART_NUM_0) { periph_module_enable(PERIPH_UART0_MODULE); } else if(uart_num == UART_NUM_1) { @@ -407,7 +417,7 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((intr_conf), "param null\n", ESP_FAIL); + UART_CHECK((intr_conf), "param null", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_clr.val = UART_INTR_MASK; if(intr_conf->intr_enable_mask & UART_RXFIFO_TOUT_INT_ENA_M) { @@ -468,17 +478,17 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) while(tx_fifo_rem) { if(p_uart->tx_len_tot == 0 || p_uart->tx_ptr == NULL || p_uart->tx_len_cur == 0) { size_t size; - p_uart->tx_head = (uart_event_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); + p_uart->tx_head = (uart_tx_data_t*) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size); if(p_uart->tx_head) { //The first item is the data description //Get the first item to get the data information if(p_uart->tx_len_tot == 0) { p_uart->tx_ptr = NULL; - p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_len_tot = p_uart->tx_head->tx_data.size; if(p_uart->tx_head->type == UART_DATA_BREAK) { - p_uart->tx_len_tot = p_uart->tx_head->data.size; + p_uart->tx_len_tot = p_uart->tx_head->tx_data.size; p_uart->tx_brk_flg = 1; - p_uart->tx_brk_len = p_uart->tx_head->data.brk_len; + p_uart->tx_brk_len = p_uart->tx_head->tx_data.brk_len; } //We have saved the data description from the 1st item, return buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); @@ -553,7 +563,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.rxfifo_full = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_DATA; - uart_event.data.size = rx_fifo_len; + uart_event.size = rx_fifo_len; //If we fail to push data to ring buffer, we will have to stash the data, and send next time. //Mainly for applications that uses flow control or small ring buffer. if(pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->rx_data_buf, p_uart->rx_stash_len, &HPTaskAwoken)) { @@ -711,9 +721,9 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool if(p_uart_obj[uart_num]->tx_buf_size > 0) { int max_size = xRingbufferGetMaxItemSize(p_uart_obj[uart_num]->tx_ring_buf); int offset = 0; - uart_event_t evt; - evt.data.size = size; - evt.data.brk_len = brk_len; + uart_tx_data_t evt; + evt.tx_data.size = size; + evt.tx_data.brk_len = brk_len; if(brk_en) { evt.type = UART_DATA_BREAK; } else { @@ -882,12 +892,12 @@ esp_err_t uart_flush(uart_port_t uart_num) esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error\n", ESP_FAIL); + UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { ESP_INTR_DISABLE(uart_intr_num); p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); if(p_uart_obj[uart_num] == NULL) { - ESP_LOGE(UART_TAG, "UART driver malloc error\n"); + ESP_LOGE(UART_TAG, "UART driver malloc error"); return ESP_FAIL; } p_uart_obj[uart_num]->uart_num = uart_num; @@ -909,7 +919,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; - ESP_LOGI(UART_TAG, "queue free spaces: %d\n", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); + ESP_LOGI(UART_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); } else { p_uart_obj[uart_num]->xQueueUart = NULL; } @@ -927,7 +937,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->tx_buf_size = 0; } } else { - ESP_LOGE(UART_TAG, "UART driver already installed\n"); + ESP_LOGE(UART_TAG, "UART driver already installed"); return ESP_FAIL; } uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); @@ -951,7 +961,7 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { - ESP_LOGI(UART_TAG, "ALREADY NULL\n"); + ESP_LOGI(UART_TAG, "ALREADY NULL"); return ESP_OK; } ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); From 98a0387854918b73d10e00820e40a36786ee8eab Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Nov 2016 17:54:47 +1100 Subject: [PATCH 143/285] bootloader_support: Move secure boot code to bootloader_support --- .../bootloader/src/main/bootloader_config.h | 1 - .../bootloader/src/main/bootloader_start.c | 7 +- .../{esp_secureboot.h => esp_secure_boot.h} | 24 +++- .../src}/secure_boot.c | 128 +++++++++--------- .../bootloader_support/src/secureboot.c | 7 - 5 files changed, 85 insertions(+), 82 deletions(-) rename components/bootloader_support/include/{esp_secureboot.h => esp_secure_boot.h} (64%) rename components/{bootloader/src/main => bootloader_support/src}/secure_boot.c (71%) delete mode 100644 components/bootloader_support/src/secureboot.c diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 0823581824..4559d5f816 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -60,7 +60,6 @@ typedef struct { } bootloader_state_t; bool flash_encrypt(bootloader_state_t *bs); -bool secure_boot_generate_bootloader_digest(void); #ifdef __cplusplus } diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 3bc2696a4b..59b180fd84 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -34,6 +34,7 @@ #include "sdkconfig.h" #include "esp_image_format.h" +#include "esp_secure_boot.h" #include "bootloader_flash.h" #include "bootloader_config.h" @@ -214,6 +215,7 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); + esp_err_t err; esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; @@ -308,8 +310,9 @@ void bootloader_main() if(fhdr.secure_boot_flag == 0x01) { /* Generate secure digest from this bootloader to protect future modifications */ - if (secure_boot_generate_bootloader_digest() == false){ - ESP_LOGE(TAG, "Bootloader digest generation failed. SECURE BOOT IS NOT ENABLED."); + err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK){ + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); /* Allow booting to continue, as the failure is probably due to user-configured EFUSEs for testing... */ diff --git a/components/bootloader_support/include/esp_secureboot.h b/components/bootloader_support/include/esp_secure_boot.h similarity index 64% rename from components/bootloader_support/include/esp_secureboot.h rename to components/bootloader_support/include/esp_secure_boot.h index b0097df8a6..4bf2dc8b23 100644 --- a/components/bootloader_support/include/esp_secureboot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -16,6 +16,7 @@ #include #include +#include "soc/efuse_reg.h" /* Support functions for secure boot features. @@ -30,21 +31,34 @@ * * @return true if secure boot is enabled. */ -bool esp_secure_boot_enabled(void); +static inline bool esp_secure_boot_enabled(void) { + return REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_RD_ABS_DONE_0); +} -/** @brief Enable secure boot if it isw not already enabled. +/** @brief Enable secure boot if it is not already enabled. * - * @important If this function succeeds, secure boot is permanentl + * @important If this function succeeds, secure boot is permanently * enabled on the chip via efuse. * - * This function is intended to be called from bootloader code. + * @important This function is intended to be called from bootloader code only. + * + * If secure boot is not yet enabled for bootloader, this will + * generate the secure boot digest and enable secure boot by blowing + * the EFUSE_RD_ABS_DONE_0 efuse. + * + * This function does not verify secure boot of the bootloader (the + * ROM bootloader does this.) + * + * Will fail if efuses have been part-burned in a way that indicates + * secure boot should not or could not be correctly enabled. + * * * @return ESP_ERR_INVALID_STATE if efuse state doesn't allow * secure boot to be enabled cleanly. ESP_OK if secure boot * is enabled on this chip from now on. */ -esp_err_t esp_secure_boot_enable(void); +esp_err_t esp_secure_boot_permanently_enable(void); diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader_support/src/secure_boot.c similarity index 71% rename from components/bootloader/src/main/secure_boot.c rename to components/bootloader_support/src/secure_boot.c index 2b1b8573fc..c17aebfbea 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -30,72 +30,81 @@ #include "sdkconfig.h" -#include "bootloader_config.h" #include "bootloader_flash.h" #include "esp_image_format.h" +#include "esp_secure_boot.h" static const char* TAG = "secure_boot"; +#define HASH_BLOCK_SIZE 128 +#define IV_LEN HASH_BLOCK_SIZE +#define DIGEST_LEN 64 + /** * @function : secure_boot_generate - * @description: generate boot abstract & iv + * @description: generate boot digest (aka "abstract") & iv * - * @inputs: bool + * @inputs: image_len - length of image to calculate digest for */ static bool secure_boot_generate(uint32_t image_len){ - SpiFlashOpResult spiRet; - uint32_t buf[32]; + SpiFlashOpResult spiRet; + /* buffer is uint32_t not uint8_t to meet ROM SPI API signature */ + uint32_t buf[IV_LEN / sizeof(uint32_t)]; const void *image; - if (image_len % 128 != 0) { - image_len = (image_len / 128 + 1) * 128; - } - ets_secure_boot_start(); - ets_secure_boot_rd_iv(buf); - ets_secure_boot_hash(NULL); - Cache_Read_Disable(0); - /* iv stored in sec 0 */ - spiRet = SPIEraseSector(0); - if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - Cache_Read_Enable(0); + /* hardware secure boot engine only takes full blocks, so round up the + image length. The additional data should all be 0xFF. + */ + if (image_len % HASH_BLOCK_SIZE != 0) { + image_len = (image_len / HASH_BLOCK_SIZE + 1) * HASH_BLOCK_SIZE; + } + ets_secure_boot_start(); + ets_secure_boot_rd_iv(buf); + ets_secure_boot_hash(NULL); + Cache_Read_Disable(0); + /* iv stored in sec 0 */ + spiRet = SPIEraseSector(0); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, "SPI erase failed %d", spiRet); + return false; + } + Cache_Read_Enable(0); - /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ + /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ ESP_LOGD(TAG, "write iv to flash."); - spiRet = SPIWrite(0, buf, 128); - if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } + spiRet = SPIWrite(0, buf, IV_LEN); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, "SPI write failed %d", spiRet); + return false; + } + bzero(buf, sizeof(buf)); - /* generate digest from image contents */ + /* generate digest from image contents */ image = bootloader_mmap(0x1000, image_len); if (!image) { ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len); return false; } - for (int i = 0; i < image_len; i+=128) { - ets_secure_boot_hash(image + i/sizeof(void *)); - } + for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) { + ets_secure_boot_hash(image + i/sizeof(void *)); + } bootloader_unmap(image); - ets_secure_boot_obtain(); - ets_secure_boot_rd_abstract(buf); - ets_secure_boot_finish(); + ets_secure_boot_obtain(); + ets_secure_boot_rd_abstract(buf); + ets_secure_boot_finish(); - ESP_LOGD(TAG, "write abstract to flash."); - spiRet = SPIWrite(0x80, buf, 64); - if (spiRet != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - ESP_LOGD(TAG, "write abstract to flash."); - Cache_Read_Enable(0); - return true; + ESP_LOGD(TAG, "write digest to flash."); + spiRet = SPIWrite(0x80, buf, DIGEST_LEN); + if (spiRet != SPI_FLASH_RESULT_OK) { + ESP_LOGE(TAG, "SPI write failed %d", spiRet); + return false; + } + ESP_LOGD(TAG, "write digest to flash."); + Cache_Read_Enable(0); + return true; } /* Burn values written to the efuse write registers */ @@ -109,34 +118,19 @@ static inline void burn_efuses() while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ } -/** - * @brief Enable secure boot if it is not already enabled. - * - * Called if the secure boot flag is set on the - * bootloader image in flash. If secure boot is not yet enabled for - * bootloader, this will generate the secure boot digest and enable - * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. - * - * This function does not verify secure boot of the bootloader (the - * ROM bootloader does this.) - * - * @return true if secure boot is enabled (either was already enabled, - * or is freshly enabled as a result of calling this function.) false - * implies an error occured (possibly secure boot is part-enabled.) - */ -bool secure_boot_generate_bootloader_digest(void) { +esp_err_t esp_secure_boot_permanently_enable(void) { esp_err_t err; uint32_t image_len = 0; - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) + if (esp_secure_boot_enabled()) { ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); - return true; + return ESP_OK; } err = esp_image_basic_verify(0x1000, &image_len); if (err != ESP_OK) { ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err); - return false; + return err; } uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG); @@ -178,17 +172,17 @@ bool secure_boot_generate_bootloader_digest(void) { ESP_LOGI(TAG, "Generating secure boot digest..."); if (false == secure_boot_generate(image_len)){ ESP_LOGE(TAG, "secure boot generation failed"); - return false; + return ESP_FAIL; } ESP_LOGI(TAG, "Digest generation complete."); if (!efuse_key_read_protected) { ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); - return false; + return ESP_ERR_INVALID_STATE; } if (!efuse_key_write_protected) { ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse."); - return false; + return ESP_ERR_INVALID_STATE; } ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); @@ -200,9 +194,9 @@ bool secure_boot_generate_bootloader_digest(void) { ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after); if (after & EFUSE_RD_ABS_DONE_0) { ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); - return true; + return ESP_OK; } else { ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); - return false; + return ESP_ERR_INVALID_STATE; } } diff --git a/components/bootloader_support/src/secureboot.c b/components/bootloader_support/src/secureboot.c deleted file mode 100644 index e55c1f33fa..0000000000 --- a/components/bootloader_support/src/secureboot.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -#include "esp_log.h" - - - From fce359b240cc3869f609256701a202c788ba4634 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 6 Oct 2016 12:51:47 +1100 Subject: [PATCH 144/285] build system: Add support for embedded arbitrary binary or text files in .rodata Simplifies examples of embedding a certificate file or a root cert. This is a much cruder mechanism than the full flash filesystem we want eventually, but still sometimes useful. --- docs/build_system.rst | 22 +++++++ examples/04_https_request/main/cert.c | 44 -------------- examples/04_https_request/main/component.mk | 9 ++- .../main/https_request_main.c | 18 +++++- .../main/server_root_cert.pem | 27 +++++++++ make/component_common.mk | 57 ++++++++++++++++--- 6 files changed, 117 insertions(+), 60 deletions(-) delete mode 100644 examples/04_https_request/main/cert.c create mode 100644 examples/04_https_request/main/server_root_cert.pem diff --git a/docs/build_system.rst b/docs/build_system.rst index 34db487e0a..85ebfe46d5 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -280,6 +280,28 @@ component and resides under the component path. Because logo.h is a generated file, it needs to be cleaned when make clean is called which why it is added to the COMPONENT_EXTRA_CLEAN variable. +Embedding Binary Data +===================== + +Sometimes you have a file with some binary or text data that you'd like to make available to your component - but you don't want to reformat the file as C source. + +You can set a variable COMPONENT_EMBED_FILES in component.mk, giving the names of the files to embed in this way:: + + COMPONENT_EMBED_FILES := server_root_cert.der + +Or if the file is a string, you can use the variable COMPONENT_EMBED_TXTFILES. This will embed the contents of the text file as a null-terminated string:: + + COMPONENT_EMBED_TXTFILES := server_root_cert.pem + +The file's contents will be added to the .rodata section in flash, and are available via symbol names as follows:: + + extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start"); + extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end"); + +The names are generated from the full name of the file, as given in COMPONENT_EMBED_FILES. Characters /, ., etc. are replaced with underscores. The _binary prefix in the symbol name is added by objcopy and is the same for both text and binary files. + +For an example of using this technique, see examples/04_https_request - the certificate file contents are loaded from the text .pem file at compile time. + Cosmetic Improvements ===================== diff --git a/examples/04_https_request/main/cert.c b/examples/04_https_request/main/cert.c deleted file mode 100644 index 7acc438ef3..0000000000 --- a/examples/04_https_request/main/cert.c +++ /dev/null @@ -1,44 +0,0 @@ -/* This is the CA certificate for the CA trust chain of - www.howsmyssl.com in PEM format, as dumped via: - - openssl s_client -showcerts -connect www.howsmyssl.com:443 -#include -#include - -/* - 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 - i:/O=Digital Signature Trust Co./CN=DST Root CA X3 - */ -const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n" -"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n" -"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n" -"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n" -"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n" -"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n" -"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n" -"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n" -"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n" -"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n" -"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n" -"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n" -"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n" -"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n" -"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n" -"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n" -"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n" -"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n" -"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n" -"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n" -"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n" -"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n" -"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n" -"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n" -"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n" -"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n" -"-----END CERTIFICATE-----\r\n"; - - diff --git a/examples/04_https_request/main/component.mk b/examples/04_https_request/main/component.mk index 24356f23ed..7fbfcc55d2 100644 --- a/examples/04_https_request/main/component.mk +++ b/examples/04_https_request/main/component.mk @@ -1,10 +1,9 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# + +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := server_root_cert.pem include $(IDF_PATH)/make/component_common.mk diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index 0c8b2463f8..7f302409d8 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -74,8 +74,18 @@ static const char *REQUEST = "GET " WEB_URL " HTTP/1.1\n" "User-Agent: esp-idf/1.0 esp32\n" "\n"; -/* Root cert for howsmyssl.com, found in cert.c */ -extern const char *server_root_cert; +/* Root cert for howsmyssl.com, taken from server_root_cert.pem + + The PEM file was extracted from the output of this command: + openssl s_client -showcerts -connect www.howsmyssl.com:443 > $$(notdir $$<) ) + $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ + $$(Q) rm $$(notdir $$<) +endef + +# generate targets to embed binary & text files +$(foreach binfile,$(COMPONENT_EMBED_FILES), $(eval $(call GenerateEmbedTarget,$(binfile),bin))) + +$(foreach txtfile,$(COMPONENT_EMBED_TXTFILES), $(eval $(call GenerateEmbedTarget,$(txtfile),txt))) + +# generate targets to create binary embed directories +$(foreach bindir,$(sort $(dir $(COMPONENT_EMBED_FILES))), $(eval $(call GenerateBuildDirTarget,$(bindir)))) From d12078b6926dcb72a5f31e6a0e43cf2f036b04e0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 11:55:48 +0800 Subject: [PATCH 145/285] Revert "Merge branch 'feature/ci_checkout_same_branch_for_submodule' into 'master'" This reverts merge request !178 --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2f65fdfc00..1e09b55c44 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,14 +13,12 @@ before_script: - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config + # if testing master branch, use github wifi libs. # if testing other branches, use gitlab wifi libs (as maybe changes aren't merged to master yet) - test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-wifi-lib%ssh://git@gitlab.espressif.cn:27227/idf/esp32-wifi-lib%" .gitmodules # fetch all submodules - git submodule update --init --recursive - # try use submodule with same branch - - SUBMODULES=`cat .gitmodules | grep path | awk '{print $3}'` - - for MODULE in $SUBMODULES;do (echo $MODULE;cd $MODULE;git checkout ${CI_BUILD_REF_NAME} || echo "using default branch";cd $CI_PROJECT_DIR); done build_template_app: stage: build From 178db4bdd91c37b2fd633930c8df44233b496e1c Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 8 Nov 2016 13:30:35 +0800 Subject: [PATCH 146/285] esp32: merge bt/wifi coexist code 1. Modify makefile to add coexist lib 2. Update bt/wifi coexist lib --- components/esp32/component.mk | 2 +- components/esp32/lib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/component.mk b/components/esp32/component.mk index e91020c3e6..c658787d87 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -10,7 +10,7 @@ COMPONENT_SRCDIRS := . hwcrypto -LIBS := core net80211 phy rtc pp wpa smartconfig +LIBS := core net80211 phy rtc pp wpa smartconfig coexist LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld diff --git a/components/esp32/lib b/components/esp32/lib index b3090d8854..a1ab74c2a6 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit b3090d885413fb78c86e7b88116cdb5c8c5e9e68 +Subproject commit a1ab74c2a6122693ee87f08306364fc7a016ced7 From 1418f886eb5c04913923daaf89c36b85daea59b9 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 8 Nov 2016 14:19:31 +0800 Subject: [PATCH 147/285] Only fix rmt_mem_t struct definition error. --- components/esp32/include/soc/rmt_struct.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/esp32/include/soc/rmt_struct.h b/components/esp32/include/soc/rmt_struct.h index fb4c21055e..511fefa267 100644 --- a/components/esp32/include/soc/rmt_struct.h +++ b/components/esp32/include/soc/rmt_struct.h @@ -231,11 +231,10 @@ typedef volatile struct { struct { union { struct { - uint32_t level1: 1; - uint32_t duration1: 15; - uint32_t level0: 1; uint32_t duration0: 15; - + uint32_t level0: 1; + uint32_t duration1: 15; + uint32_t level1: 1; }; uint32_t val; } data[64]; From 7340a77689943629d4364fef787272da7fffb768 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 8 Nov 2016 15:28:20 +0800 Subject: [PATCH 148/285] esp32: remove libcrypto according to review comments --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index a1ab74c2a6..596a82d4e0 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a1ab74c2a6122693ee87f08306364fc7a016ced7 +Subproject commit 596a82d4e0122432a51d3ec5a62975db9fd38179 From cb5f7690a4ab654a5a5e0a60a59e3acc99cc31ab Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 8 Nov 2016 17:34:46 +0800 Subject: [PATCH 149/285] esp32/make: add detailed return error code for wifi APIs 1. Add detailed return error code for wifi APIs 2. Add description about error code in esp_wifi.h 3. Modify esp_wifi_reg_rxcb to esp_wifi_internal_reg_rxcb 4. Modify esp_wifi_set_sta_ip to esp_wifi_internal_set_sta_ip 5. Mark system_init as deprecated API --- components/esp32/event_default_handlers.c | 12 +- components/esp32/include/esp_err.h | 2 + components/esp32/include/esp_system.h | 10 +- components/esp32/include/esp_wifi.h | 291 ++++++++++++------- components/esp32/include/esp_wifi_internal.h | 33 ++- components/esp32/lib | 2 +- make/project.mk | 3 +- 7 files changed, 241 insertions(+), 112 deletions(-) diff --git a/components/esp32/event_default_handlers.c b/components/esp32/event_default_handlers.c index 37ba634041..a2bb3ccea3 100644 --- a/components/esp32/event_default_handlers.c +++ b/components/esp32/event_default_handlers.c @@ -18,6 +18,7 @@ #include "esp_err.h" #include "esp_wifi.h" +#include "esp_wifi_internal.h" #include "esp_event.h" #include "esp_event_loop.h" #include "esp_task.h" @@ -76,8 +77,7 @@ static system_event_handle_t g_system_event_handle_table[] = { static esp_err_t system_event_sta_got_ip_default(system_event_t *event) { - extern esp_err_t esp_wifi_set_sta_ip(void); - WIFI_API_CALL_CHECK("esp_wifi_set_sta_ip", esp_wifi_set_sta_ip(), ESP_OK); + WIFI_API_CALL_CHECK("esp_wifi_internal_set_sta_ip", esp_wifi_internal_set_sta_ip(), ESP_OK); ESP_LOGI(TAG, "ip: " IPSTR ", mask: " IPSTR ", gw: " IPSTR, IP2STR(&event->event_info.got_ip.ip_info.ip), @@ -92,7 +92,7 @@ esp_err_t system_event_ap_start_handle_default(system_event_t *event) tcpip_adapter_ip_info_t ap_ip; uint8_t ap_mac[6]; - WIFI_API_CALL_CHECK("esp_wifi_reg_rxcb", esp_wifi_reg_rxcb(WIFI_IF_AP, (wifi_rxcb_t)tcpip_adapter_ap_input), ESP_OK); + WIFI_API_CALL_CHECK("esp_wifi_internal_reg_rxcb", esp_wifi_internal_reg_rxcb(WIFI_IF_AP, (wifi_rxcb_t)tcpip_adapter_ap_input), ESP_OK); WIFI_API_CALL_CHECK("esp_wifi_mac_get", esp_wifi_get_mac(WIFI_IF_AP, ap_mac), ESP_OK); tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ap_ip); @@ -103,7 +103,7 @@ esp_err_t system_event_ap_start_handle_default(system_event_t *event) esp_err_t system_event_ap_stop_handle_default(system_event_t *event) { - WIFI_API_CALL_CHECK("esp_wifi_reg_rxcb", esp_wifi_reg_rxcb(WIFI_IF_AP, NULL), ESP_OK); + WIFI_API_CALL_CHECK("esp_wifi_internal_reg_rxcb", esp_wifi_internal_reg_rxcb(WIFI_IF_AP, NULL), ESP_OK); tcpip_adapter_stop(TCPIP_ADAPTER_IF_AP); @@ -133,7 +133,7 @@ esp_err_t system_event_sta_connected_handle_default(system_event_t *event) { tcpip_adapter_dhcp_status_t status; - WIFI_API_CALL_CHECK("esp_wifi_reg_rxcb", esp_wifi_reg_rxcb(WIFI_IF_STA, (wifi_rxcb_t)tcpip_adapter_sta_input), ESP_OK); + WIFI_API_CALL_CHECK("esp_wifi_internal_reg_rxcb", esp_wifi_internal_reg_rxcb(WIFI_IF_STA, (wifi_rxcb_t)tcpip_adapter_sta_input), ESP_OK); tcpip_adapter_up(TCPIP_ADAPTER_IF_STA); @@ -165,7 +165,7 @@ esp_err_t system_event_sta_connected_handle_default(system_event_t *event) esp_err_t system_event_sta_disconnected_handle_default(system_event_t *event) { tcpip_adapter_down(TCPIP_ADAPTER_IF_STA); - WIFI_API_CALL_CHECK("esp_wifi_reg_rxcb", esp_wifi_reg_rxcb(WIFI_IF_STA, NULL), ESP_OK); + WIFI_API_CALL_CHECK("esp_wifi_internal_reg_rxcb", esp_wifi_internal_reg_rxcb(WIFI_IF_STA, NULL), ESP_OK); return ESP_OK; } diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index af9d2ec33a..dc2c6c2d77 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -34,6 +34,8 @@ typedef int32_t esp_err_t; #define ESP_ERR_INVALID_SIZE 0x104 #define ESP_ERR_NOT_FOUND 0x105 +#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ + /** * Macro which can be used to check the error code, * and terminate the program in case the code is not ESP_OK. diff --git a/components/esp32/include/esp_system.h b/components/esp32/include/esp_system.h index 8c6564c55b..d416e23d07 100644 --- a/components/esp32/include/esp_system.h +++ b/components/esp32/include/esp_system.h @@ -16,7 +16,6 @@ #define __ESP_SYSTEM_H__ #include -#include #include "esp_err.h" #include "esp_deepsleep.h" @@ -33,6 +32,13 @@ extern "C" { * @{ */ +/** + * @attention application don't need to call this function anymore. It do nothing and will + * be removed in future version. + */ +void system_init(void) __attribute__ ((deprecated)); + + /** * @brief Get information of the SDK version. * @@ -170,8 +176,6 @@ bool system_rtc_mem_write(uint16_t dst, const void *src, uint16_t n); esp_err_t system_efuse_read_mac(uint8_t mac[6]); -void system_init(void); - /** * @} */ diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 80ced5dc61..8e2c1df558 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -70,6 +70,23 @@ extern "C" { #endif +#define ESP_ERR_WIFI_OK ESP_OK /*!< No error */ +#define ESP_ERR_WIFI_FAIL ESP_FAIL /*!< General fail code */ +#define ESP_ERR_WIFI_NO_MEM ESP_ERR_NO_MEM /*!< Out of memory */ +#define ESP_ERR_WIFI_ARG ESP_ERR_INVALID_ARG /*!< Invalid argument */ +#define ESP_ERR_WIFI_NOT_INIT (ESP_ERR_WIFI_BASE + 1) /*!< WiFi driver is not installed by esp_wifi_init */ +#define ESP_ERR_WIFI_NOT_START (ESP_ERR_WIFI_BASE + 2) /*!< WiFi driver is not started by esp_wifi_start */ +#define ESP_ERR_WIFI_NOT_SUPPORT (ESP_ERR_WIFI_BASE + 3) /*!< WiFi API doesn't support */ +#define ESP_ERR_WIFI_IF (ESP_ERR_WIFI_BASE + 4) /*!< WiFi interface error */ +#define ESP_ERR_WIFI_MODE (ESP_ERR_WIFI_BASE + 5) /*!< WiFi mode error */ +#define ESP_ERR_WIFI_STATE (ESP_ERR_WIFI_BASE + 6) /*!< WiFi internal state error */ +#define ESP_ERR_WIFI_CONN (ESP_ERR_WIFI_BASE + 7) /*!< WiFi internal control block of station or soft-AP error */ +#define ESP_ERR_WIFI_NVS (ESP_ERR_WIFI_BASE + 8) /*!< WiFi internal NVS module error */ +#define ESP_ERR_WIFI_MAC (ESP_ERR_WIFI_BASE + 9) /*!< MAC address is invalid */ +#define ESP_ERR_WIFI_SSID (ESP_ERR_WIFI_BASE + 10) /*!< SSID is invalid */ +#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 11) /*!< Passord is invalid */ +#define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 12) /*!< Timeout error */ + typedef struct { system_event_handler_t event_handler; /**< WiFi event handler */ } wifi_init_config_t; @@ -92,8 +109,10 @@ typedef struct { * * @param wifi_init_config_t *config : provide WiFi init configuration * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NO_MEM : out of memory + * - others : refer to error code esp_err.h */ esp_err_t esp_wifi_init(wifi_init_config_t *config); @@ -104,7 +123,6 @@ esp_err_t esp_wifi_init(wifi_init_config_t *config); * @attention 1. This API should be called if you want to remove WiFi driver from the system * * @return ESP_OK : succeed - * @return others : fail */ esp_err_t esp_wifi_deinit(void); @@ -116,8 +134,11 @@ esp_err_t esp_wifi_deinit(void); * * @param wifi_mode_t mode : WiFi operating modes: * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - others : refer to error code in esp_err.h */ esp_err_t esp_wifi_set_mode(wifi_mode_t mode); @@ -126,8 +147,10 @@ esp_err_t esp_wifi_set_mode(wifi_mode_t mode); * * @param wifi_mode_t *mode : store current WiFi mode * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_get_mode(wifi_mode_t *mode); @@ -139,8 +162,13 @@ esp_err_t esp_wifi_get_mode(wifi_mode_t *mode); * * @param null * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_NO_MEM : out of memory + * - ESP_ERR_WIFI_CONN : WiFi internal error, station or soft-AP control block wrong + * - ESP_ERR_WIFI_FAIL : other WiFi internal errors */ esp_err_t esp_wifi_start(void); @@ -152,8 +180,9 @@ esp_err_t esp_wifi_start(void); * * @param null * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_stop(void); @@ -165,8 +194,12 @@ esp_err_t esp_wifi_stop(void); * * @param null * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_CONN : WiFi internal error, station or soft-AP control block wrong + * - ESP_ERR_WIFI_SSID : SSID of AP which station connects is invalid */ esp_err_t esp_wifi_connect(void); @@ -175,8 +208,11 @@ esp_err_t esp_wifi_connect(void); * * @param null * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_FAIL : other WiFi internal errors */ esp_err_t esp_wifi_disconnect(void); @@ -185,8 +221,9 @@ esp_err_t esp_wifi_disconnect(void); * * @param null * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - others : fail */ esp_err_t esp_wifi_clear_fast_connect(void); @@ -195,8 +232,12 @@ esp_err_t esp_wifi_clear_fast_connect(void); * * @param uint16_t aid : when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_MODE : WiFi mode is wrong */ esp_err_t esp_wifi_deauth_sta(uint16_t aid); @@ -211,8 +252,12 @@ esp_err_t esp_wifi_deauth_sta(uint16_t aid); * @param bool block : if block is true, this API will block the caller until the scan is done, otherwise * it will return immediately * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_TIMEOUT : blocking scan is timeout + * - others : refer to error code in esp_err.h */ esp_err_t esp_wifi_scan_start(wifi_scan_config_t *conf, bool block); @@ -220,8 +265,10 @@ esp_err_t esp_wifi_scan_start(wifi_scan_config_t *conf, bool block); * @brief Stop the scan in process * * @param null - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start */ esp_err_t esp_wifi_scan_stop(void); @@ -232,8 +279,11 @@ esp_err_t esp_wifi_scan_stop(void); * * @attention This API can only be called when the scan is completed, otherwise it may get wrong value * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); @@ -244,8 +294,12 @@ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); the actual AP number this API returns * @param wifi_ap_record_t *ap_records: wifi_ap_record_t array to hold the found APs * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_NO_MEM : out of memory */ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records); @@ -255,8 +309,9 @@ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_re * * @param wifi_ap_record_t *ap_info: the wifi_ap_record_t to hold station assocated AP * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - others : fail */ esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); @@ -265,8 +320,7 @@ esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); * * @param wifi_ps_type_t type : power save type * - * @return ESP_OK : succeed - * @return others : fail + * @return ESP_ERR_WIFI_NOT_SUPPORT : not support yet */ esp_err_t esp_wifi_set_ps(wifi_ps_type_t type); @@ -275,8 +329,7 @@ esp_err_t esp_wifi_set_ps(wifi_ps_type_t type); * * @param wifi_ps_type_t *type : store current power save type * - * @return ESP_OK : succeed - * @return others : fail + * @return ESP_ERR_WIFI_NOT_SUPPORT : not support yet */ esp_err_t esp_wifi_get_ps(wifi_ps_type_t *type); @@ -289,8 +342,11 @@ esp_err_t esp_wifi_get_ps(wifi_ps_type_t *type); * @param wifi_interface_t ifx : interfaces * @param uint8_t protocol : WiFi protocol bitmap * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF : invalid interface + * - others : refer to erro code in esp_err.h */ esp_err_t esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap); @@ -300,8 +356,12 @@ esp_err_t esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap); * @param wifi_interface_t ifx : interfaces * @param uint8_t protocol : store current WiFi protocol bitmap of interface ifx * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_ERR_WIFI_ARG : invalid argument + * - others : refer to error code in esp_err.h */ esp_err_t esp_wifi_get_protocol(wifi_interface_t ifx, uint8_t *protocol_bitmap); @@ -314,8 +374,12 @@ esp_err_t esp_wifi_get_protocol(wifi_interface_t ifx, uint8_t *protocol_bitmap); * @param wifi_interface_t ifx : interface to be configured * @param wifi_bandwidth_t bw : bandwidth * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_ERR_WIFI_ARG : invalid argument + * - others : refer to error code in esp_err.h */ esp_err_t esp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw); @@ -327,8 +391,11 @@ esp_err_t esp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw); * @param wifi_interface_t ifx : interface to be configured * @param wifi_bandwidth_t *bw : store bandwidth of interface ifx * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw); @@ -340,8 +407,11 @@ esp_err_t esp_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw); * @param uint8_t primary : for HT20, primary is the channel number, for HT40, primary is the primary channel * @param wifi_second_chan_t second : for HT20, second is ignored, for HT40, second is the second channel * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_set_channel(uint8_t primary, wifi_second_chan_t second); @@ -353,8 +423,10 @@ esp_err_t esp_wifi_set_channel(uint8_t primary, wifi_second_chan_t second); * @param uint8_t *primary : store current primary channel * @param wifi_second_chan_t *second : store current second channel * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second); @@ -364,8 +436,11 @@ esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second); * * @param wifi_country_t country : country type * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - others : refer to error code in esp_err.h */ esp_err_t esp_wifi_set_country(wifi_country_t country); @@ -374,8 +449,10 @@ esp_err_t esp_wifi_set_country(wifi_country_t country); * * @param wifi_country_t country : store current country * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_get_country(wifi_country_t *country); @@ -390,8 +467,14 @@ esp_err_t esp_wifi_get_country(wifi_country_t *country); * @param wifi_interface_t ifx : interface * @param uint8 mac[6]: the MAC address. * - * @return true : succeed - * @return false : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_ERR_WIFI_MAC : invalid mac address + * - ESP_ERR_WIFI_MODE : WiFi mode is wrong + * - others : refer to error code in esp_err.h */ esp_err_t esp_wifi_set_mac(wifi_interface_t ifx, uint8_t mac[6]); @@ -400,8 +483,11 @@ esp_err_t esp_wifi_set_mac(wifi_interface_t ifx, uint8_t mac[6]); * * @param uint8_t mac[6] : store mac of this interface ifx * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_IF : invalid interface */ esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]); @@ -413,8 +499,7 @@ esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]); * @param void *buf : the data received * @param uint16_t len : data length * - * @return ESP_OK : succeed - * @return others : fail + * @return none */ typedef void (* wifi_promiscuous_cb_t)(void *buf, uint16_t len); @@ -425,8 +510,9 @@ typedef void (* wifi_promiscuous_cb_t)(void *buf, uint16_t len); * * @param wifi_promiscuous_cb_t cb : callback * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); @@ -435,8 +521,9 @@ esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); * * @param bool promiscuous : false - disable / true - enable * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_promiscuous(bool en); @@ -445,8 +532,10 @@ esp_err_t esp_wifi_set_promiscuous(bool en); * * @param bool *enable : store the current status of promiscuous mode * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_get_promiscuous(bool *en); @@ -461,8 +550,15 @@ esp_err_t esp_wifi_get_promiscuous(bool *en); * @param wifi_interface_t ifx : interface * @param wifi_config_t *conf : station or soft-AP configuration * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_ERR_WIFI_MODE : invalid mode + * - ESP_ERR_WIFI_PASSWORD : invalid password + * - ESP_ERR_WIFI_NVS : WiFi internal NVS error + * - others : refer to the erro code in esp_err.h */ esp_err_t esp_wifi_set_config(wifi_interface_t ifx, wifi_config_t *conf); @@ -472,8 +568,11 @@ esp_err_t esp_wifi_set_config(wifi_interface_t ifx, wifi_config_t *conf); * @param wifi_interface_t ifx : interface * @param wifi_config_t *conf : station or soft-AP configuration * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_IF : invalid interface */ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); @@ -484,8 +583,12 @@ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); * * @param wifi_sta_list_t *sta: station list * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_MODE : WiFi mode is wrong + * - ESP_ERR_WIFI_CONN : WiFi internal error, the station/soft-AP control block is invalid */ esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); @@ -497,42 +600,24 @@ esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); * * @param wifi_storage_t storage : storage type * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_set_storage(wifi_storage_t storage); -/** - * @brief The WiFi RX callback function - * - * Each time the WiFi need to forward the packets to high layer, the callback function will be called - * - */ -typedef esp_err_t (*wifi_rxcb_t)(void *buffer, uint16_t len, void *eb); - -/** - * @brief Set the WiFi RX callback - * - * @attention 1. Currently we support only one RX callback for each interface - * - * @param wifi_interface_t ifx : interface - * @param wifi_rxcb_t fn : WiFi RX callback - * - * @return ESP_OK : succeed - * @return others : fail - */ -esp_err_t esp_wifi_reg_rxcb(wifi_interface_t ifx, wifi_rxcb_t fn); - /** * @brief Set auto connect * The default value is true * - * @attention 1. - * * @param bool en : true - enable auto connect / false - disable auto connect * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_MODE : WiFi internal error, the station/soft-AP control block is invalid + * - others : refer to error code in esp_err.h */ esp_err_t esp_wifi_set_auto_connect(bool en); @@ -541,8 +626,10 @@ esp_err_t esp_wifi_set_auto_connect(bool en); * * @param bool *en : store current auto connect configuration * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument */ esp_err_t esp_wifi_get_auto_connect(bool *en); @@ -559,8 +646,11 @@ esp_err_t esp_wifi_get_auto_connect(bool *en); 1 - WIFI_VND_IE_ID_1 * @param uint8_t *vnd_ie : pointer to a vendor specific element * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_ERR_WIFI_NO_MEM : out of memory */ esp_err_t esp_wifi_set_vendor_ie(bool enable, wifi_vendor_ie_type_t type, wifi_vendor_ie_id_t idx, uint8_t *vnd_ie); @@ -584,8 +674,9 @@ typedef void (*esp_vendor_ie_cb_t) (void *ctx, wifi_vendor_ie_type_t type, const * @param esp_vendor_ie_cb_t cb : callback function * @param void *ctx : reserved * - * @return ESP_OK : succeed - * @return others : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_vendor_ie_cb(esp_vendor_ie_cb_t cb, void *ctx); diff --git a/components/esp32/include/esp_wifi_internal.h b/components/esp32/include/esp_wifi_internal.h index 217d5f6d1f..9aa05dc716 100644 --- a/components/esp32/include/esp_wifi_internal.h +++ b/components/esp32/include/esp_wifi_internal.h @@ -71,7 +71,38 @@ void esp_wifi_internal_free_rx_buffer(void* buffer); * @return True : success transmit the buffer to wifi driver * False : failed to transmit the buffer to wifi driver */ -bool esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buffer, u16_t len); +int esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buffer, u16_t len); + +/** + * @brief The WiFi RX callback function + * + * Each time the WiFi need to forward the packets to high layer, the callback function will be called + * + */ +typedef esp_err_t (*wifi_rxcb_t)(void *buffer, uint16_t len, void *eb); + +/** + * @brief Set the WiFi RX callback + * + * @attention 1. Currently we support only one RX callback for each interface + * + * @param wifi_interface_t ifx : interface + * @param wifi_rxcb_t fn : WiFi RX callback + * + * @return ESP_OK : succeed + * @return others : fail + */ +esp_err_t esp_wifi_internal_reg_rxcb(wifi_interface_t ifx, wifi_rxcb_t fn); + +/** + * @brief Notify WIFI driver that the station got ip successfully + * + * @param none + * + * @return ESP_OK : succeed + * @return others : fail + */ +esp_err_t esp_wifi_internal_set_sta_ip(void); #ifdef __cplusplus } diff --git a/components/esp32/lib b/components/esp32/lib index 596a82d4e0..66236c85b3 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 596a82d4e0122432a51d3ec5a62975db9fd38179 +Subproject commit 66236c85b3ef1a6d756d4e7d58cc6a1180e31365 diff --git a/make/project.mk b/make/project.mk index 67e2e92bdb..a60a3958fe 100644 --- a/make/project.mk +++ b/make/project.mk @@ -166,7 +166,8 @@ CPPFLAGS := -DESP_PLATFORM $(CPPFLAGS) COMMON_WARNING_FLAGS = -Wall -Werror \ -Wno-error=unused-function \ -Wno-error=unused-but-set-variable \ - -Wno-error=unused-variable + -Wno-error=unused-variable \ + -Wno-error=deprecated-declarations # Flags which control code generation and dependency generation, both for C and C++ COMMON_FLAGS = \ From 8dee0a3f6d5a0bed87ed1da032cd394703e66a83 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 8 Nov 2016 19:19:28 +0800 Subject: [PATCH 150/285] esp32: modify the code according to review comments 1. Adjust error code definition 2. Refractor the comments for esp_wifi_internal_tx --- components/esp32/include/esp_err.h | 1 + components/esp32/include/esp_wifi.h | 21 ++++++++++---------- components/esp32/include/esp_wifi_internal.h | 9 +++++---- components/esp32/lib | 2 +- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index dc2c6c2d77..f8271ba259 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -33,6 +33,7 @@ typedef int32_t esp_err_t; #define ESP_ERR_INVALID_STATE 0x103 #define ESP_ERR_INVALID_SIZE 0x104 #define ESP_ERR_NOT_FOUND 0x105 +#define ESP_ERR_NOT_SUPPORTED 0x106 #define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 8e2c1df558..88ea0d9a65 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -74,18 +74,19 @@ extern "C" { #define ESP_ERR_WIFI_FAIL ESP_FAIL /*!< General fail code */ #define ESP_ERR_WIFI_NO_MEM ESP_ERR_NO_MEM /*!< Out of memory */ #define ESP_ERR_WIFI_ARG ESP_ERR_INVALID_ARG /*!< Invalid argument */ +#define ESP_ERR_WIFI_NOT_SUPPORT ESP_ERR_NOT_SUPPORTED /*!< Indicates that API is not supported yet */ + #define ESP_ERR_WIFI_NOT_INIT (ESP_ERR_WIFI_BASE + 1) /*!< WiFi driver is not installed by esp_wifi_init */ #define ESP_ERR_WIFI_NOT_START (ESP_ERR_WIFI_BASE + 2) /*!< WiFi driver is not started by esp_wifi_start */ -#define ESP_ERR_WIFI_NOT_SUPPORT (ESP_ERR_WIFI_BASE + 3) /*!< WiFi API doesn't support */ -#define ESP_ERR_WIFI_IF (ESP_ERR_WIFI_BASE + 4) /*!< WiFi interface error */ -#define ESP_ERR_WIFI_MODE (ESP_ERR_WIFI_BASE + 5) /*!< WiFi mode error */ -#define ESP_ERR_WIFI_STATE (ESP_ERR_WIFI_BASE + 6) /*!< WiFi internal state error */ -#define ESP_ERR_WIFI_CONN (ESP_ERR_WIFI_BASE + 7) /*!< WiFi internal control block of station or soft-AP error */ -#define ESP_ERR_WIFI_NVS (ESP_ERR_WIFI_BASE + 8) /*!< WiFi internal NVS module error */ -#define ESP_ERR_WIFI_MAC (ESP_ERR_WIFI_BASE + 9) /*!< MAC address is invalid */ -#define ESP_ERR_WIFI_SSID (ESP_ERR_WIFI_BASE + 10) /*!< SSID is invalid */ -#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 11) /*!< Passord is invalid */ -#define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 12) /*!< Timeout error */ +#define ESP_ERR_WIFI_IF (ESP_ERR_WIFI_BASE + 3) /*!< WiFi interface error */ +#define ESP_ERR_WIFI_MODE (ESP_ERR_WIFI_BASE + 4) /*!< WiFi mode error */ +#define ESP_ERR_WIFI_STATE (ESP_ERR_WIFI_BASE + 5) /*!< WiFi internal state error */ +#define ESP_ERR_WIFI_CONN (ESP_ERR_WIFI_BASE + 6) /*!< WiFi internal control block of station or soft-AP error */ +#define ESP_ERR_WIFI_NVS (ESP_ERR_WIFI_BASE + 7) /*!< WiFi internal NVS module error */ +#define ESP_ERR_WIFI_MAC (ESP_ERR_WIFI_BASE + 8) /*!< MAC address is invalid */ +#define ESP_ERR_WIFI_SSID (ESP_ERR_WIFI_BASE + 9) /*!< SSID is invalid */ +#define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 10) /*!< Passord is invalid */ +#define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 11) /*!< Timeout error */ typedef struct { system_event_handler_t event_handler; /**< WiFi event handler */ diff --git a/components/esp32/include/esp_wifi_internal.h b/components/esp32/include/esp_wifi_internal.h index 9aa05dc716..2015b5063d 100644 --- a/components/esp32/include/esp_wifi_internal.h +++ b/components/esp32/include/esp_wifi_internal.h @@ -62,14 +62,15 @@ void esp_wifi_internal_free_rx_buffer(void* buffer); /** * @brief transmit the buffer via wifi driver * - * @attention1 TODO should modify the return type from bool to int - * * @param wifi_interface_t wifi_if : wifi interface id * @param void *buffer : the buffer to be tansmit * @param u16_t len : the length of buffer * - * @return True : success transmit the buffer to wifi driver - * False : failed to transmit the buffer to wifi driver + * @return + * - ERR_OK : Successfully transmit the buffer to wifi driver + * - ERR_MEM : Out of memory + * - ERR_IF : WiFi driver error + * - ERR_ARG : Invalid argument */ int esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buffer, u16_t len); diff --git a/components/esp32/lib b/components/esp32/lib index 66236c85b3..76f9109806 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 66236c85b3ef1a6d756d4e7d58cc6a1180e31365 +Subproject commit 76f91098061b0052fe1bb67e85001014f39b84a0 From bdd7fb7a137c4b6e2bacc95976f6c901f590e9ed Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Mon, 7 Nov 2016 14:59:35 +0800 Subject: [PATCH 151/285] Add wps API --- components/esp32/component.mk | 2 +- components/esp32/include/esp_wps.h | 131 +++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 components/esp32/include/esp_wps.h diff --git a/components/esp32/component.mk b/components/esp32/component.mk index c658787d87..e483b58ee7 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -10,7 +10,7 @@ COMPONENT_SRCDIRS := . hwcrypto -LIBS := core net80211 phy rtc pp wpa smartconfig coexist +LIBS := core net80211 phy rtc pp wpa smartconfig coexist wps LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h new file mode 100644 index 0000000000..1588d36191 --- /dev/null +++ b/components/esp32/include/esp_wps.h @@ -0,0 +1,131 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_WPS_H__ +#define __ESP_WPS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup WiFi_APIs WiFi Related APIs + * @brief WiFi APIs + */ + +/** @addtogroup WiFi_APIs + * @{ + */ + +/** \defgroup WPS_APIs WPS APIs + * @brief ESP32 WPS APIs + * + * WPS can only be used when ESP32 station is enabled. + * + */ + +/** @addtogroup WPS_APIs + * @{ + */ + +typedef enum wps_type { + WPS_TYPE_DISABLE = 0, + WPS_TYPE_PBC, + WPS_TYPE_PIN, + WPS_TYPE_DISPLAY, + WPS_TYPE_MAX, +} WPS_TYPE_t; + +enum wps_cb_status { + WPS_CB_ST_SUCCESS = 0, /**< WPS succeed */ + WPS_CB_ST_FAILED, /**< WPS fail */ + WPS_CB_ST_TIMEOUT, /**< WPS timeout, fail */ + WPS_CB_ST_WEP, /**< WPS failed because that WEP is not supported */ + WPS_CB_ST_SCAN_ERR, /**< can not find the target WPS AP */ +}; + +/** + * @brief Enable Wi-Fi WPS function. + * + * @attention WPS can only be used when ESP32 station is enabled. + * + * @param WPS_TYPE_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported + * + * @return true : succeed + * @return false : fail + */ +bool esp_wifi_wps_enable(WPS_TYPE_t wps_type); + +/** + * @brief Disable Wi-Fi WPS function and release resource it taken. + * + * @param null + * + * @return true : succeed + * @return false : fail + */ +bool esp_wifi_wps_disable(void); + +/** + * @brief WPS starts to work. + * + * @attention WPS can only be used when ESP32 station is enabled. + * + * @param null + * + * @return true : WPS starts to work successfully, but does not mean WPS succeed. + * @return false : fail + */ +bool esp_wifi_wps_start(void); + +/** + * @brief WPS callback. + * + * @param int status : status of WPS, enum wps_cb_status. + * - If parameter status == WPS_CB_ST_SUCCESS in WPS callback, it means WPS got AP's + * information, user can call wifi_wps_disable to disable WPS and release resource, + * then call wifi_station_connect to connect to target AP. + * - Otherwise, it means that WPS fail, user can create a timer to retry WPS by + * wifi_wps_start after a while, or call wifi_wps_disable to disable WPS and release resource. + * + * @return null + */ +typedef void (*wps_st_cb_t)(int status); + +/** + * @brief Set WPS callback. + * + * @attention WPS can only be used when ESP32 station is enabled. + * + * @param wps_st_cb_t cb : callback. + * + * @return true : WPS starts to work successfully, but does not mean WPS succeed. + * @return false : fail + */ +bool esp_wifi_set_wps_cb(wps_st_cb_t cb); + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_WPS_H__ */ From 13b3c916f306ab97877c5331cc9a15a6296616f7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 7 Nov 2016 14:26:21 +0800 Subject: [PATCH 152/285] vfs: check error code returned by FS driver open function Fixes https://github.com/espressif/esp-idf/issues/78 --- components/vfs/vfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index bf26968ff7..b60c60a818 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -151,6 +151,10 @@ int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode) const char* path_within_vfs = translate_path(vfs, path); int ret; CHECK_AND_CALL(ret, r, vfs, open, path_within_vfs, flags, mode); + if (ret < 0) { + return ret; + } + assert(ret >= vfs->vfs.fd_offset); return ret - vfs->vfs.fd_offset + (vfs->offset << VFS_INDEX_S); } From aa0cd0ab474e6ba27dced833ef5ea440a3eabeb2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 09:02:30 +0800 Subject: [PATCH 153/285] spi_flash: add missing volatile qualifier for lock flags http://esp32.com/viewtopic.php?f=14&t=419&p=1901 --- components/spi_flash/cache_utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index 6ae47bdb3e..904007b316 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -38,8 +38,8 @@ static uint32_t s_flash_op_cache_state[2]; #ifndef CONFIG_FREERTOS_UNICORE static SemaphoreHandle_t s_flash_op_mutex; -static bool s_flash_op_can_start = false; -static bool s_flash_op_complete = false; +static volatile bool s_flash_op_can_start = false; +static volatile bool s_flash_op_complete = false; void spi_flash_init_lock() { From 3f8d9d71e2c10e5ebdf3b07f3d527d74411bc35a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 09:05:05 +0800 Subject: [PATCH 154/285] lwip: fix duplicate definition of O_NONBLOCK LwIP will define O_NONBLOCK in sockets.h if it isn't defined yet. If sys/fcntl.h is included after socket.h, there will be duplicate definition. Work around by including sys/fcntl.h into lwipopts.h. https://github.com/espressif/esp-idf/issues/75 --- components/lwip/include/lwip/port/lwipopts.h | 1 + 1 file changed, 1 insertion(+) diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index f705887508..d06e756850 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -35,6 +35,7 @@ #include #include #include +#include #include "esp_task.h" #include "sdkconfig.h" From 6dd3681115668fc18a7a13624ffe7c5e60a3c513 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 09:08:23 +0800 Subject: [PATCH 155/285] fix order of creation of standard streams With existing order, file descriptors assigned to stdin, stdout, stderr didn't match standard assignment. https://github.com/espressif/esp-idf/issues/67 --- components/esp32/cpu_start.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 2688cd7f81..a96fdee950 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -177,9 +177,9 @@ void start_cpu0_default(void) esp_vfs_dev_uart_register(); esp_reent_init(_GLOBAL_REENT); const char* default_uart_dev = "/dev/uart/0"; + _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); - _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); do_global_ctors(); #if !CONFIG_FREERTOS_UNICORE esp_crosscore_int_init(); From d29935b9cefa723d977a3be8b6b0a27d1449edf6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 10:33:21 +0800 Subject: [PATCH 156/285] newlib: fix `_times_r` syscall implementation tms_cstime should only take system time in child processes into account, so has to be zero. https://github.com/espressif/esp-idf/issues/81 --- components/newlib/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/newlib/time.c b/components/newlib/time.c index 5f60e1d7b2..0d7b6b7474 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -112,7 +112,7 @@ void esp_setup_time_syscalls() clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) { clock_t t = xTaskGetTickCount() * (portTICK_PERIOD_MS * CLK_TCK / 1000); - ptms->tms_cstime = t; + ptms->tms_cstime = 0; ptms->tms_cutime = 0; ptms->tms_stime = t; ptms->tms_utime = 0; From f142da3cede379d044a222712c6800fcdebafb94 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 10:35:46 +0800 Subject: [PATCH 157/285] driver/gpio: fix interrupt type names in comment block https://github.com/espressif/esp-idf/issues/56 --- components/driver/include/driver/gpio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 7106489d6c..1b2b758564 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -313,7 +313,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull); * * @param gpio_num GPIO number. * - * @param intr_type GPIO wake-up type. Only GPIO_INTR_LOLEVEL\GPIO_INTR_HILEVEL can be used + * @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL\GPIO_INTR_HIGH_LEVEL can be used. * * @return * - ESP_OK Success From 1e4f185d29dd603e54846e1d2a9c10e16418799d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 11:24:44 +0800 Subject: [PATCH 158/285] wifi: use MACSTR and MAC2STR in logging statements Fixes https://github.com/espressif/esp-idf/issues/49 --- components/esp32/event_default_handlers.c | 27 ++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/components/esp32/event_default_handlers.c b/components/esp32/event_default_handlers.c index a2bb3ccea3..b41d8be055 100644 --- a/components/esp32/event_default_handlers.c +++ b/components/esp32/event_default_handlers.c @@ -22,6 +22,7 @@ #include "esp_event.h" #include "esp_event_loop.h" #include "esp_task.h" +#include "rom/ets_sys.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -196,16 +197,14 @@ static esp_err_t esp_system_event_debug(system_event_t *event) } case SYSTEM_EVENT_STA_CONNECTED: { system_event_sta_connected_t *connected = &event->event_info.connected; - ESP_LOGD(TAG, "SYSTEM_EVENT_STA_CONNECTED, ssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, channel:%d, authmode:%d", \ - connected->ssid, connected->ssid_len, connected->bssid[0], connected->bssid[0], connected->bssid[1], \ - connected->bssid[3], connected->bssid[4], connected->bssid[5], connected->channel, connected->authmode); + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_CONNECTED, ssid:%s, ssid_len:%d, bssid:" MACSTR ", channel:%d, authmode:%d", \ + connected->ssid, connected->ssid_len, MAC2STR(connected->bssid), connected->channel, connected->authmode); break; } case SYSTEM_EVENT_STA_DISCONNECTED: { system_event_sta_disconnected_t *disconnected = &event->event_info.disconnected; - ESP_LOGD(TAG, "SYSTEM_EVENT_STA_DISCONNECTED, ssid:%s, ssid_len:%d, bssid:%02x:%02x:%02x:%02x:%02x:%02x, reason:%d", \ - disconnected->ssid, disconnected->ssid_len, disconnected->bssid[0], disconnected->bssid[0], disconnected->bssid[1], \ - disconnected->bssid[3], disconnected->bssid[4], disconnected->bssid[5], disconnected->reason); + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_DISCONNECTED, ssid:%s, ssid_len:%d, bssid:" MACSTR ", reason:%d", \ + disconnected->ssid, disconnected->ssid_len, MAC2STR(disconnected->bssid), disconnected->reason); break; } case SYSTEM_EVENT_STA_AUTHMODE_CHANGE: { @@ -231,23 +230,21 @@ static esp_err_t esp_system_event_debug(system_event_t *event) } case SYSTEM_EVENT_AP_STACONNECTED: { system_event_ap_staconnected_t *staconnected = &event->event_info.sta_connected; - ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STACONNECTED, mac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d", \ - staconnected->mac[0], staconnected->mac[0], staconnected->mac[1], \ - staconnected->mac[3], staconnected->mac[4], staconnected->mac[5], staconnected->aid); + ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STACONNECTED, mac:" MACSTR ", aid:%d", \ + MAC2STR(staconnected->mac), staconnected->aid); break; } case SYSTEM_EVENT_AP_STADISCONNECTED: { system_event_ap_stadisconnected_t *stadisconnected = &event->event_info.sta_disconnected; - ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STADISCONNECTED, mac:%02x:%02x:%02x:%02x:%02x:%02x, aid:%d", \ - stadisconnected->mac[0], stadisconnected->mac[0], stadisconnected->mac[1], \ - stadisconnected->mac[3], stadisconnected->mac[4], stadisconnected->mac[5], stadisconnected->aid); + ESP_LOGD(TAG, "SYSTEM_EVENT_AP_STADISCONNECTED, mac:" MACSTR ", aid:%d", \ + MAC2STR(stadisconnected->mac), stadisconnected->aid); break; } case SYSTEM_EVENT_AP_PROBEREQRECVED: { system_event_ap_probe_req_rx_t *ap_probereqrecved = &event->event_info.ap_probereqrecved; - ESP_LOGD(TAG, "SYSTEM_EVENT_AP_PROBEREQRECVED, rssi:%d, mac:%02x:%02x:%02x:%02x:%02x:%02x", \ - ap_probereqrecved->rssi, ap_probereqrecved->mac[0], ap_probereqrecved->mac[0], ap_probereqrecved->mac[1], \ - ap_probereqrecved->mac[3], ap_probereqrecved->mac[4], ap_probereqrecved->mac[5]); + ESP_LOGD(TAG, "SYSTEM_EVENT_AP_PROBEREQRECVED, rssi:%d, mac:" MACSTR, \ + ap_probereqrecved->rssi, \ + MAC2STR(ap_probereqrecved->mac)); break; } default: { From 81b18aa8d588156862559e0b22a86506fc6dac1a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 11:25:40 +0800 Subject: [PATCH 159/285] bootloader: move some functions out of IRAM when building in bootloader mode Fixes https://github.com/espressif/esp-idf/issues/80 --- components/log/log.c | 15 ++++++++++----- components/spi_flash/spi_flash_rom_patch.c | 10 ++++++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/components/log/log.c b/components/log/log.c index a2b41d7e62..9670b82dfd 100644 --- a/components/log/log.c +++ b/components/log/log.c @@ -284,7 +284,15 @@ static inline void heap_swap(int i, int j) } #endif //BOOTLOADER_BUILD -IRAM_ATTR uint32_t esp_log_early_timestamp() + +#ifndef BOOTLOADER_BUILD +#define ATTR IRAM_ATTR +#else +#define ATTR +#endif // BOOTLOADER_BUILD + + +uint32_t ATTR esp_log_early_timestamp() { return xthal_get_ccount() / (CPU_CLK_FREQ_ROM / 1000); } @@ -305,9 +313,6 @@ uint32_t IRAM_ATTR esp_log_timestamp() #else -uint32_t IRAM_ATTR esp_log_timestamp() -{ - return esp_log_early_timestamp(); -} +uint32_t esp_log_timestamp() __attribute__((alias("esp_log_early_timestamp"))); #endif //BOOTLOADER_BUILD diff --git a/components/spi_flash/spi_flash_rom_patch.c b/components/spi_flash/spi_flash_rom_patch.c index 7e23beaea2..36e5bf8236 100644 --- a/components/spi_flash/spi_flash_rom_patch.c +++ b/components/spi_flash/spi_flash_rom_patch.c @@ -19,9 +19,15 @@ static const uint32_t STATUS_QIE_BIT = (1 << 9); /* Quad Enable */ #define SPI_IDX 1 #define OTH_IDX 0 +#ifndef BOOTLOADER_BUILD +#define ATTR IRAM_ATTR +#else +#define ATTR +#endif // BOOTLOADER_BUILD + extern SpiFlashChip SPI_flashchip_data; -static void IRAM_ATTR Wait_SPI_Idle(void) +static void ATTR Wait_SPI_Idle(void) { /* Wait for SPI state machine to be idle */ while((REG_READ(SPI_EXT2_REG(SPI_IDX)) & SPI_ST)) { @@ -42,7 +48,7 @@ static void IRAM_ATTR Wait_SPI_Idle(void) about interrupts, CPU coordination, flash mapping. However some of the functions in esp_spi_flash.c call it. */ -SpiFlashOpResult IRAM_ATTR SPIUnlock(void) +SpiFlashOpResult ATTR SPIUnlock(void) { uint32_t status; From c5793521a015cd061bc5b10d5bd8cc87cc3b6723 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 8 Nov 2016 12:00:38 +1100 Subject: [PATCH 160/285] build system: Fix bootloader-flash target ESP32 forum thread: http://esp32.com/viewtopic.php?f=2&t=407&p=1902#p1902 --- components/bootloader/Makefile.projbuild | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 50c95f9fec..0b460059e8 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -33,7 +33,7 @@ clean: bootloader-clean bootloader: $(BOOTLOADER_BIN) @echo "Bootloader built. Default flash command is:" - @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $^" all_binaries: $(BOOTLOADER_BIN) @@ -41,7 +41,7 @@ ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) # bootloader-flash calls flash in the bootloader dummy project bootloader-flash: $(BOOTLOADER_BIN) - $(BOOTLOADER_MAKE) flash + $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ # synchronise the project level config to the bootloader's # config From cc072f1d8ab4858775fc36341fc29283765e619a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 8 Nov 2016 20:26:12 +0800 Subject: [PATCH 161/285] wpa_supplicant: clean up unused variable warning --- components/wpa_supplicant/src/crypto/libtommath.h | 1 + 1 file changed, 1 insertion(+) diff --git a/components/wpa_supplicant/src/crypto/libtommath.h b/components/wpa_supplicant/src/crypto/libtommath.h index 31f9706593..1010f9f63f 100644 --- a/components/wpa_supplicant/src/crypto/libtommath.h +++ b/components/wpa_supplicant/src/crypto/libtommath.h @@ -668,6 +668,7 @@ mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) } else { #endif #ifdef BN_S_MP_EXPTMOD_C + (void) dr; /* otherwise use the generic Barrett reduction technique */ return s_mp_exptmod (G, X, P, Y, 0); #else From 830e5caf4de6c211ff0321d6d74010bafca35a0b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 12:51:55 +1100 Subject: [PATCH 162/285] build system: Replace get_variable target w/ component_project_vars.mk generated makefiles Reduces number of make invocations, allows variables exported in project to be seen in all component make processes, not just the main ones. Also makes a no-op build about 3x faster than it was. --- components/bootloader/src/main/component.mk | 2 +- components/bt/component.mk | 4 +- components/esp32/component.mk | 6 +- components/newlib/component.mk | 2 +- make/common.mk | 6 -- make/component_common.mk | 23 ++++-- make/project.mk | 86 ++++++++++----------- make/project_config.mk | 2 +- 8 files changed, 66 insertions(+), 65 deletions(-) diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index 8c8ea4cb63..bd1dcf4d90 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -7,6 +7,6 @@ # please read the esp-idf build system document if you need to do this. # -COMPONENT_ADD_LDFLAGS := -L $(abspath .) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld +COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld include $(IDF_PATH)/make/component_common.mk diff --git a/components/bt/component.mk b/components/bt/component.mk index e88651aa13..5addee2ceb 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -2,15 +2,13 @@ # Component Makefile # -#COMPONENT_ADD_INCLUDEDIRS := - COMPONENT_ADD_INCLUDEDIRS := include CFLAGS += -Wno-error=unused-label -Wno-error=return-type -Wno-error=missing-braces -Wno-error=pointer-sign -Wno-error=parentheses LIBS := btdm_app -COMPONENT_ADD_LDFLAGS := -lbt -L$(abspath lib) \ +COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \ $(addprefix -l,$(LIBS)) \ $(LINKER_SCRIPTS) diff --git a/components/esp32/component.mk b/components/esp32/component.mk index c658787d87..7e22b65183 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -15,10 +15,10 @@ LIBS := core net80211 phy rtc pp wpa smartconfig coexist LINKER_SCRIPTS += -T esp32_out.ld -T esp32.common.ld -T esp32.rom.ld -T esp32.peripherals.ld COMPONENT_ADD_LDFLAGS := -lesp32 \ - $(abspath libhal.a) \ - -L$(abspath lib) \ + $(COMPONENT_PATH)/libhal.a \ + -L$(COMPONENT_PATH)/lib \ $(addprefix -l,$(LIBS)) \ - -L $(abspath ld) \ + -L $(COMPONENT_PATH)/ld \ $(LINKER_SCRIPTS) include $(IDF_PATH)/make/component_common.mk diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 3731b5ef0e..0648946c20 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -1,4 +1,4 @@ -COMPONENT_ADD_LDFLAGS := $(abspath lib/libc.a) $(abspath lib/libm.a) -lnewlib +COMPONENT_ADD_LDFLAGS := $(COMPONENT_PATH)/lib/libc.a $(COMPONENT_PATH)/lib/libm.a -lnewlib COMPONENT_ADD_INCLUDEDIRS := include platform_include diff --git a/make/common.mk b/make/common.mk index 2b7376a4db..6183cfbc46 100644 --- a/make/common.mk +++ b/make/common.mk @@ -2,12 +2,6 @@ # and component makefiles # -# Include project config file, if it exists. -# -# (Note that we only rebuild auto.conf automatically for some targets, -# see project_config.mk for details.) --include $(BUILD_DIR_BASE)/include/config/auto.conf - #Handling of V=1/VERBOSE=1 flag # # if V=1, $(summary) does nothing and $(details) will echo extra details diff --git a/make/component_common.mk b/make/component_common.mk index bf2eace019..3177ca6e30 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -58,10 +58,19 @@ COMPONENT_ADD_LDFLAGS ?= -l$(COMPONENT_NAME) OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) -#This target is used to collect variable values from inside project.mk -# see project.mk GetVariable macro for details. -get_variable: - @echo "$(GET_VARIABLE)=$(call $(GET_VARIABLE)) " +# This target is used to take component.mk variables COMPONENT_ADD_INCLUDEDIRS, +# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject them into the project +# makefile level. +# +# The target here has no dependencies, as the parent target in +# project.mk evaluates dependencies before calling down to here. See +# GenerateProjectVarsTarget in project.mk. +component_project_vars.mk:: + $(details) "Rebuilding component project variables list $(abspath $@)" + @echo "# Automatically generated build file. Do not edit." > $@ + @echo "COMPONENT_INCLUDES += $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS))" >> $@ + @echo "COMPONENT_LDFLAGS += $(COMPONENT_ADD_LDFLAGS)" >> $@ + @echo "$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))" >> $@ #Targets for build/clean. Use builtin recipe if component Makefile #hasn't defined its own. @@ -77,10 +86,12 @@ $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(Q) $(AR) cru $@ $(COMPONENT_OBJS) endif +CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk + ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") clean: - $(summary) RM $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) - $(Q) rm -f $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) + $(summary) RM $(CLEAN_FILES) + $(Q) rm -f $(CLEAN_FILES) endif #Include all dependency files already generated diff --git a/make/project.mk b/make/project.mk index a60a3958fe..ac02261869 100644 --- a/make/project.mk +++ b/make/project.mk @@ -48,11 +48,22 @@ PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) export PROJECT_PATH endif +COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/component_common.mk) +export COMMON_MAKEFILES + #The directory where we put all objects/libraries/binaries. The project Makefile can #configure this if needed. BUILD_DIR_BASE ?= $(PROJECT_PATH)/build export BUILD_DIR_BASE +# Include project config file, if it exists. +# +# (Note that we only rebuild auto.conf automatically for some targets, +# see project_config.mk for details.) +SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf +-include $(SDKCONFIG_MAKEFILE) +export $(filter CONFIG_%,$(.VARIABLES)) + #Component directories. These directories are searched for components. #The project Makefile can override these component dirs, or define extra component directories. COMPONENT_DIRS ?= $(PROJECT_PATH)/components $(EXTRA_COMPONENT_DIRS) $(IDF_PATH)/components @@ -87,48 +98,33 @@ COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(c # LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. COMPONENT_INCLUDES := COMPONENT_LDFLAGS := -# -# Also add any inter-component dependencies for each component. -# Extract a variable from a child make process +# include paths for generated "component project variables" targets with +# COMPONENT_INCLUDES, COMPONENT_LDFLAGS & dependency targets # -# $(1) - path to directory to invoke make in -# $(2) - name of variable to print via the get_variable target (passed in GET_VARIABLE) +# See component_project_vars.mk target in component_common.mk +COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE))) +COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) +include $(COMPONENT_PROJECT_VARS) + + +# Generate a target to rebuild component_project_vars.mk for a component +# $(1) - component directory +# $(2) - component name only # -# needs 'sed' processing of stdout because make sometimes echoes other stuff on stdout, -# even if asked not to. -# -# Debugging this? Replace $(shell with $(error and you'll see the full command as-run. -define GetVariable -$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) | sed -En "s/^$(2)=(.+)/\1/p" ) +# Rebuilds if component.mk, makefiles or sdkconfig changes. +define GenerateProjectVarsTarget +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(if $(MAKE_RESTARTS),,$(SDKCONFIG_MAKEFILE)) $(BUILD_DIR_BASE)/$(2) + $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk component_project_vars.mk COMPONENT_PATH=$(1) endef +$(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateProjectVarsTarget,$(comp),$(notdir $(comp))))) -COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(addprefix $(comp)/, \ - $(call GetVariable,$(comp),COMPONENT_ADD_INCLUDEDIRS)))) - -#Also add project include path, for sdk includes +#Also add project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) + export COMPONENT_INCLUDES - -#COMPONENT_LDFLAGS has a list of all flags that are needed to link the components together. It's collected -#in the same way as COMPONENT_INCLUDES is. -COMPONENT_LDFLAGS := $(foreach comp,$(COMPONENT_PATHS_BUILDABLE), \ - $(call GetVariable,$(comp),COMPONENT_ADD_LDFLAGS)) export COMPONENT_LDFLAGS -# Generate component dependency targets from dependencies lists -# each component gains a target of its own -build with dependencies -# of the names of any other components (-build) that need building first -# -# the actual targets (that invoke submakes) are generated below by -# GenerateComponentTarget macro. -define GenerateComponentDependencies -# $(1) = component path -.PHONY: $$(notdir $(1)) -$$(notdir $(1))-build: $(addsuffix -build,$(call GetVariable,$(1),COMPONENT_DEPENDS)) -endef -$(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateComponentDependencies,$(comp)))) - #Make sure submakes can also use this. export PROJECT_PATH @@ -242,7 +238,7 @@ COMPONENT_PATH := $(1) endef $(foreach componentpath,$(COMPONENT_PATHS),$(eval $(call includeProjBuildMakefile,$(componentpath)))) -# once we know component paths, we can include the config +# once we know component paths, we can include the config generation targets include $(IDF_PATH)/make/project_config.mk # A "component" library is any library in the LDFLAGS where @@ -269,29 +265,31 @@ $(BUILD_DIR_BASE): define GenerateComponentPhonyTarget # $(1) - path to component dir -# $(2) - target to generate (build, clean) -.PHONY: $(notdir $(1))-$(2) -$(notdir $(1))-$(2): | $(BUILD_DIR_BASE)/$(notdir $(1)) - $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(notdir $(1)) -f $(1)/component.mk COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(notdir $(1)) $(2) +# $(2) - name of component +# $(3) - target to generate (build, clean) +.PHONY: $(2)-$(3) +$(2)-$(3): | $(BUILD_DIR_BASE)/$(2) + $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) $(3) endef define GenerateComponentTargets # $(1) - path to component dir -$(BUILD_DIR_BASE)/$(notdir $(1)): - @mkdir -p $(BUILD_DIR_BASE)/$(notdir $(1)) +# $(2) - name of component +$(BUILD_DIR_BASE)/$(2): + @mkdir -p $(BUILD_DIR_BASE)/$(2) # tell make it can build any component's library by invoking the recursive -build target # (this target exists for all components even ones which don't build libraries, but it's # only invoked for the targets whose libraries appear in COMPONENT_LIBRARIES and hence the # APP_ELF dependencies.) -$(BUILD_DIR_BASE)/$(notdir $(1))/lib$(notdir $(1)).a: $(notdir $(1))-build +$(BUILD_DIR_BASE)/$(2)/lib$(2).a: $(2)-build $(details) "Target '$$^' responsible for '$$@'" # echo which build target built this file endef -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTargets,$(component)))) +$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTargets,$(component),$(notdir $(component))))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),build))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),clean))) +$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),build))) +$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),clean))) app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) diff --git a/make/project_config.mk b/make/project_config.mk index 7ca83ce5af..4d0f6c2059 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -37,7 +37,7 @@ defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) # Work out of whether we have to build the Kconfig makefile # (auto.conf), or if we're in a situation where we don't need it -NON_CONFIG_TARGETS := clean %-clean get_variable help menuconfig defconfig +NON_CONFIG_TARGETS := clean %-clean help menuconfig defconfig AUTO_CONF_REGEN_TARGET := $(BUILD_DIR_BASE)/include/config/auto.conf # disable AUTO_CONF_REGEN_TARGET if all targets are non-config targets From 155f912433d93882f55f6f813ce72b3623cd073a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 14:26:50 +1100 Subject: [PATCH 163/285] build system: Don't build an sdkconfig for bootloader, share the top-level one This works because all CONFIG variables are exported into child make processes. --- components/bootloader/Makefile.projbuild | 16 ++++------------ components/bootloader/src/Makefile | 7 +++++-- make/component_common.mk | 2 +- make/project.mk | 10 +++++++++- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 0b460059e8..5bcd77b36f 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -13,21 +13,18 @@ ifndef IS_BOOTLOADER_BUILD BOOTLOADER_COMPONENT_PATH := $(COMPONENT_PATH) BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin -BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ - V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ - BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ + V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) -$(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig +$(BOOTLOADER_BIN): $(Q) $(BOOTLOADER_MAKE) $@ bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean config-clean - $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old + $(Q) $(BOOTLOADER_MAKE) app-clean clean: bootloader-clean @@ -43,15 +40,10 @@ ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) bootloader-flash: $(BOOTLOADER_BIN) $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ -# synchronise the project level config to the bootloader's -# config -$(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR) - $(Q) cp $< $@ - $(BOOTLOADER_BUILD_DIR): $(Q) mkdir -p $@ else -CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include +CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include endif diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index add9c15d61..3a939a8857 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,6 +4,9 @@ # PROJECT_NAME := bootloader + +#We cannot include the esp32 component directly but we need its includes. +#This is fixed by adding CFLAGS from Makefile.projbuild COMPONENTS := esptool_py bootloader log spi_flash # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. @@ -12,7 +15,7 @@ COMPONENTS := esptool_py bootloader log spi_flash IS_BOOTLOADER_BUILD := 1 export IS_BOOTLOADER_BUILD -#We cannot include the esp32 component directly but we need its includes. -#This is fixed by adding CFLAGS from Makefile.projbuild +# include the top-level "project" include directory, for sdkconfig.h +CFLAGS += -I$(BUILD_DIR_BASE)/../include include $(IDF_PATH)/make/project.mk diff --git a/make/component_common.mk b/make/component_common.mk index 3177ca6e30..9db57bf948 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -66,7 +66,7 @@ COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_I # project.mk evaluates dependencies before calling down to here. See # GenerateProjectVarsTarget in project.mk. component_project_vars.mk:: - $(details) "Rebuilding component project variables list $(abspath $@)" + $(details) "Building component project variables list $(abspath $@)" @echo "# Automatically generated build file. Do not edit." > $@ @echo "COMPONENT_INCLUDES += $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS))" >> $@ @echo "COMPONENT_LDFLAGS += $(COMPONENT_ADD_LDFLAGS)" >> $@ diff --git a/make/project.mk b/make/project.mk index ac02261869..deaf987709 100644 --- a/make/project.mk +++ b/make/project.mk @@ -56,13 +56,17 @@ export COMMON_MAKEFILES BUILD_DIR_BASE ?= $(PROJECT_PATH)/build export BUILD_DIR_BASE +ifndef IS_BOOTLOADER_BUILD # Include project config file, if it exists. +# (bootloader build doesn't need this, config is exported from top-level) # # (Note that we only rebuild auto.conf automatically for some targets, # see project_config.mk for details.) +# SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf -include $(SDKCONFIG_MAKEFILE) export $(filter CONFIG_%,$(.VARIABLES)) +endif #Component directories. These directories are searched for components. #The project Makefile can override these component dirs, or define extra component directories. @@ -114,7 +118,7 @@ include $(COMPONENT_PROJECT_VARS) # # Rebuilds if component.mk, makefiles or sdkconfig changes. define GenerateProjectVarsTarget -$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(if $(MAKE_RESTARTS),,$(SDKCONFIG_MAKEFILE)) $(BUILD_DIR_BASE)/$(2) +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk component_project_vars.mk COMPONENT_PATH=$(1) endef $(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateProjectVarsTarget,$(comp),$(notdir $(comp))))) @@ -238,8 +242,12 @@ COMPONENT_PATH := $(1) endef $(foreach componentpath,$(COMPONENT_PATHS),$(eval $(call includeProjBuildMakefile,$(componentpath)))) +ifndef IS_BOOTLOADER_BUILD # once we know component paths, we can include the config generation targets +# +# (bootloader build doesn't need this, config is exported from top-level) include $(IDF_PATH)/make/project_config.mk +endif # A "component" library is any library in the LDFLAGS where # the name of the library is also a name of the component From d7e57eb66830ce0b6b8f9a168b7af67efc34ec38 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 17:25:57 +1100 Subject: [PATCH 164/285] build system: Refactor the three component-target-related macros into one --- make/project.mk | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/make/project.mk b/make/project.mk index deaf987709..17d9e99c0c 100644 --- a/make/project.mk +++ b/make/project.mk @@ -111,18 +111,6 @@ COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMP COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) include $(COMPONENT_PROJECT_VARS) - -# Generate a target to rebuild component_project_vars.mk for a component -# $(1) - component directory -# $(2) - component name only -# -# Rebuilds if component.mk, makefiles or sdkconfig changes. -define GenerateProjectVarsTarget -$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) - $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk component_project_vars.mk COMPONENT_PATH=$(1) -endef -$(foreach comp,$(COMPONENT_PATHS_BUILDABLE), $(eval $(call GenerateProjectVarsTarget,$(comp),$(notdir $(comp))))) - #Also add project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) @@ -271,34 +259,45 @@ all_binaries: $(APP_BIN) $(BUILD_DIR_BASE): mkdir -p $(BUILD_DIR_BASE) -define GenerateComponentPhonyTarget -# $(1) - path to component dir -# $(2) - name of component -# $(3) - target to generate (build, clean) -.PHONY: $(2)-$(3) -$(2)-$(3): | $(BUILD_DIR_BASE)/$(2) - $(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) $(3) +# Macro for the recursive sub-make for each component +# $(1) - component directory +# $(2) - component name only +# +# Is recursively expanded by the GenerateComponentTargets macro +define ComponentMake +$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) endef define GenerateComponentTargets # $(1) - path to component dir # $(2) - name of component +.PHONY: $(2)-build $(2)-clean + +$(2)-build: + $(call ComponentMake,$(1),$(2)) build + +$(2)-clean: + $(call ComponentMake,$(1),$(2)) clean + $(BUILD_DIR_BASE)/$(2): @mkdir -p $(BUILD_DIR_BASE)/$(2) -# tell make it can build any component's library by invoking the recursive -build target +# tell make it can build any component's library by invoking the -build target # (this target exists for all components even ones which don't build libraries, but it's # only invoked for the targets whose libraries appear in COMPONENT_LIBRARIES and hence the # APP_ELF dependencies.) $(BUILD_DIR_BASE)/$(2)/lib$(2).a: $(2)-build $(details) "Target '$$^' responsible for '$$@'" # echo which build target built this file + +# add a target to generate the component_project_vars.mk files +# that are used to inject variables into project make pass (see +# component_project_vars.mk target in component_common.mk). +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) + $(call ComponentMake,$(1),$(2)) component_project_vars.mk endef $(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTargets,$(component),$(notdir $(component))))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),build))) -$(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentPhonyTarget,$(component),$(notdir $(component)),clean))) - app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) $(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) From 3b96f68afd4e998f987fcc1a22810b5902b25b7e Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Wed, 9 Nov 2016 17:27:12 +0800 Subject: [PATCH 165/285] 1. Add wps event processing. 2. Change wps API return type and value. --- components/esp32/event_default_handlers.c | 20 +++++++ components/esp32/include/esp_event.h | 9 +++ components/esp32/include/esp_wps.h | 71 ++++++++--------------- 3 files changed, 54 insertions(+), 46 deletions(-) diff --git a/components/esp32/event_default_handlers.c b/components/esp32/event_default_handlers.c index a2bb3ccea3..497c437163 100644 --- a/components/esp32/event_default_handlers.c +++ b/components/esp32/event_default_handlers.c @@ -67,6 +67,10 @@ static system_event_handle_t g_system_event_handle_table[] = { {SYSTEM_EVENT_STA_DISCONNECTED, system_event_sta_disconnected_handle_default}, {SYSTEM_EVENT_STA_AUTHMODE_CHANGE, NULL}, {SYSTEM_EVENT_STA_GOT_IP, system_event_sta_got_ip_default}, + {SYSTEM_EVENT_STA_WPS_ER_SUCCESS, NULL}, + {SYSTEM_EVENT_STA_WPS_ER_FAILED, NULL}, + {SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, NULL}, + {SYSTEM_EVENT_STA_WPS_ER_PIN, NULL}, {SYSTEM_EVENT_AP_START, system_event_ap_start_handle_default}, {SYSTEM_EVENT_AP_STOP, system_event_ap_stop_handle_default}, {SYSTEM_EVENT_AP_STACONNECTED, NULL}, @@ -221,6 +225,22 @@ static esp_err_t esp_system_event_debug(system_event_t *event) IP2STR(&got_ip->ip_info.gw)); break; } + case SYSTEM_EVENT_STA_WPS_ER_SUCCESS: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_SUCCESS"); + break; + } + case SYSTEM_EVENT_STA_WPS_ER_FAILED: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_FAILED"); + break; + } + case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_TIMEOUT"); + break; + } + case SYSTEM_EVENT_STA_WPS_ER_PIN: { + ESP_LOGD(TAG, "SYSTEM_EVENT_STA_WPS_ER_PIN"); + break; + } case SYSTEM_EVENT_AP_START: { ESP_LOGD(TAG, "SYSTEM_EVENT_AP_START"); break; diff --git a/components/esp32/include/esp_event.h b/components/esp32/include/esp_event.h index 0ca4ca90ba..481c5effd5 100644 --- a/components/esp32/include/esp_event.h +++ b/components/esp32/include/esp_event.h @@ -35,6 +35,10 @@ typedef enum { SYSTEM_EVENT_STA_DISCONNECTED, /**< ESP32 station disconnected from AP */ SYSTEM_EVENT_STA_AUTHMODE_CHANGE, /**< the auth mode of AP connected by ESP32 station changed */ SYSTEM_EVENT_STA_GOT_IP, /**< ESP32 station got IP from connected AP */ + SYSTEM_EVENT_STA_WPS_ER_SUCCESS, /**< ESP32 station wps succeeds in enrollee mode */ + SYSTEM_EVENT_STA_WPS_ER_FAILED, /**< ESP32 station wps fails in enrollee mode */ + SYSTEM_EVENT_STA_WPS_ER_TIMEOUT, /**< ESP32 station wps timeout in enrollee mode */ + SYSTEM_EVENT_STA_WPS_ER_PIN, /**< ESP32 station wps pin code in enrollee mode */ SYSTEM_EVENT_AP_START, /**< ESP32 soft-AP start */ SYSTEM_EVENT_AP_STOP, /**< ESP32 soft-AP stop */ SYSTEM_EVENT_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */ @@ -73,6 +77,10 @@ typedef struct { tcpip_adapter_ip_info_t ip_info; } system_event_sta_got_ip_t; +typedef struct { + uint8_t pin_code; /**< PIN code of station in enrollee mode */ +}system_event_sta_wps_er_pin_t; + typedef struct { uint8_t mac[6]; /**< MAC address of the station connected to ESP32 soft-AP */ uint8_t aid; /**< the aid that ESP32 soft-AP gives to the station connected to */ @@ -94,6 +102,7 @@ typedef union { system_event_sta_scan_done_t scan_done; /**< ESP32 station scan (APs) done */ system_event_sta_authmode_change_t auth_change; /**< the auth mode of AP ESP32 station connected to changed */ system_event_sta_got_ip_t got_ip; /**< ESP32 station got IP */ + system_event_sta_wps_er_pin_t sta_er_pin; system_event_ap_staconnected_t sta_connected; /**< a station connected to ESP32 soft-AP */ system_event_ap_stadisconnected_t sta_disconnected; /**< a station disconnected to ESP32 soft-AP */ system_event_ap_probe_req_rx_t ap_probereqrecved; /**< ESP32 soft-AP receive probe request packet */ diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h index 1588d36191..2ea7e194bb 100644 --- a/components/esp32/include/esp_wps.h +++ b/components/esp32/include/esp_wps.h @@ -16,6 +16,7 @@ #define __ESP_WPS_H__ #include +#include "esp_err.h" #ifdef __cplusplus extern "C" { @@ -40,43 +41,43 @@ extern "C" { * @{ */ +#define ESP_ERR_WIFI_REGISTRAR (ESP_ERR_WIFI_BASE + 51) /*!< WPS registrar is not supported */ +#define ESP_ERR_WIFI_WPS_TYPE (ESP_ERR_WIFI_BASE + 52) /*!< WPS type error */ +#define ESP_ERR_WIFI_WPS_SM (ESP_ERR_WIFI_BASE + 53) /*!< WPS state machine is not initialized */ + typedef enum wps_type { WPS_TYPE_DISABLE = 0, WPS_TYPE_PBC, WPS_TYPE_PIN, - WPS_TYPE_DISPLAY, WPS_TYPE_MAX, -} WPS_TYPE_t; - -enum wps_cb_status { - WPS_CB_ST_SUCCESS = 0, /**< WPS succeed */ - WPS_CB_ST_FAILED, /**< WPS fail */ - WPS_CB_ST_TIMEOUT, /**< WPS timeout, fail */ - WPS_CB_ST_WEP, /**< WPS failed because that WEP is not supported */ - WPS_CB_ST_SCAN_ERR, /**< can not find the target WPS AP */ -}; +} wps_type_t; /** * @brief Enable Wi-Fi WPS function. * * @attention WPS can only be used when ESP32 station is enabled. * - * @param WPS_TYPE_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported + * @param wps_type_t wps_type : WPS type, so far only WPS_TYPE_PBC and WPS_TYPE_PIN is supported * - * @return true : succeed - * @return false : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on + * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized + * - ESP_ERR_WIFI_FAIL : wps initialization fails */ -bool esp_wifi_wps_enable(WPS_TYPE_t wps_type); +esp_err_t esp_wifi_wps_enable(wps_type_t wps_type); /** * @brief Disable Wi-Fi WPS function and release resource it taken. * * @param null * - * @return true : succeed - * @return false : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on */ -bool esp_wifi_wps_disable(void); +esp_err_t esp_wifi_wps_disable(void); /** * @brief WPS starts to work. @@ -85,36 +86,14 @@ bool esp_wifi_wps_disable(void); * * @param null * - * @return true : WPS starts to work successfully, but does not mean WPS succeed. - * @return false : fail + * @return + * - ESP_OK : succeed + * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid + * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on + * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized + * - ESP_ERR_WIFI_FAIL : wps initialization fails */ -bool esp_wifi_wps_start(void); - -/** - * @brief WPS callback. - * - * @param int status : status of WPS, enum wps_cb_status. - * - If parameter status == WPS_CB_ST_SUCCESS in WPS callback, it means WPS got AP's - * information, user can call wifi_wps_disable to disable WPS and release resource, - * then call wifi_station_connect to connect to target AP. - * - Otherwise, it means that WPS fail, user can create a timer to retry WPS by - * wifi_wps_start after a while, or call wifi_wps_disable to disable WPS and release resource. - * - * @return null - */ -typedef void (*wps_st_cb_t)(int status); - -/** - * @brief Set WPS callback. - * - * @attention WPS can only be used when ESP32 station is enabled. - * - * @param wps_st_cb_t cb : callback. - * - * @return true : WPS starts to work successfully, but does not mean WPS succeed. - * @return false : fail - */ -bool esp_wifi_set_wps_cb(wps_st_cb_t cb); +esp_err_t esp_wifi_wps_start(void); /** * @} From 2f0efe151084f9c77e4c1f6dce9e5711b02ba7a7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 20:38:16 +1100 Subject: [PATCH 166/285] build system: Fix defconfig vs menuconfig regression in 155f9124 --- make/build_examples.sh | 2 +- make/project_config.mk | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index d264862874..53cba23b1e 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -22,7 +22,7 @@ for example in ${IDF_PATH}/examples/*; do pushd ${EXAMPLE_NUM}/`basename ${example}` # can't do "make defconfig all" as this will trip menuconfig # sometimes - make defconfig && make || RESULT=$? + make defconfig V=1 && make V=1 || RESULT=$? popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) done diff --git a/make/project_config.mk b/make/project_config.mk index 4d0f6c2059..555086fb8d 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -21,13 +21,17 @@ KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfi COMPONENT_KCONFIGS="$(COMPONENT_KCONFIGS)" KCONFIG_CONFIG=$(SDKCONFIG) \ COMPONENT_KCONFIGS_PROJBUILD="$(COMPONENT_KCONFIGS_PROJBUILD)" -menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) +menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(summary) MENUCONFIG $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(SDKCONFIG))","") -#No sdkconfig found. Need to run menuconfig to make this if we need it. +ifeq ("$(filter defconfig,$(MAKECMDGOALS))","") +# if not configuration is present and defconfig is not a target, run makeconfig $(SDKCONFIG): menuconfig +else +$(SDKCONFIG): defconfig +endif endif defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) From c441d3630c267e047327a7afd29c56cf683cbc56 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Wed, 9 Nov 2016 18:15:10 +0800 Subject: [PATCH 167/285] change pin code value type and wps API comments. --- components/esp32/include/esp_event.h | 2 +- components/esp32/include/esp_wps.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/components/esp32/include/esp_event.h b/components/esp32/include/esp_event.h index 481c5effd5..3b2b54acca 100644 --- a/components/esp32/include/esp_event.h +++ b/components/esp32/include/esp_event.h @@ -78,7 +78,7 @@ typedef struct { } system_event_sta_got_ip_t; typedef struct { - uint8_t pin_code; /**< PIN code of station in enrollee mode */ + uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */ }system_event_sta_wps_er_pin_t; typedef struct { diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h index 2ea7e194bb..0a413a1978 100644 --- a/components/esp32/include/esp_wps.h +++ b/components/esp32/include/esp_wps.h @@ -63,7 +63,6 @@ typedef enum wps_type { * - ESP_OK : succeed * - ESP_ERR_WIFI_WPS_TYPE : wps type is invalid * - ESP_ERR_WIFI_WPS_MODE : wifi is not in station mode or sniffer mode is on - * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized * - ESP_ERR_WIFI_FAIL : wps initialization fails */ esp_err_t esp_wifi_wps_enable(wps_type_t wps_type); From f1938a909ae8fd94be5a4f97a6485a0206327aec Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 10:29:42 +1100 Subject: [PATCH 168/285] build system: Generated makefiles should contain environment-variable-relative paths where possible Means that moving directories around then partial building should succeed when possible. --- make/component_common.mk | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/make/component_common.mk b/make/component_common.mk index 9db57bf948..a9598a23ba 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -58,19 +58,33 @@ COMPONENT_ADD_LDFLAGS ?= -l$(COMPONENT_NAME) OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) -# This target is used to take component.mk variables COMPONENT_ADD_INCLUDEDIRS, -# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject them into the project -# makefile level. +# macro to generate relative paths inside component_project_vars.mk, whenever possible +# ie put literal $(IDF_PATH), $(PROJECT_PATH) and $(BUILD_DIR_BASE) into the generated +# makefiles where possible. +# +# This means if directories move (breaking absolute paths), don't need to 'make clean' +define MakeRelativePath +$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),\$$(BUILD_DIR_BASE),$(1)))) +endef + +# This target generates component_project_vars.mk for the +# component. This is used to take component.mk variables +# COMPONENT_ADD_INCLUDEDIRS, COMPONENT_ADD_LDFLAGS and +# COMPONENT_DEPENDS and inject those into the project makefile. # # The target here has no dependencies, as the parent target in # project.mk evaluates dependencies before calling down to here. See -# GenerateProjectVarsTarget in project.mk. +# GenerateComponentTargets macro in project.mk. +# +# If you are thinking of editing the output of this makefile for a +# component-specific feature, please don't! What you want is a +# Makefile.projbuild for your component (see docs/build-system.rst for more.) component_project_vars.mk:: $(details) "Building component project variables list $(abspath $@)" - @echo "# Automatically generated build file. Do not edit." > $@ - @echo "COMPONENT_INCLUDES += $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS))" >> $@ - @echo "COMPONENT_LDFLAGS += $(COMPONENT_ADD_LDFLAGS)" >> $@ - @echo "$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))" >> $@ + @echo '# Automatically generated build file. Do not edit.' > $@ + @echo 'COMPONENT_INCLUDES += $(call MakeRelativePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ + @echo 'COMPONENT_LDFLAGS += $(call MakeRelativePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ + @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ #Targets for build/clean. Use builtin recipe if component Makefile #hasn't defined its own. From 208e83def7839c0b317a37f54d9c46cfe91421ef Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 13:20:55 +1100 Subject: [PATCH 169/285] build system: Refactor component.mk to not need component_common.mk New makefile component_wrapper.mk allows some variables to be set before component.mk is evaluated. This properly fixes problems with sdkconfig being hard to access in all phases of the build. Including component_common.mk is no longer necessary and will print a deprecation warning for components which use it. --- components/bootloader/Makefile.projbuild | 2 +- components/bootloader/src/main/component.mk | 9 +- components/bt/component.mk | 2 - components/driver/component.mk | 6 - components/esp32/component.mk | 10 -- components/expat/component.mk | 6 - components/freertos/component.mk | 1 - components/json/component.mk | 6 - components/log/component.mk | 6 +- components/lwip/component.mk | 1 - components/mbedtls/component.mk | 1 - components/newlib/component.mk | 1 - components/nghttp/component.mk | 2 - components/nvs_flash/component.mk | 1 - components/openssl/component.mk | 1 - components/spi_flash/component.mk | 1 - components/tcpip_adapter/component.mk | 2 +- components/vfs/component.mk | 6 +- components/wpa_supplicant/component.mk | 2 - components/xtensa-debug-module/component.mk | 3 +- examples/01_hello_world/main/component.mk | 7 +- examples/02_blink/main/component.mk | 8 +- examples/03_http_request/main/component.mk | 8 +- examples/04_https_request/main/component.mk | 7 +- examples/05_ble_adv/main/component.mk | 8 +- examples/06_sntp/main/component.mk | 8 +- make/common.mk | 12 +- make/component_common.mk | 136 +--------------- make/component_wrapper.mk | 169 ++++++++++++++++++++ make/project.mk | 110 +++++++------ make/project_config.mk | 4 +- 31 files changed, 261 insertions(+), 285 deletions(-) create mode 100644 make/component_wrapper.mk diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 5bcd77b36f..c03288d107 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -20,7 +20,7 @@ BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) -$(BOOTLOADER_BIN): +$(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE) $(Q) $(BOOTLOADER_MAKE) $@ bootloader-clean: diff --git a/components/bootloader/src/main/component.mk b/components/bootloader/src/main/component.mk index bd1dcf4d90..01b07b9498 100644 --- a/components/bootloader/src/main/component.mk +++ b/components/bootloader/src/main/component.mk @@ -1,12 +1,9 @@ # -# Main Makefile. This is basically the same as a component makefile. +# Main bootloader Makefile. # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the esp-idf build system document if you need to do this. +# This is basically the same as a component makefile, but in the case of the bootloader +# we pull in bootloader-specific linker arguments. # COMPONENT_ADD_LDFLAGS := -L $(COMPONENT_PATH) -lmain -T esp32.bootloader.ld -T $(IDF_PATH)/components/esp32/ld/esp32.rom.ld -include $(IDF_PATH)/make/component_common.mk diff --git a/components/bt/component.mk b/components/bt/component.mk index 5addee2ceb..91620ddc14 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -12,8 +12,6 @@ COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \ $(addprefix -l,$(LIBS)) \ $(LINKER_SCRIPTS) -include $(IDF_PATH)/make/component_common.mk - ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) $(COMPONENT_LIBRARY): $(ALL_LIB_FILES) diff --git a/components/driver/component.mk b/components/driver/component.mk index a19b131ef5..a208f6ae20 100644 --- a/components/driver/component.mk +++ b/components/driver/component.mk @@ -1,14 +1,8 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the SDK documents if you need to do this. -# COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := include/driver -include $(IDF_PATH)/make/component_common.mk diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 7e22b65183..3866d3e4b9 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -1,12 +1,6 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the esp-idf build system document if you need to do this. -# --include include/config/auto.conf COMPONENT_SRCDIRS := . hwcrypto @@ -21,8 +15,6 @@ COMPONENT_ADD_LDFLAGS := -lesp32 \ -L $(COMPONENT_PATH)/ld \ $(LINKER_SCRIPTS) -include $(IDF_PATH)/make/component_common.mk - ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) # automatically trigger a git submodule update @@ -44,8 +36,6 @@ $(COMPONENT_LIBRARY): $(ALL_LIB_FILES) # saves us from having to add the target to a Makefile.projbuild $(COMPONENT_LIBRARY): esp32_out.ld -# .. is BUILD_DIR_BASE here, as component makefiles -# are evaluated with CWD=component build dir esp32_out.ld: $(COMPONENT_PATH)/ld/esp32.ld ../include/sdkconfig.h $(CC) -I ../include -C -P -x c -E $< -o $@ diff --git a/components/expat/component.mk b/components/expat/component.mk index 907b358a87..0ee1199d1b 100644 --- a/components/expat/component.mk +++ b/components/expat/component.mk @@ -1,15 +1,9 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the SDK documents if you need to do this. -# COMPONENT_ADD_INCLUDEDIRS := port/include include/expat COMPONENT_SRCDIRS := library port CFLAGS += -Wno-unused-function -DHAVE_EXPAT_CONFIG_H -include $(IDF_PATH)/make/component_common.mk diff --git a/components/freertos/component.mk b/components/freertos/component.mk index 6702d1b95c..0240ea235f 100644 --- a/components/freertos/component.mk +++ b/components/freertos/component.mk @@ -6,4 +6,3 @@ COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) -Wl,--undefined=uxTopUsedPriority COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := include/freertos -include $(IDF_PATH)/make/component_common.mk diff --git a/components/json/component.mk b/components/json/component.mk index 311a902f99..2dd6ea8b87 100644 --- a/components/json/component.mk +++ b/components/json/component.mk @@ -1,13 +1,7 @@ # # Component Makefile # -# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default, -# this will take the sources in this directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the SDK documents if you need to do this. -# COMPONENT_ADD_INCLUDEDIRS := include port/include COMPONENT_SRCDIRS := library port -include $(IDF_PATH)/make/component_common.mk diff --git a/components/log/component.mk b/components/log/component.mk index ef497a7ecb..c2c4c03a1a 100755 --- a/components/log/component.mk +++ b/components/log/component.mk @@ -1,3 +1,5 @@ -COMPONENT_ADD_INCLUDEDIRS := include +# +# Component Makefile +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/components/lwip/component.mk b/components/lwip/component.mk index 5d15020047..49fc644ae4 100644 --- a/components/lwip/component.mk +++ b/components/lwip/component.mk @@ -8,4 +8,3 @@ COMPONENT_SRCDIRS := api apps/sntp apps core/ipv4 core/ipv6 core netif port/free CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable -include $(IDF_PATH)/make/component_common.mk diff --git a/components/mbedtls/component.mk b/components/mbedtls/component.mk index 98838d4d7b..bd7209a926 100644 --- a/components/mbedtls/component.mk +++ b/components/mbedtls/component.mk @@ -6,4 +6,3 @@ COMPONENT_ADD_INCLUDEDIRS := port/include include COMPONENT_SRCDIRS := library port -include $(IDF_PATH)/make/component_common.mk diff --git a/components/newlib/component.mk b/components/newlib/component.mk index 0648946c20..4f567242b6 100644 --- a/components/newlib/component.mk +++ b/components/newlib/component.mk @@ -2,4 +2,3 @@ COMPONENT_ADD_LDFLAGS := $(COMPONENT_PATH)/lib/libc.a $(COMPONENT_PATH)/lib/libm COMPONENT_ADD_INCLUDEDIRS := include platform_include -include $(IDF_PATH)/make/component_common.mk diff --git a/components/nghttp/component.mk b/components/nghttp/component.mk index a4dca5cf70..d2cd0455f5 100644 --- a/components/nghttp/component.mk +++ b/components/nghttp/component.mk @@ -5,5 +5,3 @@ COMPONENT_ADD_INCLUDEDIRS := port/include include COMPONENT_SRCDIRS := library port - -include $(IDF_PATH)/make/component_common.mk \ No newline at end of file diff --git a/components/nvs_flash/component.mk b/components/nvs_flash/component.mk index 02ff8cf038..a905ca689e 100755 --- a/components/nvs_flash/component.mk +++ b/components/nvs_flash/component.mk @@ -6,4 +6,3 @@ COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_SRCDIRS := src -include $(IDF_PATH)/make/component_common.mk diff --git a/components/openssl/component.mk b/components/openssl/component.mk index 2dfcc6b38d..be40549d20 100644 --- a/components/openssl/component.mk +++ b/components/openssl/component.mk @@ -7,4 +7,3 @@ COMPONENT_PRIV_INCLUDEDIRS := include/internal include/platform include/openssl COMPONENT_SRCDIRS := library platform -include $(IDF_PATH)/make/component_common.mk diff --git a/components/spi_flash/component.mk b/components/spi_flash/component.mk index 459da06419..d511eedb8e 100755 --- a/components/spi_flash/component.mk +++ b/components/spi_flash/component.mk @@ -5,4 +5,3 @@ ifdef IS_BOOTLOADER_BUILD COMPONENT_OBJS := spi_flash_rom_patch.o endif -include $(IDF_PATH)/make/component_common.mk diff --git a/components/tcpip_adapter/component.mk b/components/tcpip_adapter/component.mk index a57ae0b12b..c2c4c03a1a 100755 --- a/components/tcpip_adapter/component.mk +++ b/components/tcpip_adapter/component.mk @@ -1,5 +1,5 @@ # # Component Makefile # +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/components/vfs/component.mk b/components/vfs/component.mk index fccf88db8a..c2c4c03a1a 100755 --- a/components/vfs/component.mk +++ b/components/vfs/component.mk @@ -1 +1,5 @@ -include $(IDF_PATH)/make/component_common.mk +# +# Component Makefile +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk index cb6b06652f..b01eb83be9 100644 --- a/components/wpa_supplicant/component.mk +++ b/components/wpa_supplicant/component.mk @@ -2,5 +2,3 @@ COMPONENT_ADD_INCLUDEDIRS := include port/include COMPONENT_SRCDIRS := src/crypto CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing - -include $(IDF_PATH)/make/component_common.mk diff --git a/components/xtensa-debug-module/component.mk b/components/xtensa-debug-module/component.mk index a57ae0b12b..308f64f0ea 100755 --- a/components/xtensa-debug-module/component.mk +++ b/components/xtensa-debug-module/component.mk @@ -1,5 +1,4 @@ # # Component Makefile # - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/01_hello_world/main/component.mk b/examples/01_hello_world/main/component.mk index 24356f23ed..4d3b30caf3 100644 --- a/examples/01_hello_world/main/component.mk +++ b/examples/01_hello_world/main/component.mk @@ -1,10 +1,5 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/examples/02_blink/main/component.mk b/examples/02_blink/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/02_blink/main/component.mk +++ b/examples/02_blink/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/03_http_request/main/component.mk b/examples/03_http_request/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/03_http_request/main/component.mk +++ b/examples/03_http_request/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/04_https_request/main/component.mk b/examples/04_https_request/main/component.mk index 24356f23ed..4d3b30caf3 100644 --- a/examples/04_https_request/main/component.mk +++ b/examples/04_https_request/main/component.mk @@ -1,10 +1,5 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/examples/05_ble_adv/main/component.mk b/examples/05_ble_adv/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/05_ble_adv/main/component.mk +++ b/examples/05_ble_adv/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/06_sntp/main/component.mk b/examples/06_sntp/main/component.mk index 24356f23ed..b4fa72791c 100644 --- a/examples/06_sntp/main/component.mk +++ b/examples/06_sntp/main/component.mk @@ -1,10 +1,4 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# - -include $(IDF_PATH)/make/component_common.mk +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/make/common.mk b/make/common.mk index 6183cfbc46..779811f1ad 100644 --- a/make/common.mk +++ b/make/common.mk @@ -1,7 +1,15 @@ -# Functionality common to both top-level project makefile -# and component makefiles +# Functionality common to both top-level project makefile (project.mk) +# and component makefiles (component_wrapper.mk) # +# Include project config makefile, if it exists. +# +# (Note that we only rebuild this makefile automatically for some +# targets, see project_config.mk for details.) +SDKCONFIG_MAKEFILE ?= $(abspath $(BUILD_DIR_BASE)/include/config/auto.conf) +-include $(SDKCONFIG_MAKEFILE) +export SDKCONFIG_MAKEFILE # sub-makes (like bootloader) will reuse this path + #Handling of V=1/VERBOSE=1 flag # # if V=1, $(summary) does nothing and $(details) will echo extra details diff --git a/make/component_common.mk b/make/component_common.mk index a9598a23ba..32af3c7c06 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -1,135 +1,3 @@ -# Component common makefile -# -# This Makefile gets included in the Makefile of all the components to set the correct include paths etc. -# PWD is the build directory of the component and the top Makefile is the one in the -# component source dir. -# -# The way the Makefile differentiates between those two is by looking at the environment -# variable PROJECT_PATH. If this is set (to the basepath of the project), we're building a -# component and its Makefile has included this makefile. If not, we're building the entire project. -# +$(warning Deprecated feature: It is no longer necessary to include component_common.mk.) +$(warning The line "include $$(IDF_PATH)/make/component_common.mk" can be removed from component.mk files.) -# -# This Makefile requires the environment variable IDF_PATH to be set -# to the top-level directory where ESP-IDF is located (the directory -# containing this 'make' directory). -# - -ifeq ("$(PROJECT_PATH)","") -$(error Make was invoked from $(CURDIR). However please do not run make from the sdk or a component directory; invoke make from the project directory. See the ESP-IDF README for details.) -endif - -# Find the path to the component -COMPONENT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) -export COMPONENT_PATH - -include $(IDF_PATH)/make/common.mk - -#Some of these options are overridable by the component's component.mk Makefile - -#Name of the component -COMPONENT_NAME ?= $(lastword $(subst /, ,$(realpath $(COMPONENT_PATH)))) - -#Absolute path of the .a file -COMPONENT_LIBRARY := lib$(COMPONENT_NAME).a - -#Source dirs a component has. Default to root directory of component. -COMPONENT_SRCDIRS ?= . - -#Object files which need to be linked into the library -#By default we take all .c/.S files in the component directory. -ifeq ("$(COMPONENT_OBJS)", "") -#Find all source files in all COMPONENT_SRCDIRS -COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) -COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) -COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S))) -#Make relative by removing COMPONENT_PATH from all found object paths -COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) -endif - -#By default, include only the include/ dir. -COMPONENT_ADD_INCLUDEDIRS ?= include -COMPONENT_ADD_LDFLAGS ?= -l$(COMPONENT_NAME) - -#If we're called to compile something, we'll get passed the COMPONENT_INCLUDES -#variable with all the include dirs from all the components in random order. This -#means we can accidentally grab a header from another component before grabbing our own. -#To make sure that does not happen, re-order the includes so ours come first. -OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) -COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) - -# macro to generate relative paths inside component_project_vars.mk, whenever possible -# ie put literal $(IDF_PATH), $(PROJECT_PATH) and $(BUILD_DIR_BASE) into the generated -# makefiles where possible. -# -# This means if directories move (breaking absolute paths), don't need to 'make clean' -define MakeRelativePath -$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),\$$(BUILD_DIR_BASE),$(1)))) -endef - -# This target generates component_project_vars.mk for the -# component. This is used to take component.mk variables -# COMPONENT_ADD_INCLUDEDIRS, COMPONENT_ADD_LDFLAGS and -# COMPONENT_DEPENDS and inject those into the project makefile. -# -# The target here has no dependencies, as the parent target in -# project.mk evaluates dependencies before calling down to here. See -# GenerateComponentTargets macro in project.mk. -# -# If you are thinking of editing the output of this makefile for a -# component-specific feature, please don't! What you want is a -# Makefile.projbuild for your component (see docs/build-system.rst for more.) -component_project_vars.mk:: - $(details) "Building component project variables list $(abspath $@)" - @echo '# Automatically generated build file. Do not edit.' > $@ - @echo 'COMPONENT_INCLUDES += $(call MakeRelativePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ - @echo 'COMPONENT_LDFLAGS += $(call MakeRelativePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ - @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ - -#Targets for build/clean. Use builtin recipe if component Makefile -#hasn't defined its own. -ifeq ("$(COMPONENT_OWNBUILDTARGET)", "") -build: $(COMPONENT_LIBRARY) - @mkdir -p $(COMPONENT_SRCDIRS) - -#Build the archive. We remove the archive first, otherwise ar will get confused if we update -#an archive when multiple filenames have the same name (src1/test.o and src2/test.o) -$(COMPONENT_LIBRARY): $(COMPONENT_OBJS) - $(summary) AR $@ - $(Q) rm -f $@ - $(Q) $(AR) cru $@ $(COMPONENT_OBJS) -endif - -CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk - -ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") -clean: - $(summary) RM $(CLEAN_FILES) - $(Q) rm -f $(CLEAN_FILES) -endif - -#Include all dependency files already generated --include $(COMPONENT_OBJS:.o=.d) - -#This pattern is generated for each COMPONENT_SRCDIR to compile the files in it. -define GenerateCompileTargets -# $(1) - directory containing source files, relative to $(COMPONENT_PATH) -$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.c | $(1) - $$(summary) CC $$@ - $$(Q) $$(CC) $$(CFLAGS) $(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ - -$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp | $(1) - $$(summary) CXX $$@ - $$(Q) $$(CXX) $$(CXXFLAGS) $(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ - -$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S | $(1) - $$(summary) AS $$@ - $$(Q) $$(CC) $$(CFLAGS) $(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ - -# CWD is build dir, create the build subdirectory if it doesn't exist -$(1): - @mkdir -p $(1) -endef - -#Generate all the compile target recipes -$(foreach srcdir,$(COMPONENT_SRCDIRS), $(eval $(call GenerateCompileTargets,$(srcdir)))) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk new file mode 100644 index 0000000000..68efe0d211 --- /dev/null +++ b/make/component_wrapper.mk @@ -0,0 +1,169 @@ +# Component wrapper makefile +# +# This makefile gets called recursively from the project make, once for each component. +# COMPONENT_MAKEFILE is set to point at the component.mk file for the component itself, +# which is included as part of this process (after default variables are defined). +# +# This makefile comprises multiple stages, marked in blocked comments below. +# +# CWD is the build directory of the component. + +ifeq ("$(PROJECT_PATH)","") +$(error Make was invoked from $(CURDIR). However please do not run make from the sdk or a component directory; invoke make from the project directory. See the ESP-IDF README for details.) +endif + + +################################################################################ +# 1) Set default variables for the component build (including configuration +# loaded from sdkconfig.) +################################################################################ + +# Find the path to the component +COMPONENT_PATH := $(abspath $(dir $(COMPONENT_MAKEFILE))) +export COMPONENT_PATH + +# COMPONENT_BUILD_DIR is otherwise known as CWD for the build +COMPONENT_BUILD_DIR := $(abspath .) + +# include elements common to both project & component makefiles +# (includes project configuration set via menuconfig) +include $(IDF_PATH)/make/common.mk + +# Some of the following defaults may be overriden by the component's component.mk makefile, +# during the next step: + +# Name of the component +COMPONENT_NAME := $(lastword $(subst /, ,$(realpath $(COMPONENT_PATH)))) + +# Absolute path of the .a file +COMPONENT_LIBRARY = lib$(COMPONENT_NAME).a + +# Source dirs a component has. Default to root directory of component. +COMPONENT_SRCDIRS = . + +# By default, include only the include/ dir. +COMPONENT_ADD_INCLUDEDIRS = include +COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) + + +################################################################################ +# 2) Include the component.mk for the specific component (COMPONENT_MAKEFILE) to +# override variables & optionally define custom targets. +################################################################################ + +include $(COMPONENT_MAKEFILE) + + +################################################################################ +# 3) Set variables that depend on values that may changed by component.mk +################################################################################ + +# Object files which need to be linked into the library +# By default we take all .c, .cpp & .S files in COMPONENT_SRCDIRS. +ifeq ("$(COMPONENT_OBJS)", "") +# Find all source files in all COMPONENT_SRCDIRS +COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) +COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) +COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.S))) +# Make relative by removing COMPONENT_PATH from all found object paths +COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) +endif + +# If we're called to compile something, we'll get passed the COMPONENT_INCLUDES +# variable with all the include dirs from all the components in random order. This +# means we can accidentally grab a header from another component before grabbing our own. +# To make sure that does not happen, re-order the includes so ours come first. +OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS) $(COMPONENT_PRIV_INCLUDEDIRS))) +COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES)) + + +################################################################################ +# 4) Define a target to generate component_project_vars.mk Makefile which +# contains common per-component settings which are included directly in the +# top-level project make +################################################################################ + +# macro to generate variable-relative paths inside component_project_vars.mk, whenever possible +# ie put literal $(IDF_PATH), $(PROJECT_PATH) and $(BUILD_DIR_BASE) into the generated +# makefiles where possible. +# +# This means if directories move (breaking absolute paths), don't need to 'make clean' +define MakeVariablePath +$(subst $(IDF_PATH),$$(IDF_PATH),$(subst $(PROJECT_PATH),$$(PROJECT_PATH),$(subst $(BUILD_DIR_BASE),\$$(BUILD_DIR_BASE),$(1)))) +endef + +# component_project_vars.mk target for the component. This is used to +# take component.mk variables COMPONENT_ADD_INCLUDEDIRS, +# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject those into +# the project make pass. +# +# The target here has no dependencies, as the parent target in +# project.mk evaluates dependencies before calling down to here. See +# GenerateComponentTargets macro in project.mk. +# +# If you are thinking of editing the output of this target for a +# component-specific feature, please don't! What you want is a +# Makefile.projbuild for your component (see docs/build-system.rst for +# more.) +component_project_vars.mk:: + $(details) "Building component project variables list $(abspath $@)" + @echo '# Automatically generated build file. Do not edit.' > $@ + @echo 'COMPONENT_INCLUDES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ + @echo 'COMPONENT_LDFLAGS += $(call MakeVariablePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ + @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ + + +################################################################################ +# 5) If COMPONENT_OWNBUILDTARGET / COMPONENT_OWNCLEANTARGET is not set by component.mk, +# define default build, clean, etc. targets +################################################################################ + +# If COMPONENT_OWNBUILDTARGET is not set, define a phony build target and +# a COMPONENT_LIBRARY link target. +ifeq ("$(COMPONENT_OWNBUILDTARGET)", "") +.PHONY: build +build: $(COMPONENT_LIBRARY) + @mkdir -p $(COMPONENT_SRCDIRS) + +# Build the archive. We remove the archive first, otherwise ar will get confused if we update +# an archive when multiple filenames have the same name (src1/test.o and src2/test.o) +$(COMPONENT_LIBRARY): $(COMPONENT_OBJS) + $(summary) AR $@ + $(Q) rm -f $@ + $(Q) $(AR) cru $@ $(COMPONENT_OBJS) +endif + +# If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target +ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") +CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk +.PHONY: clean +clean: + $(summary) RM $(CLEAN_FILES) + $(Q) rm -f $(CLEAN_FILES) +endif + +# Include all dependency files already generated +-include $(COMPONENT_OBJS:.o=.d) + +# This pattern is generated for each COMPONENT_SRCDIR to compile the files in it. +define GenerateCompileTargets +# $(1) - directory containing source files, relative to $(COMPONENT_PATH) +$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.c | $(1) + $$(summary) CC $$@ + $$(Q) $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + +$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp | $(1) + $$(summary) CXX $$@ + $$(Q) $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + +$(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S | $(1) + $$(summary) AS $$@ + $$(Q) $$(CC) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + +# CWD is build dir, create the build subdirectory if it doesn't exist +$(1): + @mkdir -p $(1) +endef + +# Generate all the compile target patterns +$(foreach srcdir,$(COMPONENT_SRCDIRS), $(eval $(call GenerateCompileTargets,$(srcdir)))) diff --git a/make/project.mk b/make/project.mk index 17d9e99c0c..f9cf20a329 100644 --- a/make/project.mk +++ b/make/project.mk @@ -48,80 +48,73 @@ PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) export PROJECT_PATH endif -COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/component_common.mk) +# A list of the "common" makefiles, to use as a target dependency +COMMON_MAKEFILES := $(abspath $(IDF_PATH)/make/project.mk $(IDF_PATH)/make/common.mk $(IDF_PATH)/make/component_wrapper.mk) export COMMON_MAKEFILES -#The directory where we put all objects/libraries/binaries. The project Makefile can -#configure this if needed. +# The directory where we put all objects/libraries/binaries. The project Makefile can +# configure this if needed. BUILD_DIR_BASE ?= $(PROJECT_PATH)/build export BUILD_DIR_BASE -ifndef IS_BOOTLOADER_BUILD -# Include project config file, if it exists. -# (bootloader build doesn't need this, config is exported from top-level) -# -# (Note that we only rebuild auto.conf automatically for some targets, -# see project_config.mk for details.) -# -SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf --include $(SDKCONFIG_MAKEFILE) -export $(filter CONFIG_%,$(.VARIABLES)) -endif - -#Component directories. These directories are searched for components. -#The project Makefile can override these component dirs, or define extra component directories. +# Component directories. These directories are searched for components. +# The project Makefile can override these component dirs, or define extra component directories. COMPONENT_DIRS ?= $(PROJECT_PATH)/components $(EXTRA_COMPONENT_DIRS) $(IDF_PATH)/components export COMPONENT_DIRS -#The project Makefile can define a list of components, but if it does not do this we just take -#all available components in the component dirs. +# Source directories of the project itself (a special, project-specific component.) Defaults to only "main". +SRCDIRS ?= main + +# The project Makefile can define a list of components, but if it does not do this we just take +# all available components in the component dirs. ifeq ("$(COMPONENTS)","") -#Find all component names. The component names are the same as the -#directories they're in, so /bla/components/mycomponent/ -> mycomponent. We later use -#the COMPONENT_DIRS bit to find back the component path. +# Find all component names. The component names are the same as the +# directories they're in, so /bla/components/mycomponent/ -> mycomponent. We then use +# COMPONENT_DIRS to build COMPONENT_PATHS with the full path to each component. COMPONENTS := $(foreach dir,$(COMPONENT_DIRS),$(wildcard $(dir)/*)) COMPONENTS := $(sort $(foreach comp,$(COMPONENTS),$(lastword $(subst /, ,$(comp))))) endif export COMPONENTS -#Sources default to only "main" -SRCDIRS ?= main - -#Here, we resolve and add all the components and source paths into absolute paths. -#If a component exists in multiple COMPONENT_DIRS, we take the first match. -#WARNING: These directories paths must be generated WITHOUT a trailing / so we -#can use $(notdir x) to get the component name. +# Resolve all of COMPONENTS into absolute paths in COMPONENT_PATHS. +# +# If a component name exists in multiple COMPONENT_DIRS, we take the first match. +# +# NOTE: These paths must be generated WITHOUT a trailing / so we +# can use $(notdir x) to get the component name. COMPONENT_PATHS := $(foreach comp,$(COMPONENTS),$(firstword $(foreach dir,$(COMPONENT_DIRS),$(wildcard $(dir)/$(comp))))) COMPONENT_PATHS += $(abspath $(SRCDIRS)) -#A component is buildable if it has a component.mk makefile; we assume that a -# 'make -C $(component dir) -f component.mk build' results in a lib$(componentname).a +# A component is buildable if it has a component.mk makefile in it COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(cp)/component.mk),$(cp))) -# Assemble global list of include dirs (COMPONENT_INCLUDES), and -# LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. +# Initialise a project-wide list of include dirs (COMPONENT_INCLUDES), +# and LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. +# +# These variables are built up via the component_project_vars.mk +# generated makefiles (one per component). COMPONENT_INCLUDES := COMPONENT_LDFLAGS := -# include paths for generated "component project variables" targets with -# COMPONENT_INCLUDES, COMPONENT_LDFLAGS & dependency targets +# COMPONENT_PROJECT_VARS is the list of component_project_vars.mk generated makefiles +# for each component. # -# See component_project_vars.mk target in component_common.mk +# Including $(COMPONENT_PROJECT_VARS) builds the COMPONENT_INCLUDES, +# COMPONENT_LDFLAGS variables and also targets for any inter-component +# dependencies. +# +# See the component_project_vars.mk target in component_wrapper.mk COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE))) COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) include $(COMPONENT_PROJECT_VARS) -#Also add project include path, for top-level includes +# Also add top-level project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) export COMPONENT_INCLUDES -export COMPONENT_LDFLAGS -#Make sure submakes can also use this. -export PROJECT_PATH - -#Include functionality common to both project & component --include $(IDF_PATH)/make/common.mk +# Set variables common to both project & component +include $(IDF_PATH)/make/common.mk # Set default LDFLAGS @@ -198,15 +191,15 @@ CXXFLAGS := $(strip \ export CFLAGS CPPFLAGS CXXFLAGS -#Set host compiler and binutils +# Set host compiler and binutils HOSTCC := $(CC) HOSTLD := $(LD) HOSTAR := $(AR) HOSTOBJCOPY := $(OBJCOPY) export HOSTCC HOSTLD HOSTAR HOSTOBJCOPY -#Set target compiler. Defaults to whatever the user has -#configured as prefix + yer olde gcc commands +# Set target compiler. Defaults to whatever the user has +# configured as prefix + ye olde gcc commands CC := $(call dequote,$(CONFIG_TOOLPREFIX))gcc CXX := $(call dequote,$(CONFIG_TOOLPREFIX))c++ LD := $(call dequote,$(CONFIG_TOOLPREFIX))ld @@ -230,10 +223,10 @@ COMPONENT_PATH := $(1) endef $(foreach componentpath,$(COMPONENT_PATHS),$(eval $(call includeProjBuildMakefile,$(componentpath)))) -ifndef IS_BOOTLOADER_BUILD # once we know component paths, we can include the config generation targets # # (bootloader build doesn't need this, config is exported from top-level) +ifndef IS_BOOTLOADER_BUILD include $(IDF_PATH)/make/project_config.mk endif @@ -265,12 +258,14 @@ $(BUILD_DIR_BASE): # # Is recursively expanded by the GenerateComponentTargets macro define ComponentMake -$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(1)/component.mk COMPONENT_PATH=$(1) COMPONENT_BUILD_DIR=$(BUILD_DIR_BASE)/$(2) +$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk endef -define GenerateComponentTargets +# Generate top-level component-specific targets for each component # $(1) - path to component dir # $(2) - name of component +# +define GenerateComponentTargets .PHONY: $(2)-build $(2)-clean $(2)-build: @@ -289,10 +284,19 @@ $(BUILD_DIR_BASE)/$(2): $(BUILD_DIR_BASE)/$(2)/lib$(2).a: $(2)-build $(details) "Target '$$^' responsible for '$$@'" # echo which build target built this file -# add a target to generate the component_project_vars.mk files -# that are used to inject variables into project make pass (see -# component_project_vars.mk target in component_common.mk). -$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) +# add a target to generate the component_project_vars.mk files that +# are used to inject variables into project make pass (see matching +# component_project_vars.mk target in component_wrapper.mk). +# +# If any component_project_vars.mk file is out of date, the make +# process will call this target to rebuild it and then restart. +# +# Note: $(SDKCONFIG) is a normal prereq as we need to rebuild these +# files whenever the config changes. $(SDKCONFIG_MAKEFILE) is an +# order-only prereq because if it hasn't been rebuilt, we need to +# build it first - but including it as a normal prereq can lead to +# infinite restarts as the conf process will keep updating it. +$(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAKEFILES) $(SDKCONFIG) | $(BUILD_DIR_BASE)/$(2) $(SDKCONFIG_MAKEFILE) $(call ComponentMake,$(1),$(2)) component_project_vars.mk endef diff --git a/make/project_config.mk b/make/project_config.mk index 555086fb8d..09cec2e0be 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -42,7 +42,7 @@ defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) # Work out of whether we have to build the Kconfig makefile # (auto.conf), or if we're in a situation where we don't need it NON_CONFIG_TARGETS := clean %-clean help menuconfig defconfig -AUTO_CONF_REGEN_TARGET := $(BUILD_DIR_BASE)/include/config/auto.conf +AUTO_CONF_REGEN_TARGET := $(SDKCONFIG_MAKEFILE) # disable AUTO_CONF_REGEN_TARGET if all targets are non-config targets # (and not building default target) @@ -50,7 +50,7 @@ ifneq ("$(MAKECMDGOALS)","") ifeq ($(filter $(NON_CONFIG_TARGETS), $(MAKECMDGOALS)),$(MAKECMDGOALS)) AUTO_CONF_REGEN_TARGET := # dummy target -$(BUILD_DIR_BASE)/include/config/auto.conf: +$(SDKCONFIG_MAKEFILE): endif endif From f7cb6fc9690074674ad4a0e4d302131787b77253 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 9 Nov 2016 20:26:28 +0100 Subject: [PATCH 170/285] NVS Examples update Updated previusly submitted example, saved it as "07_nvs_rw_value" and added a new one. Overview of examples: * 07_nvs_rw_value - simple read and write a single value * 08_nvs_rw_blob - read and write a blob that is extened with new data on each restart of ESP32 Removed LICENSE files --- examples/07_nvs_read_write/README.md | 7 - .../07_nvs_read_write/main/nvs_read_write.c | 62 ------ .../Makefile | 2 +- examples/07_nvs_rw_value/README.md | 13 ++ .../main/component.mk | 0 examples/07_nvs_rw_value/main/nvs_rw_value.c | 77 ++++++++ examples/08_nvs_rw_blob/Makefile | 9 + examples/08_nvs_rw_blob/README.md | 14 ++ examples/08_nvs_rw_blob/main/component.mk | 10 + examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 183 ++++++++++++++++++ 10 files changed, 307 insertions(+), 70 deletions(-) delete mode 100644 examples/07_nvs_read_write/README.md delete mode 100644 examples/07_nvs_read_write/main/nvs_read_write.c rename examples/{07_nvs_read_write => 07_nvs_rw_value}/Makefile (83%) create mode 100644 examples/07_nvs_rw_value/README.md rename examples/{07_nvs_read_write => 07_nvs_rw_value}/main/component.mk (100%) create mode 100644 examples/07_nvs_rw_value/main/nvs_rw_value.c create mode 100644 examples/08_nvs_rw_blob/Makefile create mode 100644 examples/08_nvs_rw_blob/README.md create mode 100644 examples/08_nvs_rw_blob/main/component.mk create mode 100644 examples/08_nvs_rw_blob/main/nvs_rw_blob.c diff --git a/examples/07_nvs_read_write/README.md b/examples/07_nvs_read_write/README.md deleted file mode 100644 index bac8ee8d54..0000000000 --- a/examples/07_nvs_read_write/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Non-Volatile Storage (NVS) Read and Write Example - -Demonstrates how to read and write a value using NVS. The value tracks number of ESP32 module restarts. - -Example also shows how to use basic diagnostics if read / write operation was successful. - -See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/07_nvs_read_write/main/nvs_read_write.c b/examples/07_nvs_read_write/main/nvs_read_write.c deleted file mode 100644 index 40d330f62f..0000000000 --- a/examples/07_nvs_read_write/main/nvs_read_write.c +++ /dev/null @@ -1,62 +0,0 @@ -/* Non-Volatile Storage (NVS) Read and Write Example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" -#include "nvs_flash.h" -#include "nvs.h" - -void app_main() -{ - nvs_flash_init(); - system_init(); - - nvs_handle handle_to_settings; - esp_err_t err; - int32_t restart_counter = 0; - - // Open the NVS - printf("Opening Non-Volatile Storage (NVS) ... "); - err = nvs_open("settings", NVS_READWRITE, &handle_to_settings); - printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); - - // Read from the NVS - printf("Reading restart counter from NVS ... "); - err = nvs_get_i32(handle_to_settings, "restart_conter", &restart_counter); - switch (err) { - case ESP_OK: - printf("OK\n"); - printf("Restart counter = %d\n", restart_counter); - break; - case ESP_ERR_NVS_NOT_FOUND: - printf("The counter is not initialized yet!\n"); - break; - default : - printf("Error (%d) reading!\n", err); - } - - // Write to the NVS - printf("Updating restart counter in NVS ... "); - restart_counter++; - err = nvs_set_i32(handle_to_settings, "restart_conter", restart_counter); - printf((err != ESP_OK) ? "Failed!\n" : "OK\n"); - - // Close the NVS - nvs_close(handle_to_settings); - - // Restart module - for (int i = 10; i >= 0; i--) { - printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); - } - printf("Restarting now.\n"); - fflush(stdout); - system_restart(); -} diff --git a/examples/07_nvs_read_write/Makefile b/examples/07_nvs_rw_value/Makefile similarity index 83% rename from examples/07_nvs_read_write/Makefile rename to examples/07_nvs_rw_value/Makefile index 3d6adb4d02..fdfc09c574 100644 --- a/examples/07_nvs_read_write/Makefile +++ b/examples/07_nvs_rw_value/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -PROJECT_NAME := nvs-read-write +PROJECT_NAME := nvs-rw-value include $(IDF_PATH)/make/project.mk diff --git a/examples/07_nvs_rw_value/README.md b/examples/07_nvs_rw_value/README.md new file mode 100644 index 0000000000..83dc29fd18 --- /dev/null +++ b/examples/07_nvs_rw_value/README.md @@ -0,0 +1,13 @@ +# Non-Volatile Storage (NVS) Read and Write Example + +Demonstrates how to read and write a single integer value using NVS. + +The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts. + +Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVR. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. + +Check another example *08_nvs_rw_blob*, that shows how to read and write a blob (binary large object). + +Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html). + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/07_nvs_read_write/main/component.mk b/examples/07_nvs_rw_value/main/component.mk similarity index 100% rename from examples/07_nvs_read_write/main/component.mk rename to examples/07_nvs_rw_value/main/component.mk diff --git a/examples/07_nvs_rw_value/main/nvs_rw_value.c b/examples/07_nvs_rw_value/main/nvs_rw_value.c new file mode 100644 index 0000000000..df53d33b4a --- /dev/null +++ b/examples/07_nvs_rw_value/main/nvs_rw_value.c @@ -0,0 +1,77 @@ +/* Non-Volatile Storage (NVS) Read and Write a Value - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" + +void app_main() +{ + nvs_flash_init(); + + nvs_handle my_handle; + esp_err_t err; + + printf("\n"); + + // Open + printf("Opening Non-Volatile Storage (NVS) ... "); + err = nvs_open("storage", NVS_READWRITE, &my_handle); + if (err != ESP_OK) { + printf("Error (%d) opening NVS!\n", err); + } else { + printf("Done\n"); + + // Read + printf("Reading restart counter from NVS ... "); + int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_i32(my_handle, "restart_conter", &restart_counter); + switch (err) { + case ESP_OK: + printf("Done\n"); + printf("Restart counter = %d\n", restart_counter); + break; + case ESP_ERR_NVS_NOT_FOUND: + printf("The value is not initialized yet!\n"); + break; + default : + printf("Error (%d) reading!\n", err); + } + + // Write + printf("Updating restart counter in NVS ... "); + restart_counter++; + err = nvs_set_i32(my_handle, "restart_conter", restart_counter); + printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); + + // Commit + printf("Committing updates in NVS ... "); + err = nvs_commit(my_handle); + printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); + + // Close + nvs_close(my_handle); + } + + printf("\n"); + + // Restart module + for (int i = 10; i >= 0; i--) { + printf("Restarting in %d seconds...\n", i); + vTaskDelay(1000 / portTICK_RATE_MS); + } + printf("Restarting now.\n"); + fflush(stdout); + system_restart(); +} diff --git a/examples/08_nvs_rw_blob/Makefile b/examples/08_nvs_rw_blob/Makefile new file mode 100644 index 0000000000..717744bbe8 --- /dev/null +++ b/examples/08_nvs_rw_blob/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := nvs-rw-blob + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/08_nvs_rw_blob/README.md b/examples/08_nvs_rw_blob/README.md new file mode 100644 index 0000000000..81a0e36c71 --- /dev/null +++ b/examples/08_nvs_rw_blob/README.md @@ -0,0 +1,14 @@ +# Non-Volatile Storage (NVS) Read and Write Example + +Demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP32 module restarts. + + * value - tracks number of ESP32 module soft and hard restarts. + * blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. New run time is added to the table on each manually triggered soft restart and written back to NVS. Triggering is done by pulling down GPIO0. + +Example also shows how to implement diagnostics if read / write operation was successful. + +If not done already, consider checking simpler example *07_nvs_rw_value*, that has been used as a starting point for preparing this one. + +Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html). + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/08_nvs_rw_blob/main/component.mk b/examples/08_nvs_rw_blob/main/component.mk new file mode 100644 index 0000000000..24356f23ed --- /dev/null +++ b/examples/08_nvs_rw_blob/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c new file mode 100644 index 0000000000..b4ff7e98ac --- /dev/null +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -0,0 +1,183 @@ +/* Non-Volatile Storage (NVS) Read and Write a Blob - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "nvs.h" +#include "driver/gpio.h" + +#define STORAGE_NAMESPACE "storage" + +/* Save the number of module restarts in NVS + by first reading and then incrementing + the number that has been saved previously. + Return an error if anything goes wrong + during this process. + */ +esp_err_t save_restart_counter(void) +{ + nvs_handle my_handle; + esp_err_t err; + + // Open + err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Read + int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_i32(my_handle, "restart_conter", &restart_counter); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + + // Write + restart_counter++; + err = nvs_set_i32(my_handle, "restart_conter", restart_counter); + if (err != ESP_OK) return err; + + // Commit + err = nvs_commit(my_handle); + if (err != ESP_OK) return err; + + // Close + nvs_close(my_handle); + return ESP_OK; +} + +/* Save new run time value in NVS + by first reading a table of previously saved values + and then adding the new value at the end of the table. + Return an error if anything goes wrong + during this process. + */ +esp_err_t save_run_time(void) +{ + nvs_handle my_handle; + esp_err_t err; + + // Open + err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Read the size of memory space required for blob + size_t required_size = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_blob(my_handle, "run_time", NULL, &required_size); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + + // Read previously saved blob if available + uint32_t* run_time; + if (required_size > 0) { + run_time = malloc(required_size); + // read previously saved blob + err = nvs_get_blob(my_handle, "run_time", run_time, &required_size); + if (err != ESP_OK) return err; + // add extra space for the new value + required_size += sizeof(uint32_t); + run_time = realloc(run_time, required_size); + } else { + // nothing saved jet - just allocate space for the first value to save + required_size = sizeof(uint32_t); + run_time = malloc(required_size); + } + + // Write value including previously saved blob if available + run_time[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS; + err = nvs_set_blob(my_handle, "run_time", run_time, required_size); + if (err != ESP_OK) return err; + + free(run_time); + + // Commit + err = nvs_commit(my_handle); + if (err != ESP_OK) return err; + + // Close + nvs_close(my_handle); + return ESP_OK; +} + +/* Read from NVS and print restart counter + and the table with run times. + Return an error if anything goes wrong + during this process. + */ +esp_err_t print_what_saved(void) +{ + nvs_handle my_handle; + esp_err_t err; + + // Open + err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle); + if (err != ESP_OK) return err; + + // Read restart counter + int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS + err = nvs_get_i32(my_handle, "restart_conter", &restart_counter); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + printf("Restart counter = %d\n", restart_counter); + + // Read run time blob + size_t required_size = 0; // value will default to 0, if not set yet in NVS + // obtain required memory space to store blob being read from NVS + err = nvs_get_blob(my_handle, "run_time", NULL, &required_size); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; + printf("Run time:\n"); + if (required_size == 0) { + printf("Nothing saved yet!\n"); + } else { + uint32_t* run_time = malloc(required_size); + err = nvs_get_blob(my_handle, "run_time", run_time, &required_size); + if (err != ESP_OK) return err; + for (int i = 0; i < required_size / sizeof(uint32_t); i++) { + printf("%d: %d\n", i + 1, run_time[i]); + } + free(run_time); + } + + // Close + nvs_close(my_handle); + return ESP_OK; +} + + +void app_main() +{ + nvs_flash_init(); + + esp_err_t err; + + err = print_what_saved(); + if (err != ESP_OK) printf("Error (%d) reading data from NVS!\n", err); + + err = save_restart_counter(); + if (err != ESP_OK) printf("Error (%d) saving restart counter to NVS!\n", err); + + gpio_pad_select_gpio(GPIO_NUM_0); + gpio_set_direction(GPIO_NUM_0, GPIO_MODE_DEF_INPUT); + + /* Read the status of GPIO0. If GPIO0 is LOW for longer than 1000 ms, + then save module's run time and restart it + */ + while (1) { + if (gpio_get_level(GPIO_NUM_0) == 0) { + vTaskDelay(1000 / portTICK_RATE_MS); + if(gpio_get_level(GPIO_NUM_0) == 0) { + err = save_run_time(); + if (err != ESP_OK) printf("Error (%d) saving run time blob to NVS!\n", err); + printf("Restarting...\n"); + fflush(stdout); + system_restart(); + } + } + vTaskDelay(200 / portTICK_RATE_MS); + } +} From 3aca537157d7e8fc0d88be780260e4e1c134a897 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 07:37:16 +0100 Subject: [PATCH 171/285] Optimsed code Following note in review by @igrr :+1: --- examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c index b4ff7e98ac..38066bf62f 100644 --- a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -74,22 +74,14 @@ esp_err_t save_run_time(void) if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err; // Read previously saved blob if available - uint32_t* run_time; + uint32_t* run_time = malloc(required_size + sizeof(uint32_t)); if (required_size > 0) { - run_time = malloc(required_size); - // read previously saved blob err = nvs_get_blob(my_handle, "run_time", run_time, &required_size); if (err != ESP_OK) return err; - // add extra space for the new value - required_size += sizeof(uint32_t); - run_time = realloc(run_time, required_size); - } else { - // nothing saved jet - just allocate space for the first value to save - required_size = sizeof(uint32_t); - run_time = malloc(required_size); } // Write value including previously saved blob if available + required_size += sizeof(uint32_t); run_time[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS; err = nvs_set_blob(my_handle, "run_time", run_time, required_size); if (err != ESP_OK) return err; From 25db1effd6f7673bac7f4eef290f6cd7a0556485 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 15:52:47 +1100 Subject: [PATCH 172/285] docs: Rewrite build system docs to address new system, plus general editing Fixes TW#8017 --- docs/build_system.rst | 491 ++++++++++++++++++++++++++---------------- 1 file changed, 304 insertions(+), 187 deletions(-) diff --git a/docs/build_system.rst b/docs/build_system.rst index 34db487e0a..a9ebcfe004 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -8,262 +8,368 @@ Read this document if you want to know how to organise a new ESP-IDF project. We recommend using the esp-idf-template_ project as a starting point for your project. +Using the Build System +====================== + +The esp-idf README file contains a description of how to use the build system to build your project. + Overview ======== -An ESP-IDF project can be seen as an almagation of a number of components. -For example, for a webserver that shows the current humidity, we would -have: +An ESP-IDF project can be seen as an amalgamation of a number of components. +For example, for a webserver that shows the current humidity, there could be: - The ESP32 base libraries (libc, rom bindings etc) - The WiFi drivers - A TCP/IP stack - The FreeRTOS operating system - A webserver -- A driver for an humidity sensor +- A driver for the humidity sensor - Main code tying it all together -ESP-IDF makes these components explicit and configurable. To do that, when a project -is compiled, the build environment will look up all the components in the -ESP-IDF directories, the project directories and optionally custom other component -directories. It then allows the user to configure compile-time options using -a friendly text-based menu system to customize the ESP-IDF as well as other components -to the requirements of the project. After the components are customized, the -build process will compile everything into an output file, which can then be uploaded -into a board in a way that can also be defined by components. +ESP-IDF makes these components explicit and configurable. To do that, +when a project is compiled, the build environment will look up all the +components in the ESP-IDF directories, the project directories and +(optionally) in additional custom component directories. It then +allows the user to configure the ESP-IDF project using a a text-based +menu system to customize each component. After the components in the +project are configured, the build process will compile the project. -A project in this sense is defined as a directory under which all the files required -to build it live, excluding the ESP-IDF files and the toolchain. A simple project -tree might look like this:: +Concepts +-------- - - myProject/ - build/ +- A "project" is a directory that contains all the files and configuration to build a single "app" (executable), as well as additional supporting output such as a partition table, data/filesystem partitions, and a bootloader. + +- "Project configuration" is held in a single file called sdkconfig in the root directory of the project. This configuration file is modified via ``make menuconfig`` to customise the configuration of the project. A single project contains exactly one project configuration. + +- An "app" is an executable which is built by esp-idf. A single project will usually build two apps - a "project app" (the main executable, ie your custom firmware) and a "bootloader app" (the initial bootloader program which launches the project app). + +- "components" are modular pieces of standalone code which are compiled into static libraries (.a files) and linked into an app. Some are provided by esp-idf itself, others may be sourced from other places. + +Some things are not part of the project: + +- "ESP-IDF" is not part of the project. Instead it is standalone, and linked to the project via the ``IDF_PATH`` environment variable which holds the path of the ``esp-idf`` directory. This allows the IDF framework to be decoupled from your project. + +- The toolchain for compilation is not part of the project. The toolchain should be installed in the system command line PATH, or the path to the toolchain can be set as part of the compiler prefix in the project configuration. + + +Example Project +--------------- + +An example project directory tree might look like this:: + - myProject/ + - Makefile + - sdkconfig - components/ - component1/ - component.mk - Kconfig - src1.c - component2/ - component.mk - Kconfig - src1.c + - include/ + - component2.h - main/ - src1.c - src2.c - - Makefile + - component.mk + - build/ -As we can see, a project consists of a components/ subdirectory containing its -components as well as one or more directories containing the project-specific -sources; by default a single directory called 'main' is assumed. The project -directory will also have a Makefile where the projects name as well as optionally -other options are defined. After compilation, the project directory will contain -a 'build'-directory containing all of the objects, libraries and other generated -files as well as the final binary. +This example "myProject" contains the following elements: -Components also have a custom makefile - ``component.mk``. This contains various definititions -influencing the build process of the component as well as the project it's used -in. Components may also include a Kconfig file defining the compile-time options that are -settable by means of the menu system. +- A top-level project Makefile. This Makefile set the ``PROJECT_NAME`` variable and (optionally) defines + other project-wide make variables. It includes the core ``$(IDF_PATH)/make/project.mk`` makefile which + implements the rest of the ESP-IDF build system. -Project Makefile variables that can be set by the programmer:: +- "sdkconfig" project configuration file. This file is created/updated when "make menuconfig" runs, and holds configuration for all of the components in the project (including esp-idf itself). The "sdkconfig" file may or may not be added to the source control system of the project. - PROJECT_NAME: Mandatory. Name for the project - BUILD_DIR_BASE: Set the directory where all objects/libraries/binaries end up in. - Defaults to $(PROJECT_PATH)/build - COMPONENT_DIRS: Search path for components. Defaults to the component/ directories - in the ESP-IDF path and the project path. - COMPONENTS: A list of component names. Defaults to all the component found in the - COMPONENT_DIRS directory - EXTRA_COMPONENT_DIRS: Defaults to unset. Use this to add directories to the default - COMPONENT_DIRS. - SRCDIRS: Directories under the project dir containing project-specific sources. - Defaults to 'main'. These are treated as 'lite' components: they do not have - include directories that are passed to the compilation pass of all components and - they do not have a Kconfig option. +- Optional "components" directory contains components that are part of the project. A project does not have to contain custom components of this kind, but it can be useful for structuring reusable code or including third party components that aren't part of ESP-IDF. -Component-specific component.mk variables that can be set by the programmer:: +- "main" directory is a special "pseudo-component" that contains source code for the project itself. "main" is a default name, the Makefile variable ``SRCDIRS`` defaults to this but can be set to look for pseudo-components in other directories. - COMPONENT_ADD_INCLUDEDIRS: Relative path to include directories to be added to - the entire project. If an include directory is only needed to compile this - specific component, don't add it here. - COMPONENT_PRIV_INCLUDEDIRS: Relative path to include directories that are only used - when compiling this specific component. - COMPONENT_DEPENDS: Names of any components that need to be compiled before this component. - COMPONENT_ADD_LDFLAGS: LD flags to add for the entire project. Defaults to -l$(COMPONENT_NAME). - Add libraries etc in the current directory as $(abspath libwhatever.a) - COMPONENT_EXTRA_INCLUDES: Any extra include paths used when compiling the component's - source files. These will be prefixed with '-I' and passed to the compiler. - Similar to COMPONENT_PRIV_INCLUDEDIRS, but these paths are passed as-is instead of - expanded relative to the component directory. - COMPONENT_SRCDIRS: Relative directories to look in for sources. Defaults to '.', the current - directory (the root of the component) only. Use this to specify any subdirectories. Note - that specifying this overwrites the default action of compiling everything in the - components root dir; to keep this behaviour please also add '.' as a directory in this - list. - COMPONENT_OBJS: Object files to compile. Defaults to the .o variants of all .c and .S files - that are found in COMPONENT_SRCDIRS. - COMPONENT_EXTRA_CLEAN: Files that are generated using rules in the components Makefile - that also need to be cleaned - COMPONENT_BUILDRECIPE: Recipe to build the component. Optional. Defaults to building all - COMPONENT_OBJS and linking them into lib(componentname).a - COMPONENT_CLEANRECIPE: Recipe to clean the component. Optional. Defaults to removing - all built objects and libraries. - COMPONENT_BUILD_DIR: Equals the cwd of the component build, which is the build dir - of the component (where all the .o etc files should be created). +- "build" directory is where build output is created. After the make process is run, this directory will contain interim object files and libraries as well as final binary output files. This directory is usually not added to source control or distributed with the project source code. -These variables are already set early on in the Makefile and the values in it will -be usable in component or project Makefiles:: +Component directories contain a component makefile - ``component.mk``. This may contain variable definitions +to control the build process of the component, and its integration into the overall project. See `Component Makefiles` for more details. - CC, LD, AR, OBJCOPY: Xtensa gcc tools - HOSTCC, HOSTLD etc: Host gcc tools - LDFLAGS, CFLAGS: Set to usable values as defined in ESP-IDF Makefile - PROJECT_NAME: Name of the project, as set in project makefile - PROJECT_PATH: Path to the root of the project folder - COMPONENTS: Name of the components to be included - CONFIG_*: All values set by 'make menuconfig' have corresponding Makefile variables. +Each component may also include a ``Kconfig`` file defining the `component configuration` options that can be set via the project configuration. Some components may also include ``Kconfig.projbuild`` and ``Makefile.projbuild`` files, which are special files for `overriding parts of the project`. -Inside your component's component.mk makefile, you can override or add to these variables -as necessary. The changes are isolated from other components (see Makefile.projbuild below -if you want to share these changes with all other components.) +Project Makefiles +----------------- -For components, there also are these defines:: +Each project has a single Makefile that contains build settings for the entire project. By default, the project Makefile can be quite minimal. - COMPONENT_PATH: Absolute path to the root of the source tree of the component we're - compiling - COMPONENT_LIBRARY: The full path to the static library the components compilation pass - is supposed to generate +Minimal Example Makefile +^^^^^^^^^^^^^^^^^^^^^^^^ -Make Process ------------- +:: + PROJECT_NAME := myProject + + include $(IDF_PATH)/make/project.mk -The Make process is always invoked from the project directory by the -user; invoking it anywhere else gives an error. This is what happens if -we build a binary: -The Makefile first determines how it was included. It figures out -various paths as well as the components available to it. It will also -collect the ldflags and includes that the components specify they need. -It does this by running a dummy make on the components with a "get_variable" -target that will output these values. +Mandatory Project Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The Makefile will then create targets to build the lib*.a libraries of -all components and make the elf target depend on this. The main Makefile -invokes Make on the componen.mk of each components inside a sub-mke: this way -the components have full freedom to do whatever is necessary to build -the library without influencing other components. By default, the -component.mk includes the utility makefile $(IDF_PATH)/make/component_common.mk. -This provides default targets and configurations that will work -out-of-the-box for most projects. +- ``PROJECT_NAME``: Name of the project. Binary output files will use this name - ie myProject.bin, myProject.elf. -KConfig -------- +Optional Project Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^ -Each component can also have a Kconfig file, alongside the component.mk, that contains -details to add to "menuconfig" for this component. +These variables all have default values that can be overridden for custom behaviour. Look in ``make/project.mk`` for all of the implementation details. + +- ``PROJECT_PATH``: Top-level project directory. Defaults to the directory containing the Makefile. Many other project variables are based on this variable. The project path cannot contain spaces. +- ``BUILD_DIR_BASE``: The build directory for all objects/libraries/binaries. Defaults to ``$(PROJECT_PATH)/build``. +- ``COMPONENT_DIRS``: Directories to search for components. Defaults to `$(IDF_PATH)/components`, `$(PROJECT_PATH)/components` and ``EXTRA_COMPONENT_DIRS``. Override this variable if you don't want to search for components in the esp-idf & project ``components`` directories. +- ``EXTRA_COMPONENT_DIRS``: Optional list of additional directories to search for components. Components themselves are in sub-directories of these directories, this is a top-level directory containing the component directories. +- ``COMPONENTS``: A list of component names to build into the project. Defaults to all components found in the COMPONENT_DIRS directories. +- ``SRCDIRS``: Directories under the main project directory which contain project-specific "pseudo-components". Defaults to 'main'. The difference between specifying a directory here and specifying it under ``EXTRA_COMPONENT_DIRS`` is that a directory in ``SRCDIRS`` is a component itself (contains a file "component.mk"), whereas a directory in ``EXTRA_COMPONENT_DIRS`` contains component directories which contain a file "component.mk". See the `Example Project` for a concrete case of this. + + +Component Makefiles +------------------- + +Each project contains one or more components, which can either be part of esp-idf or added from other component directories. + +A component is any sub-directory that contains a `component.mk` file.[#f1]_. + +Minimal Component Makefile +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set: +- All source files in the same directory as the makefile (*.c, *.cpp, *.S) will be compiled into the component library +- A sub-directory "include" will be added to the global include search path for all other components. +- The component library will be linked into the project app. + +See `example component makefiles` for more complete component makefile examples. + +Note that there is a different between an empty ``component.mk`` file (which invokes default component build behaviour) and no ``component.mk`` file (which means no default component build behaviour will occur.) It is possible for a component to have no `component.mk` file, if it only contains other files which influence the project configuration or build process. + +.. component variables: + +Preset Component Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following component-specific variables are available for use inside ``component.mk``, but should not be modified: + +- ``COMPONENT_PATH``: The component directory. Evaluates to the absolute path of the directory containing ``component.mk``. The component path cannot contain spaces. +- ``COMPONENT_NAME``: Name of the component. Defaults to the name of the component directory. +- ``COMPONENT_BUILD_DIR``: The component build directory. Evaluates to the absolute path of a directory inside `$(BUILD_DIR_BASE)` where this component's source files are to be built. This is also the Current Working Directory any time the component is being built, so relative paths in make targets, etc. will be relative to this directory. +- ``COMPONENT_LIBRARY``: Name of the static library file (relative to the component build directory) that will be built for this component. Defaults to ``$(COMPONENT_NAME).a``. + +The following variables are set at the project level, but exported for use in the component build: + +- ``PROJECT_NAME``: Name of the project, as set in project Makefile +- ``PROJECT_PATH``: Absolute path of the project directory containing the project Makefile. +- ``COMPONENTS``: Name of all components that are included in this build. +- ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``. +- ``CC``, ``LD``, ``AR``, ``OBJCOPY``: Full paths to each tool from the gcc xtensa cross-toolchain. +- ``HOSTCC``, ``HOSTLD``, ``HOSTAR``: Full names of each tool from the host native toolchain. + +If you modify any of these variables inside ``component.mk`` then this will not prevent other components from building but it may make your component hard to build and/or debug. + +Optional Project-Wide Component Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following variables can be set inside ``component.mk`` to control build settings across the entire project: + +- ``COMPONENT_ADD_INCLUDEDIRS``: Paths, relative to the component + directory, which will be added to the include search path for + all components in the project. Defaults to ``include`` if not overridden. If an include directory is only needed to compile + this specific component, add it to ``COMPONENT_PRIV_INCLUDEDIRS`` instead. +- ``COMPONENT_ADD_LDFLAGS``: Add linker arguments to the LDFLAGS for + the app executable. Defaults to ``-l$(COMPONENT_NAME)``. If + adding pre-compiled libraries to this directory, add them as + absolute paths - ie $(COMPONENT_PATH)/libwhatever.a +- ``COMPONENT_DEPENDS``: Optional list of component names that should + be compiled before this component. This is not necessary for + link-time dependencies, because all component include directories + are available at all times. It is necessary if one component + generates an include file which you then want to include in another + component. Most components do not need to set this variable. + + +Optional Component-Specific Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following variables can be set inside ``component.mk`` to control the build of that component: + +- ``COMPONENT_PRIV_INCLUDEDIRS``: Directory paths, must be relative to + the component directory, which will be added to the include search + path for this component's source files only. +- ``COMPONENT_EXTRA_INCLUDES``: Any extra include paths used when + compiling the component's source files. These will be prefixed with + '-I' and passed as-is to the compiler. Similar to the + ``COMPONENT_PRIV_INCLUDEDIRS`` variable, except these paths are not + expanded relative to the component directory. +- ``COMPONENT_SRCDIRS``: Directory paths, must be relative to the + component directory, which will be searched for source files (*.cpp, + *.c, *.S). Defaults to '.', ie the component directory + itself. Override this to specify a different list of directories + which contain source files. +- ``COMPONENT_OBJS``: Object files to compile. Default value is a .o + file for each source file that is found in ``COMPONENT_SRCDIRS``. + Overriding this list allows you to exclude source files in + ``COMPONENT_SRCDIRS`` that would otherwise be compiled. See + `Specifying source files` +- ``COMPONENT_EXTRA_CLEAN``: Paths, relative to the component build + directory, of any files that are generated using custom make rules + in the component.mk file and which need to be removed as part of + ``make clean``. See `Source Code Generation` for an example. +- ``COMPONENT_OWNBUILDTARGET`` & `COMPONENT_OWNCLEANTARGET`: These + targets allow you to fully override the default build behaviour for + the component. See `Fully Overriding The Component Makefile` for + more details. +- ``CFLAGS``: Flags passed to the C compiler. A default set of + ``CFLAGS`` is defined based on project settings. Component-specific + additions can be made via ``CFLAGS +=``. It is also possible + (although not recommended) to override this variable completely for + a component. +- ``CPPFLAGS``: Flags passed to the C preprocessor (used for .c, .cpp + and .S files). A default set of ``CPPFLAGS`` is defined based on + project settings. Component-specific additions can be made via + ``CPPFLAGS +=``. It is also possible (although not recommended) to + override this variable completely for a component. +- ``CXXFLAGS``: Flags passed to the C++ compiler. A default set of + ``CXXFLAGS`` is defined based on project + settings. Component-specific additions can be made via ``CXXFLAGS + +=``. It is also possible (although not recommended) to override + this variable completely for a component. + +Component Configuration +----------------------- + +Each component can also have a Kconfig file, alongside ``component.mk``. This contains contains +configuration settings to add to the "make menuconfig" for this component. + +These settings are found under the "Component Settings" menu when menuconfig is run. + +To create a component KConfig file, it is easiest to start with one of the KConfig files distributed with esp-idf. + +For an example, see `Adding conditional configuration`. + +Build Process Internals +----------------------- + +Top Level: Project Makefile +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- "make" is always run from the project directory and the project makefile, typically named Makefile. +- The project makefile sets ``PROJECT_NAME`` and optionally customises other `optional project variables` +- The project makefile includes ``$(IDF_PATH)/make/project.mk`` which contains the project-level Make logic. +- ``project.mk`` fills in default project-level make variables and includes make variables from the project configuration. If the generated makefile containing project configuration is out of date, then it is regenerated (via targets in ``project_config.mk``) and then the make process restarts from the top. +- ``project.mk`` builds a list of components to build, based on the default component directories or a custom list of components set in `optional project variables`. +- Each component can set some `optional project-wide component variables`. These are included via generated makefiles named ``component_project_vars.mk`` - there is one per component. These generated makefiles are included into ``project.mk``. If any are missing or out of date, they are regenerated (via a recursive make call to the component makefile) and then the make process restarts from the top. +- `Makefile.projbuild` files from components are included into the make process, to add extra targets or configuration. +- By default, the project makefile also generates top-level build & clean targets for each component and sets up `app` and `clean` targets to invoke all of these sub-targets. +- In order to compile each component, a recursive make is performed for the component makefile. + +To better understand the project make process, have a read through the ``project.mk`` file itself. + +Second Level: Component Makefiles +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Each call to a component makefile goes via the ``$(IDF_PATH)/make/component_wrapper.mk`` wrapper makefile. +- The ``component_wrapper.mk`` is called with the current directory set to the component build directory, and the ``COMPONENT_MAKEFILE`` variable is set to the absolute path to ``component.mk``. +- ``component_wrapper.mk`` sets default values for all `component variables`, then includes the `component.mk` file which can override or modify these. +- If ``COMPONENT_OWNBUILDTARGET`` and ``COMPONENT_OWNCLEANTARGET`` are not defined, default build and clean targets are created for the component's source files and the prerequisite ``COMPONENT_LIBRARY`` static library file. +- The ``component_project_vars.mk`` file has its own target in ``component_wrapper.mk``, which is evaluated from ``project.mk`` if this file needs to be rebuilt due to changes in the component makefile or the project configuration. + +To better understand the component make process, have a read through the ``component_wrapper.mk`` file and some of the ``component.mk`` files included with esp-idf. + +Overriding Parts of the Project +------------------------------- Makefile.projbuild ------------------- +^^^^^^^^^^^^^^^^^^ -For components that have parts that need to be evaluated in the top-level -project context, you can create a file called Makefile.projbuild in the -component root directory. These files is included into the project's -top-level Makefile. +For components that have build requirements that must be evaluated in the top-level +project make pass, you can create a file called ``Makefile.projbuild`` in the +component directory. This makefile is included when ``project.mk`` is evaluated. For example, if your component needs to add to CFLAGS for the entire project (not just for its own source files) then you can set -``CFLAGS +=`` in Makefile.projbuild. Note that this isn't necessary for -adding include directories to the project, you can set -``COMPONENT_ADD_INCLUDEDIRS`` (see above) in the component.mk. +``CFLAGS +=`` in Makefile.projbuild. +``Makefile.projbuild`` files are used heavily inside esp-idf, for defining project-wide build features such as ``esptool.py`` command line arguments and the ``bootloader`` "special app". + +Note that ``Makefile.projbuild`` isn't necessary for the most common component uses - such as adding include directories to the project, or LDFLAGS to the final linking step. These values can be customised via the ``component.mk`` file itself. See `Optional Project-Wide Component Variables` for details. + +Take care when setting variables or targets in this file. As the values are included into the top-level project makefile pass, they can influence or break functionality across all components! KConfig.projbuild ------------------ +^^^^^^^^^^^^^^^^^ -There's an equivalent to Makefile.projbuild for KConfig: if you want to include -options at the top-level, not inside the 'components' submenu then create a Kconfig.projbuild and -it will be included in the main menu of menuconfig. +This is an equivalent to `Makefile.projbuild` for `component configuration` KConfig files. If you want to include +configuration options at the top-level of menuconfig, rather than inside the "Component Configuration" sub-menu, then these can be defined in the KConfig.projbuild file alongside the ``component.mk`` file. -Take good care when (re)defining stuff here: because it's included with all the other -.projbuild files, it's possible to overwrite variables or re-declare targets defined in -the ESP-IDF makefile/Kconfig and other .projbuild files. It's generally better to just -create a KConfig file, if you can. +Take care when adding configuration values in this file, as they will be included across the entire project configuration. Where possible, it's generally better to create a KConfig file for `component configuration`. -Writing Component Makefiles +Example Component Makefiles --------------------------- -A component consists of a directory which doubles as the name for the -component: a component named 'httpd' lives in a directory called 'httpd' -Because components usually live under the project directory (although -they can also reside in an other folder), the path to this may be -something like /home/myuser/projects/myprojects/components/httpd . +Because the build environment tries to set reasonable defaults that will work most +of the time, component.mk can be very small or even empty (see `Minimal Component Makefile`). However, overriding `component variables` is usually required for some functionality. -Components can have any name (unique to the project) but the name -cannot contain spaces (esp-idf does not support spaces in paths). - -One of the things that most components will have is a component.mk makefile, -containing instructions on how to build the component. Because the -build environment tries to set reasonable defaults that will work most -of the time, component.mk can be very small. - -Simplest component.mk -===================== - -At the minimum, component.mk will just include the ESP-IDF component "common" makefile, -which adds common component functionality:: - - include $(IDF_PATH)/make/component_common.mk - -This will take all the .c and .S files in the component root and compile -them into object files, finally linking them into a library. +Here are some more advanced examples of ``component.mk`` makefiles: Adding source directories -========================= +^^^^^^^^^^^^^^^^^^^^^^^^^ -By default, subdirectories are ignored. If your project has sources in subdirectories +By default, sub-directories are ignored. If your project has sources in sub-directories instead of in the root of the component then you can tell that to the build -system by setting COMPONENT_SRCDIRS:: +system by setting ``COMPONENT_SRCDIRS``:: COMPONENT_SRCDIRS := src1 src2 - include $(IDF_PATH)/make/component_common.mk -This will compile all source files in the src1/ and src2/ subdirectories +This will compile all source files in the src1/ and src2/ sub-directories instead. Specifying source files -======================= +^^^^^^^^^^^^^^^^^^^^^^^ The standard component.mk logic adds all .S and .c files in the source directories as sources to be compiled unconditionally. It is possible -to circumvent that logic and hardcode the objects to be compiled by -manually setting the COMPONENT_OBJS variable to the name of the +to circumvent that logic and hard-code the objects to be compiled by +manually setting the ``COMPONENT_OBJS`` variable to the name of the objects that need to be generated:: COMPONENT_OBJS := file1.o file2.o thing/filea.o thing/fileb.o anotherthing/main.o - include $(IDF_PATH)/make/component_common.mk + COMPONENT_SRCDIRS := . thing anotherthing +Note that ``COMPONENT_SRCDIRS`` must be set as well. Adding conditional configuration -================================ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The configuration system can be used to conditionally compile some files -dependending on the options selected in ``make menuconfig``: +depending on the options selected in ``make menuconfig``: -Kconfig:: +``Kconfig``:: config FOO_ENABLE_BAR - bool "Enable the BAR feature." - help - This enables the BAR feature of the FOO component. + bool "Enable the BAR feature." + help + This enables the BAR feature of the FOO component. -Makefile:: - COMPONENT_OBJS := foo_a.o foo_b.o $(if $(CONFIG_FOO_ENABLE_BAR),foo_bar.o foo_bar_interface.o) - include $(IDF_PATH)/make/component_common.mk +``component.mk``:: + COMPONENT_OBJS := foo_a.o foo_b.o + + ifdef CONFIG_FOO_BAR + COMPONENT_OBJS += foo_bar.o foo_bar_interface.o + endif + +See the `GNU Make Manual` for conditional syntax that can be used use in makefiles. Source Code Generation -====================== +^^^^^^^^^^^^^^^^^^^^^^ -Some components will have a situation where a source file isn't supplied -with the component itself but has to be generated from another file. Say -our component has a header file that consists of the converted binary -data of a BMP file, converted using a hypothetical tool called bmp2h. The -header file is then included in as C source file called graphics_lib.c:: +Some components will have a situation where a source file isn't +supplied with the component itself but has to be generated from +another file. Say our component has a header file that consists of the +converted binary data of a BMP file, converted using a hypothetical +tool called bmp2h. The header file is then included in as C source +file called graphics_lib.c:: COMPONENT_EXTRA_CLEAN := logo.h @@ -272,16 +378,25 @@ header file is then included in as C source file called graphics_lib.c:: logo.h: $(COMPONENT_PATH)/logo.bmp bmp2h -i $^ -o $@ - include $(IDF_PATH)/make/component_common.mk In this example, graphics_lib.o and logo.h will be generated in the -current directory (the build directory) while logo.bmp comes with the -component and resides under the component path. Because logo.h is a -generated file, it needs to be cleaned when make clean is called which -why it is added to the COMPONENT_EXTRA_CLEAN variable. +component build directory, whereas logo.bmp resides in the component +source directory. + +Because logo.h is a generated file, it needs to be cleaned when make +clean is called which why it is added to the COMPONENT_EXTRA_CLEAN +variable. + +Adding logo.h to the ``graphics_lib.o`` dependencies causes it to be +generated before ``graphics_lib.c`` is compiled. + +If a a source file in another component included ``logo.h``, then this +component's name would have to be added to the other component's +``COMPONENT_DEPENDS`` list to ensure that the components were built +in-order. Cosmetic Improvements -===================== +^^^^^^^^^^^^^^^^^^^^^ The above example will work just fine, but there's one last cosmetic improvement that can be done. The make system tries to make the make @@ -295,10 +410,9 @@ make process:: graphics_lib.o: logo.h logo.h: $(COMPONENT_PATH)/logo.bmp - $(summary) BMP2H $@ - $(Q) bmp2h -i $^ -o $@ + $(summary) BMP2H $@ + $(Q) bmp2h -i $^ -o $@ - include $(IDF_PATH)/make/component_common.mk Fully Overriding The Component Makefile --------------------------------------- @@ -307,12 +421,15 @@ Obviously, there are cases where all these recipes are insufficient for a certain component, for example when the component is basically a wrapper around another third-party component not originally intended to be compiled under this build system. In that case, it's possible to forego -the build system entirely by setting COMPONENT_OWNBUILDTARGET and -possibly COMPONENT_OWNCLEANTARGET and defining your own build- and clean +the esp-idf build system entirely by setting COMPONENT_OWNBUILDTARGET and +possibly COMPONENT_OWNCLEANTARGET and defining your own targets named ``build`` and ``clean`` in ``component.mk`` target. The build target can do anything as long as it creates -$(COMPONENT_LIBRARY) for the main file to link into the project binary, -and even that is not necessary: if the COMPONENT_ADD_LDFLAGS variable -is set, the component can instruct the linker to do anything else as well. +$(COMPONENT_LIBRARY) for the project make process to link into the app binary. + +(Actually, even this is not strictly necessary - if the COMPONENT_ADD_LDFLAGS variable +is set then the component can instruct the linker to link other binaries instead.) .. _esp-idf-template: https://github.com/espressif/esp-idf-template +.. _GNU Make Manual: https://www.gnu.org/software/make/manual/make.html +.. _[_f1]: Actually, some components in esp-idf are "pure configuration" components that don't have a component.mk file, only a Makefile.projbuild and/or Kconfig.projbuild file. However, these components are unusual and most components have a component.mk file. From 07f36a9437fa2fa3f8de7bd2dba0759504f9ad86 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 10 Nov 2016 16:19:59 +1100 Subject: [PATCH 173/285] Build system: Use ifndef X in makefiles instead of ifeq("$(X)","") --- make/component_wrapper.mk | 8 ++++---- make/project.mk | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 68efe0d211..fe081e32fd 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -8,7 +8,7 @@ # # CWD is the build directory of the component. -ifeq ("$(PROJECT_PATH)","") +ifndef PROJECT_PATH $(error Make was invoked from $(CURDIR). However please do not run make from the sdk or a component directory; invoke make from the project directory. See the ESP-IDF README for details.) endif @@ -60,7 +60,7 @@ include $(COMPONENT_MAKEFILE) # Object files which need to be linked into the library # By default we take all .c, .cpp & .S files in COMPONENT_SRCDIRS. -ifeq ("$(COMPONENT_OBJS)", "") +ifndef COMPONENT_OBJS # Find all source files in all COMPONENT_SRCDIRS COMPONENT_OBJS := $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.c,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.c))) COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.cpp,%.o,$(wildcard $(COMPONENT_PATH)/$(compsrcdir)/*.cpp))) @@ -120,7 +120,7 @@ component_project_vars.mk:: # If COMPONENT_OWNBUILDTARGET is not set, define a phony build target and # a COMPONENT_LIBRARY link target. -ifeq ("$(COMPONENT_OWNBUILDTARGET)", "") +ifndef COMPONENT_OWNBUILDTARGET .PHONY: build build: $(COMPONENT_LIBRARY) @mkdir -p $(COMPONENT_SRCDIRS) @@ -134,7 +134,7 @@ $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) endif # If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target -ifeq ("$(COMPONENT_OWNCLEANTARGET)", "") +ifndef COMPONENT_OWNCLEANTARGET CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(COMPONENT_EXTRA_CLEAN) component_project_vars.mk .PHONY: clean clean: diff --git a/make/project.mk b/make/project.mk index f9cf20a329..4554d1d329 100644 --- a/make/project.mk +++ b/make/project.mk @@ -40,10 +40,9 @@ help: MAKEFLAGS_OLD := $(MAKEFLAGS) MAKEFLAGS +=-rR -# Figure out PROJECT_PATH if not set -ifeq ("$(PROJECT_PATH)","") -#The path to the project: we assume the Makefile including this file resides -#in the root of that directory. +# Default path to the project: we assume the Makefile including this file +# is in the project directory +ifndef PROJECT_PATH PROJECT_PATH := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) export PROJECT_PATH endif @@ -67,7 +66,7 @@ SRCDIRS ?= main # The project Makefile can define a list of components, but if it does not do this we just take # all available components in the component dirs. -ifeq ("$(COMPONENTS)","") +ifndef COMPONENTS # Find all component names. The component names are the same as the # directories they're in, so /bla/components/mycomponent/ -> mycomponent. We then use # COMPONENT_DIRS to build COMPONENT_PATHS with the full path to each component. From 4f4c9030fd284e79eed46aa403fb9ec5efba84a8 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 10 Nov 2016 17:01:39 +0800 Subject: [PATCH 174/285] Fix uart tx bug, data pass(with flow control) through test ok. --- components/driver/uart.c | 2 +- components/esp32/include/esp_err.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index b961fbed71..d9e3fd64ca 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -729,7 +729,7 @@ static int uart_tx_all(uart_port_t uart_num, const char* src, size_t size, bool } else { evt.type = UART_DATA; } - xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_event_t), portMAX_DELAY); + xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) &evt, sizeof(uart_tx_data_t), portMAX_DELAY); while(size > 0) { int send_size = size > max_size / 2 ? max_size / 2 : size; xRingbufferSend(p_uart_obj[uart_num]->tx_ring_buf, (void*) (src + offset), send_size, portMAX_DELAY); diff --git a/components/esp32/include/esp_err.h b/components/esp32/include/esp_err.h index f8271ba259..a1f4b8f359 100644 --- a/components/esp32/include/esp_err.h +++ b/components/esp32/include/esp_err.h @@ -34,6 +34,8 @@ typedef int32_t esp_err_t; #define ESP_ERR_INVALID_SIZE 0x104 #define ESP_ERR_NOT_FOUND 0x105 #define ESP_ERR_NOT_SUPPORTED 0x106 +#define ESP_ERR_TIMEOUT 0x107 + #define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */ From 86d8f63005d42bb9c46330c67b35ba8efc3a04ec Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 10 Nov 2016 17:59:46 +0800 Subject: [PATCH 175/285] Per-CPU interrupt handlers and args --- components/esp32/crosscore_int.c | 7 +---- .../include/freertos/FreeRTOSConfig.h | 6 ----- components/freertos/readme_smp.txt | 5 ---- components/freertos/xtensa_intr.c | 11 ++++++-- components/freertos/xtensa_intr_asm.S | 23 ++++++++++++++-- components/freertos/xtensa_vectors.S | 26 +++++++++++++++++++ 6 files changed, 57 insertions(+), 21 deletions(-) diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 60a45da402..60f972a2a2 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -45,14 +45,9 @@ the ISR will cause it to switch _away_ from it. portYIELD_FROM_ISR will probably */ static void esp_crosscore_isr(void *arg) { uint32_t myReasonVal; -#if 0 //A pointer to the correct reason array item is passed to this ISR. volatile uint32_t *myReason=arg; -#else - //The previous line does not work yet, the interrupt code needs work to understand two separate interrupt and argument - //tables... this is a valid but slightly less optimal replacement. - volatile uint32_t *myReason=&reason[xPortGetCoreID()]; -#endif + //Clear the interrupt first. if (xPortGetCoreID()==0) { WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 224857c86c..47566ab3b3 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -265,12 +265,6 @@ #define INCLUDE_eTaskGetState 1 #define configUSE_QUEUE_SETS 1 -#if (!defined XT_INTEXC_HOOKS) -#define configXT_INTEXC_HOOKS 1 /* Exception hooks used by certain tests */ -#if configUSE_TRACE_FACILITY_2 -#define configASSERT_2 1 /* Specific to Xtensa port */ -#endif -#endif #define configXT_BOARD 1 /* Board mode */ #define configXT_SIMULATOR 0 diff --git a/components/freertos/readme_smp.txt b/components/freertos/readme_smp.txt index 38f332416a..fdd9b146b1 100644 --- a/components/freertos/readme_smp.txt +++ b/components/freertos/readme_smp.txt @@ -19,11 +19,6 @@ it would on a single-core system: the other core still will keep on executing all it's own. Use a mux, queue or semaphore to protect your structures instead. -- While each core has individual interrupts, the handlers are shared. This -means that when you set a handler for an interrupt, it will get triggered if -the interrupt is triggered on both CPU0 as well as on CPU1. This is something -we may change in future FreeRTOS-esp32 releases. - - This FreeRTOS version has the task local storage backported from the 8.2.x versions. It, however, has an addition: you can also set a callback when you set the pointer. This callback will be called by the idle task, with the diff --git a/components/freertos/xtensa_intr.c b/components/freertos/xtensa_intr.c index f5ca7d151f..e9c0b79b96 100644 --- a/components/freertos/xtensa_intr.c +++ b/components/freertos/xtensa_intr.c @@ -30,7 +30,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include +#include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" +#include "freertos/portable.h" #include "rom/ets_sys.h" @@ -39,7 +41,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Handler table is in xtensa_intr_asm.S */ // Todo: Make multicore - JD -extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM]; +extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM*portNUM_PROCESSORS]; /* @@ -66,6 +68,8 @@ xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f) if( n < 0 || n >= XCHAL_EXCCAUSE_NUM ) return 0; /* invalid exception number */ + /* Convert exception number to _xt_exception_table name */ + n = n * portNUM_PROCESSORS + xPortGetCoreID(); old = _xt_exception_table[n]; if (f) { @@ -89,7 +93,7 @@ typedef struct xt_handler_table_entry { void * arg; } xt_handler_table_entry; -extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS]; +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; /* @@ -118,6 +122,9 @@ xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg) if( Xthal_intlevel[n] > XCHAL_EXCM_LEVEL ) return 0; /* priority level too high to safely handle in C */ + /* Convert exception number to _xt_exception_table name */ + n = n * portNUM_PROCESSORS + xPortGetCoreID(); + entry = _xt_interrupt_table + n; old = entry->handler; diff --git a/components/freertos/xtensa_intr_asm.S b/components/freertos/xtensa_intr_asm.S index 5f9890dfe4..8c7ae63fdb 100644 --- a/components/freertos/xtensa_intr_asm.S +++ b/components/freertos/xtensa_intr_asm.S @@ -30,6 +30,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include "xtensa_context.h" +#include "FreeRTOSConfig.h" #if XCHAL_HAVE_INTERRUPTS @@ -59,6 +60,15 @@ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */ Table of C-callable interrupt handlers for each interrupt. Note that not all slots can be filled, because interrupts at level > EXCM_LEVEL will not be dispatched to a C handler by default. + + Stored as: + int 0 cpu 0 + int 0 cpu 1 + ... + int 0 cpu n + int 1 cpu 0 + int 1 cpu 1 + etc ------------------------------------------------------------------------------- */ @@ -69,7 +79,7 @@ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */ _xt_interrupt_table: .set i, 0 - .rept XCHAL_NUM_INTERRUPTS + .rept XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS .word xt_unhandled_interrupt /* handler address */ .word i /* handler arg (default: intnum) */ .set i, i+1 @@ -85,6 +95,15 @@ _xt_interrupt_table: Table of C-callable exception handlers for each exception. Note that not all slots will be active, because some exceptions (e.g. coprocessor exceptions) are always handled by the OS and cannot be hooked by user handlers. + + Stored as: + exc 0 cpu 0 + exc 0 cpu 1 + ... + exc 0 cpu n + exc 1 cpu 0 + exc 1 cpu 1 + etc ------------------------------------------------------------------------------- */ @@ -93,7 +112,7 @@ _xt_interrupt_table: .align 4 _xt_exception_table: - .rept XCHAL_EXCCAUSE_NUM + .rept XCHAL_EXCCAUSE_NUM * portNUM_PROCESSORS .word xt_unhandled_exception /* handler address */ .endr diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index 7cf70f0032..2a373810fd 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -113,6 +113,27 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define XIE_ARG 4 #define XIE_SIZE 8 + +/* + Macro get_percpu_entry_for - convert a per-core ID into a multicore entry. + Basically does reg=reg*portNUM_PROCESSORS+current_core_id + Multiple versions here for multiple +*/ + .macro get_percpu_entry_for reg scratch +#if (portNUM_PROCESSORS == 1) + /* No need to do anything */ +#elif (portNUM_PROCESSORS == 2) + /* Optimized 2-core code. */ + getcoreid \scratch + addx2 \reg,\reg,\scratch +#else + /* Generalized n-core code. Untested! */ + movi \scratch,portNUM_PROCESSORS + mull \scratch,\reg,\scratch + getcoreid \reg + add \reg,\scratch,\reg +#endif + .endm /* -------------------------------------------------------------------------------- Macro extract_msb - return the input with only the highest bit set. @@ -229,6 +250,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ + get_percpu_entry_for a3, a12 movi a4, _xt_interrupt_table addx8 a3, a3, a4 /* a3 = address of interrupt table entry */ l32i a4, a3, XIE_HANDLER /* a4 = handler address */ @@ -395,6 +417,9 @@ panic_print_hex_ok: with index 0 containing the entry for user exceptions. Initialized with all 0s, meaning no handler is installed at each level. See comment in xtensa_rtos.h for more details. + + *WARNING* This array is for all CPUs, that is, installing a hook for + one CPU will install it for all others as well! -------------------------------------------------------------------------------- */ @@ -688,6 +713,7 @@ _xt_user_exc: rsr a2, EXCCAUSE /* recover exc cause */ movi a3, _xt_exception_table + get_percpu_entry_for a3, a4 addx4 a4, a2, a3 /* a4 = address of exception table entry */ l32i a4, a4, 0 /* a4 = handler address */ #ifdef __XTENSA_CALL0_ABI__ From 07c30f22b58f4511912a2e809bba3474769632bd Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 10 Nov 2016 18:04:23 +0800 Subject: [PATCH 176/285] Add core info to xt interrupt api --- components/freertos/include/freertos/xtensa_api.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/components/freertos/include/freertos/xtensa_api.h b/components/freertos/include/freertos/xtensa_api.h index 04ad7d62d8..87922691dd 100644 --- a/components/freertos/include/freertos/xtensa_api.h +++ b/components/freertos/include/freertos/xtensa_api.h @@ -42,7 +42,8 @@ typedef void (*xt_exc_handler)(XtExcFrame *); /* ------------------------------------------------------------------------------- - Call this function to set a handler for the specified exception. + Call this function to set a handler for the specified exception. The handler + will be installed on the core that calls this function. n - Exception number (type) f - Handler function address, NULL to uninstall handler. @@ -61,7 +62,8 @@ extern xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f); /* ------------------------------------------------------------------------------- - Call this function to set a handler for the specified interrupt. + Call this function to set a handler for the specified interrupt. The handler + will be installed on the core that calls this function. n - Interrupt number. f - Handler function address, NULL to uninstall handler. @@ -73,7 +75,8 @@ extern xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg); /* ------------------------------------------------------------------------------- - Call this function to enable the specified interrupts. + Call this function to enable the specified interrupts on the core that runs + this code. mask - Bit mask of interrupts to be enabled. ------------------------------------------------------------------------------- @@ -83,7 +86,8 @@ extern void xt_ints_on(unsigned int mask); /* ------------------------------------------------------------------------------- - Call this function to disable the specified interrupts. + Call this function to disable the specified interrupts on the core that runs + this code. mask - Bit mask of interrupts to be disabled. ------------------------------------------------------------------------------- From 2f6d71ff8ce28fda15898c5fe1b5669ee8dba505 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 10 Nov 2016 21:12:22 +0800 Subject: [PATCH 177/285] esp32: merge amsdu esp32 now can support AMSDU --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 76f9109806..84af0ed366 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 76f91098061b0052fe1bb67e85001014f39b84a0 +Subproject commit 84af0ed366e1ba38984f7df517a77f8ec4fa27ed From c6073cb5de044d9fe5662dd3eee50ec4f37181ce Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:44:09 +0100 Subject: [PATCH 178/285] Conform Style Guide --- docs/conf.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 494ab3cf7f..551cd86dd0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,9 +22,6 @@ import os # -- Run DoxyGen to prepare XML for Sphinx--------------------------------- # ref. https://github.com/rtfd/readthedocs.org/issues/388 -# -# added by krzychb, 24-Oct-2016 -# from subprocess import call, Popen, PIPE import shlex @@ -298,8 +295,6 @@ texinfo_documents = [ # -- Use sphinx_rtd_theme for local builds -------------------------------- # ref. https://github.com/snide/sphinx_rtd_theme#using-this-theme-locally-then-building-on-read-the-docs # -# added by krzychb, 24-Oct-2016 -# # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' From 48a976ddbf3706c67eb15729777fba96c831a526 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:44:59 +0100 Subject: [PATCH 179/285] Fixed link so it can render by Sphinx --- docs/style-guide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 4fa8321762..9bf00f1f7d 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -172,7 +172,7 @@ To re-format a file, run:: Documenting code ---------------- -Please see the guide here: `Documenting Code `_. +Please see the guide here: :doc:`documenting-code`. Structure and naming -------------------- From 9858fde4a23ffd56454641741185979555bb1372 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:47:29 +0100 Subject: [PATCH 180/285] New items to documentation TOC - Style Guide - UART API --- docs/index.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 42be69ee0c..3f32806e5c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -92,9 +92,10 @@ Contents: Wi-Fi Bluetooth - GPIO - LED Control - + api/gpio + api/uart + api/ledc + Logging Non-Volatile Storage Virtual Filesystem @@ -114,6 +115,7 @@ Contents: :maxdepth: 1 contributing + Style Guide documenting-code contributor-agreement From ce0382f0c03f7ffbe5d75d4a2b6a3ede30406cef Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Thu, 10 Nov 2016 22:50:55 +0100 Subject: [PATCH 181/285] Revised api files - Included UART API - Addedd "Header Files" - Improved template - deleted redundat nvs.rst --- docs/api/bt.rst | 5 +++ docs/api/esp_wifi.rst | 7 ++- docs/api/gpio.rst | 7 ++- docs/api/ledc.rst | 5 +++ docs/api/log.rst | 14 ++++++ docs/api/nvs.rst | 68 ----------------------------- docs/api/nvs_flash.rst | 6 +++ docs/api/template.rst | 39 ++++++++++++----- docs/api/uart.rst | 98 ++++++++++++++++++++++++++++++++++++++++++ docs/api/vfs.rst | 6 +++ 10 files changed, 174 insertions(+), 81 deletions(-) delete mode 100644 docs/api/nvs.rst create mode 100644 docs/api/uart.rst diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 7cbbb9158b..0ab17b2aa5 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -18,6 +18,11 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `bt/include/bt.h `_ + Type Definitions ^^^^^^^^^^^^^^^^ diff --git a/docs/api/esp_wifi.rst b/docs/api/esp_wifi.rst index e417e18ca0..c13d3d751a 100644 --- a/docs/api/esp_wifi.rst +++ b/docs/api/esp_wifi.rst @@ -18,6 +18,11 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `esp32/include/esp_wifi.h `_ + Macros ------ @@ -28,7 +33,6 @@ Type Definitions ---------------- .. doxygentypedef:: wifi_promiscuous_cb_t -.. doxygentypedef:: wifi_rxcb_t .. doxygentypedef:: esp_vendor_ie_cb_t Functions @@ -68,7 +72,6 @@ Functions .. doxygenfunction:: esp_wifi_get_config .. doxygenfunction:: esp_wifi_ap_get_sta_list .. doxygenfunction:: esp_wifi_set_storage -.. doxygenfunction:: esp_wifi_reg_rxcb .. doxygenfunction:: esp_wifi_set_auto_connect .. doxygenfunction:: esp_wifi_get_auto_connect .. doxygenfunction:: esp_wifi_set_vendor_ie diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 72ba3e82fb..0cd4eca365 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -18,8 +18,13 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/driver/gpio.h `_ + Macros ------- +^^^^^^ .. doxygendefine:: GPIO_SEL_0 .. doxygendefine:: GPIO_SEL_1 diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst index 32b639f3f9..f379e9d008 100644 --- a/docs/api/ledc.rst +++ b/docs/api/ledc.rst @@ -18,6 +18,11 @@ API Reference .. _Instructions: template.html +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/ledc.h `_ + Data Structures ^^^^^^^^^^^^^^^ diff --git a/docs/api/log.rst b/docs/api/log.rst index 49f97108aa..d2f2fcd073 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -1,8 +1,22 @@ .. include:: ../../components/log/README.rst +Application Example +------------------- + +`Instructions`_ + API Reference ------------- +`Instructions`_ + +.. _Instructions: template.html + +Header Files +^^^^^^^^^^^^ + + * `log/include/esp_log.h `_ + Macros ^^^^^^ diff --git a/docs/api/nvs.rst b/docs/api/nvs.rst deleted file mode 100644 index fc2bba5a16..0000000000 --- a/docs/api/nvs.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. include:: ../../components/nvs_flash/README.rst - -API Reference -------------- - -Enumerations -^^^^^^^^^^^^ - -.. doxygenenum:: nvs_open_mode - -Functions -^^^^^^^^^ - -.. doxygenfunction:: nvs_flash_init -.. doxygenfunction:: nvs_flash_init_custom - -.. doxygenfunction:: nvs_open - -*Note: the following nvs_set_X function are "the same" except the data type accepted* - -.. doxygenfunction:: nvs_set_i8 -.. doxygenfunction:: nvs_set_u8 -.. doxygenfunction:: nvs_set_i16 -.. doxygenfunction:: nvs_set_u16 -.. doxygenfunction:: nvs_set_i32 -.. doxygenfunction:: nvs_set_u32 -.. doxygenfunction:: nvs_set_i64 -.. doxygenfunction:: nvs_set_u64 -.. doxygenfunction:: nvs_set_str -.. doxygenfunction:: nvs_set_blob - -*Note: the following nvs_get_X functions are "the same" except the data type returned* - -.. doxygenfunction:: nvs_get_i8 -.. doxygenfunction:: nvs_get_u8 -.. doxygenfunction:: nvs_get_i16 -.. doxygenfunction:: nvs_get_u16 -.. doxygenfunction:: nvs_get_i32 -.. doxygenfunction:: nvs_get_u32 -.. doxygenfunction:: nvs_get_i64 -.. doxygenfunction:: nvs_get_u64 -.. doxygenfunction:: nvs_get_str -.. doxygenfunction:: nvs_get_blob - -.. doxygenfunction:: nvs_erase_key -.. doxygenfunction:: nvs_erase_all -.. doxygenfunction:: nvs_commit -.. doxygenfunction:: nvs_close - -Error codes -^^^^^^^^^^^ - -.. doxygendefine:: ESP_ERR_NVS_BASE -.. doxygendefine:: ESP_ERR_NVS_NOT_INITIALIZED -.. doxygendefine:: ESP_ERR_NVS_NOT_FOUND -.. doxygendefine:: ESP_ERR_NVS_TYPE_MISMATCH -.. doxygendefine:: ESP_ERR_NVS_READ_ONLY -.. doxygendefine:: ESP_ERR_NVS_NOT_ENOUGH_SPACE -.. doxygendefine:: ESP_ERR_NVS_INVALID_NAME -.. doxygendefine:: ESP_ERR_NVS_INVALID_HANDLE -.. doxygendefine:: ESP_ERR_NVS_REMOVE_FAILED -.. doxygendefine:: ESP_ERR_NVS_KEY_TOO_LONG -.. doxygendefine:: ESP_ERR_NVS_PAGE_FULL -.. doxygendefine:: ESP_ERR_NVS_INVALID_STATE -.. doxygendefine:: ESP_ERR_NVS_INVALID_LENGTH - - - diff --git a/docs/api/nvs_flash.rst b/docs/api/nvs_flash.rst index 16c74fa530..0768fa5597 100644 --- a/docs/api/nvs_flash.rst +++ b/docs/api/nvs_flash.rst @@ -8,6 +8,12 @@ Application Example API Reference ------------- +Header Files +^^^^^^^^^^^^ + + * `nvs_flash/include/nvs_flash.h `_ + * `nvs_flash/include/nvs.h `_ + Macros ^^^^^^ diff --git a/docs/api/template.rst b/docs/api/template.rst index 3c8bcdb62c..6feb7ba271 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -51,10 +51,11 @@ API Reference *INSTRUCTIONS* - 1. Provide list of API members divided into sections. - 2. Use corresponding ``.. doxygen..`` directives, so member documentation is auto updated. + 1. Specify the names of header files used to generate this reference. Each name should be linked to the source on `espressif/esp-idf `_ repository. + 2. Provide list of API members divided into sections. + 3. Use corresponding ``.. doxygen..`` directives, so member documentation is auto updated. - * Data Structures -``.. doxygenstruct::`` + * Data Structures -``.. doxygenstruct::`` together with ``:members:`` * Macros - ``.. doxygendefine::`` * Type Definitions - ``.. doxygentypedef::`` * Enumerations - ``.. doxygenenum::`` @@ -62,30 +63,48 @@ API Reference See `Breathe documentation `_ for additional information. - 3. Once done remove superfluous headers. - 4. When changes are committed and documentation is build, check how this section rendered. :doc:`Correct annotations <../documenting-code>` in respective header files, if required. + 4. Once done remove superfluous headers. + 5. When changes are committed and documentation is build, check how this section rendered. :doc:`Correct annotations <../documenting-code>` in respective header files, if required. + +Header Files +^^^^^^^^^^^^ + + * `path/header-file.h` Data Structures ^^^^^^^^^^^^^^^ -``.. doxygenstruct:: name_of_structure`` +:: + + .. doxygenstruct:: name_of_structure + :members: Macros ^^^^^^ -``.. doxygendefine:: name_of_macro`` +:: + + .. doxygendefine:: name_of_macro Type Definitions ^^^^^^^^^^^^^^^^ -``.. doxygentypedef:: name_of_type`` +:: + + .. doxygentypedef:: name_of_type Enumerations ^^^^^^^^^^^^ -``.. doxygenenum:: name_of_enumeration`` +:: + + .. doxygenenum:: name_of_enumeration Functions ^^^^^^^^^ -``.. doxygenfunction:: name_of_function`` +:: + + .. doxygenfunction:: name_of_function + + diff --git a/docs/api/uart.rst b/docs/api/uart.rst new file mode 100644 index 0000000000..609816fd46 --- /dev/null +++ b/docs/api/uart.rst @@ -0,0 +1,98 @@ +UART +==== + +Overview +-------- + +`Instructions`_ + +Application Example +------------------- + +`Instructions`_ + +API Reference +------------- + +`Instructions`_ + +.. _Instructions: template.html + +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/uart.h `_ + +Data Structures +^^^^^^^^^^^^^^^ + +.. doxygenstruct:: uart_config_t + :members: + +.. doxygenstruct:: uart_intr_config_t + :members: + +.. doxygenstruct:: uart_event_t + :members: + +Macros +^^^^^^ + +.. doxygendefine:: UART_FIFO_LEN +.. doxygendefine:: UART_INTR_MASK +.. doxygendefine:: UART_LINE_INV_MASK +.. doxygendefine:: UART_BITRATE_MAX +.. doxygendefine:: UART_PIN_NO_CHANGE +.. doxygendefine:: UART_INVERSE_DISABLE +.. doxygendefine:: UART_INVERSE_RXD +.. doxygendefine:: UART_INVERSE_CTS +.. doxygendefine:: UART_INVERSE_TXD +.. doxygendefine:: UART_INVERSE_RTS + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: uart_word_length_t +.. doxygenenum:: uart_stop_bits_t +.. doxygenenum:: uart_port_t +.. doxygenenum:: uart_parity_t +.. doxygenenum:: uart_hw_flowcontrol_t +.. doxygenenum:: uart_event_type_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: uart_set_word_length +.. doxygenfunction:: uart_get_word_length +.. doxygenfunction:: uart_set_stop_bits +.. doxygenfunction:: uart_get_stop_bits +.. doxygenfunction:: uart_set_parity +.. doxygenfunction:: uart_get_parity +.. doxygenfunction:: uart_set_baudrate +.. doxygenfunction:: uart_get_baudrate +.. doxygenfunction:: uart_set_line_inverse +.. doxygenfunction:: uart_set_hw_flow_ctrl +.. doxygenfunction:: uart_get_hw_flow_ctrl +.. doxygenfunction:: uart_clear_intr_status +.. doxygenfunction:: uart_enable_intr_mask +.. doxygenfunction:: uart_disable_intr_mask +.. doxygenfunction:: uart_enable_rx_intr +.. doxygenfunction:: uart_disable_rx_intr +.. doxygenfunction:: uart_disable_tx_intr +.. doxygenfunction:: uart_enable_tx_intr +.. doxygenfunction:: uart_isr_register +.. doxygenfunction:: uart_set_pin +.. doxygenfunction:: uart_set_rts +.. doxygenfunction:: uart_set_dtr +.. doxygenfunction:: uart_param_config +.. doxygenfunction:: uart_intr_config +.. doxygenfunction:: uart_driver_install +.. doxygenfunction:: uart_driver_delete +.. doxygenfunction:: uart_wait_tx_done +.. doxygenfunction:: uart_tx_chars +.. doxygenfunction:: uart_write_bytes +.. doxygenfunction:: uart_write_bytes_with_break +.. doxygenfunction:: uart_read_bytes +.. doxygenfunction:: uart_flush + + diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst index 122a8671ea..df6cd03f67 100644 --- a/docs/api/vfs.rst +++ b/docs/api/vfs.rst @@ -8,6 +8,12 @@ Application Example API Reference ------------- +Header Files +^^^^^^^^^^^^ + + * `vfs/include/esp_vfs.h `_ + * `vfs/include/esp_vfs_dev.h `_ + Macros ^^^^^^ From 341593f7d204898fbc9686a3edc05972765386ee Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 12:29:38 +1100 Subject: [PATCH 182/285] build system: Remove need for $(Q) macro in recipes, use --silent in MAKEFLAGS instead --- components/bootloader/Makefile.projbuild | 6 ++-- components/esptool_py/Makefile.projbuild | 6 ++-- components/partition_table/Makefile.projbuild | 8 ++--- docs/build_system.rst | 30 ++++++++----------- make/common.mk | 9 +++--- make/component_wrapper.mk | 12 ++++---- make/project.mk | 6 ++-- make/project_config.mk | 14 ++++----- tools/kconfig/Makefile | 26 ++++++++-------- 9 files changed, 56 insertions(+), 61 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index c03288d107..90d9784427 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -21,10 +21,10 @@ BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) $(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE) - $(Q) $(BOOTLOADER_MAKE) $@ + $(BOOTLOADER_MAKE) $@ bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean + $(BOOTLOADER_MAKE) app-clean clean: bootloader-clean @@ -41,7 +41,7 @@ bootloader-flash: $(BOOTLOADER_BIN) $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ $(BOOTLOADER_BUILD_DIR): - $(Q) mkdir -p $@ + mkdir -p $@ else CFLAGS += -D BOOTLOADER_BUILD=1 -I $(IDF_PATH)/components/esp32/include diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 69c01e1e7f..27a2132845 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -24,14 +24,14 @@ ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_CO ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< + $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." - $(Q) $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) + $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) @echo "Flashing app to serial port $(ESPPORT), offset $(CONFIG_APP_OFFSET)..." - $(Q) $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) + $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) $(eval $(call SubmoduleCheck,$(ESPTOOLPY_SRC),$(COMPONENT_PATH)/esptool)) diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 98631cc854..0799c91d4f 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -20,7 +20,7 @@ PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.cs $(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." - $(Q) $(GEN_ESP32PART) $< $@ + $(GEN_ESP32PART) $< $@ all_binaries: $(PARTITION_TABLE_BIN) @@ -30,16 +30,16 @@ ESPTOOL_ALL_FLASH_ARGS += 0x4000 $(PARTITION_TABLE_BIN) partition_table: $(PARTITION_TABLE_BIN) @echo "Partition table binary generated. Contents:" @echo $(SEPARATOR) - $(Q) $(GEN_ESP32PART) $< + $(GEN_ESP32PART) $< @echo $(SEPARATOR) @echo "Partition flashing command:" @echo "$(PARTITION_TABLE_FLASH_CMD)" partition_table-flash: $(PARTITION_TABLE_BIN) @echo "Flashing partition table..." - $(Q) $(PARTITION_TABLE_FLASH_CMD) + $(PARTITION_TABLE_FLASH_CMD) partition_table-clean: - $(Q) rm -f $(PARTITION_TABLE_BIN) + rm -f $(PARTITION_TABLE_BIN) clean: partition_table-clean diff --git a/docs/build_system.rst b/docs/build_system.rst index a9ebcfe004..50514083ef 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -275,6 +275,18 @@ Second Level: Component Makefiles To better understand the component make process, have a read through the ``component_wrapper.mk`` file and some of the ``component.mk`` files included with esp-idf. +Debugging The Make Process +-------------------------- + +Some tips for debugging the esp-idf build system: + +- Appending ``V=1`` to the make arguments (or setting it as an environment variable) will cause make to echo all commands executed, and also each directory as it is entered for a sub-make. +- Running ``make -w`` will cause make to echo each directory as it is entered for a sub-make - same as ``V=1`` but without also echoing all commands. +- Running ``make --trace`` (possibly in addition to one of the above arguments) will print out every target as it is built, and the dependency which caused it to be built. +- Running ``make -p`` prints a (very verbose) summary of every generated target in each makefile. + +For more debugging tips and general make information, see the `GNU Make Manual`. + Overriding Parts of the Project ------------------------------- @@ -395,24 +407,6 @@ component's name would have to be added to the other component's ``COMPONENT_DEPENDS`` list to ensure that the components were built in-order. -Cosmetic Improvements -^^^^^^^^^^^^^^^^^^^^^ - -The above example will work just fine, but there's one last cosmetic -improvement that can be done. The make system tries to make the make -process somewhat easier on the eyes by hiding the commands (unless you -run make with the V=1 switch) and this does not do that yet. Here's an -improved version that will output in the same style as the rest of the -make process:: - - COMPONENT_EXTRA_CLEAN := test_tjpgd_logo.h - - graphics_lib.o: logo.h - - logo.h: $(COMPONENT_PATH)/logo.bmp - $(summary) BMP2H $@ - $(Q) bmp2h -i $^ -o $@ - Fully Overriding The Component Makefile --------------------------------------- diff --git a/make/common.mk b/make/common.mk index 779811f1ad..c465fd4b22 100644 --- a/make/common.mk +++ b/make/common.mk @@ -16,13 +16,14 @@ export SDKCONFIG_MAKEFILE # sub-makes (like bootloader) will reuse this path # if V is unset or not 1, $(summary) echoes a summary and $(details) does nothing V ?= $(VERBOSE) ifeq ("$(V)","1") -Q := summary := @true details := @echo else -Q := @ summary := @echo details := @true + +# disable echoing of commands, directory names +MAKEFLAGS += --silent endif # Pseudo-target to check a git submodule has been properly initialised @@ -36,8 +37,8 @@ endif define SubmoduleCheck $(1): @echo "WARNING: Missing submodule $(2) for $$@..." - $(Q) [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) - $(Q) [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule --init' in esp-idf root directory."; exit 1) + [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) + [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule --init' in esp-idf root directory."; exit 1) @echo "Attempting 'git submodule update --init' in esp-idf root directory..." cd ${IDF_PATH} && git submodule update --init $(2) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index fe081e32fd..84bd52da5a 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -129,8 +129,8 @@ build: $(COMPONENT_LIBRARY) # an archive when multiple filenames have the same name (src1/test.o and src2/test.o) $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(summary) AR $@ - $(Q) rm -f $@ - $(Q) $(AR) cru $@ $(COMPONENT_OBJS) + rm -f $@ + $(AR) cru $@ $(COMPONENT_OBJS) endif # If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target @@ -139,7 +139,7 @@ CLEAN_FILES = $(COMPONENT_LIBRARY) $(COMPONENT_OBJS) $(COMPONENT_OBJS:.o=.d) $(C .PHONY: clean clean: $(summary) RM $(CLEAN_FILES) - $(Q) rm -f $(CLEAN_FILES) + rm -f $(CLEAN_FILES) endif # Include all dependency files already generated @@ -150,15 +150,15 @@ define GenerateCompileTargets # $(1) - directory containing source files, relative to $(COMPONENT_PATH) $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.c | $(1) $$(summary) CC $$@ - $$(Q) $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.cpp | $(1) $$(summary) CXX $$@ - $$(Q) $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(CXX) $$(CXXFLAGS) $$(CPPFLAGS) $$(addprefix -I,$$(COMPONENT_INCLUDES)) $$(addprefix -I,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ $(1)/%.o: $$(COMPONENT_PATH)/$(1)/%.S | $(1) $$(summary) AS $$@ - $$(Q) $$(CC) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ + $$(CC) $$(CPPFLAGS) $$(addprefix -I ,$$(COMPONENT_INCLUDES)) $$(addprefix -I ,$$(COMPONENT_EXTRA_INCLUDES)) -I$(1) -c $$< -o $$@ # CWD is build dir, create the build subdirectory if it doesn't exist $(1): diff --git a/make/project.mk b/make/project.mk index 4554d1d329..a273da1c25 100644 --- a/make/project.mk +++ b/make/project.mk @@ -238,7 +238,7 @@ COMPONENT_LIBRARIES = $(filter $(notdir $(COMPONENT_PATHS_BUILDABLE)),$(APP_LIBR # the rules to build these are emitted as part of GenerateComponentTarget below $(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp)/lib$(libcomp).a) $(summary) LD $(notdir $@) - $(Q) $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP) + $(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP) # Generation of $(APP_BIN) from $(APP_ELF) is added by the esptool # component's Makefile.projbuild @@ -257,7 +257,7 @@ $(BUILD_DIR_BASE): # # Is recursively expanded by the GenerateComponentTargets macro define ComponentMake -$(Q) +$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk ++$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk endef # Generate top-level component-specific targets for each component @@ -303,7 +303,7 @@ $(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponent app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) - $(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) + rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) clean: app-clean diff --git a/make/project_config.mk b/make/project_config.mk index 09cec2e0be..aac231f99f 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -23,7 +23,7 @@ KCONFIG_TOOL_ENV=KCONFIG_AUTOHEADER=$(abspath $(BUILD_DIR_BASE)/include/sdkconfi menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(summary) MENUCONFIG - $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig + $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(SDKCONFIG))","") ifeq ("$(filter defconfig,$(MAKECMDGOALS))","") @@ -36,8 +36,8 @@ endif defconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(BUILD_DIR_BASE) $(summary) DEFCONFIG - $(Q) mkdir -p $(BUILD_DIR_BASE)/include/config - $(Q) $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig + mkdir -p $(BUILD_DIR_BASE)/include/config + $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --olddefconfig $(IDF_PATH)/Kconfig # Work out of whether we have to build the Kconfig makefile # (auto.conf), or if we're in a situation where we don't need it @@ -56,9 +56,9 @@ endif $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(SDKCONFIG) $(KCONFIG_TOOL_DIR)/conf $(COMPONENT_KCONFIGS) $(COMPONENT_KCONFIGS_PROJBUILD) $(summary) GENCONFIG - $(Q) mkdir -p $(BUILD_DIR_BASE)/include/config - $(Q) cd $(BUILD_DIR_BASE); $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --silentoldconfig $(IDF_PATH)/Kconfig - $(Q) touch $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h + mkdir -p $(BUILD_DIR_BASE)/include/config + cd $(BUILD_DIR_BASE); $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/conf --silentoldconfig $(IDF_PATH)/Kconfig + touch $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h # touch to ensure both output files are newer - as 'conf' can also update sdkconfig (a dependency). Without this, # sometimes you can get an infinite make loop on Windows where sdkconfig always gets regenerated newer # than the target(!) @@ -68,4 +68,4 @@ clean: config-clean config-clean: $(summary RM CONFIG) $(MAKE) -C $(KCONFIG_TOOL_DIR) clean - $(Q) rm -rf $(BUILD_DIR_BASE)/include/config $(BUILD_DIR_BASE)/include/sdkconfig.h + rm -rf $(BUILD_DIR_BASE)/include/config $(BUILD_DIR_BASE)/include/sdkconfig.h diff --git a/tools/kconfig/Makefile b/tools/kconfig/Makefile index fee5a69316..9680b74109 100644 --- a/tools/kconfig/Makefile +++ b/tools/kconfig/Makefile @@ -41,13 +41,13 @@ nconfig: nconf $< $(silent) $(Kconfig) silentoldconfig: conf - $(Q)mkdir -p include/config include/generated + mkdir -p include/config include/generated $< $(silent) --$@ $(Kconfig) localyesconfig localmodconfig: streamline_config.pl conf - $(Q)mkdir -p include/config include/generated - $(Q)perl $< --$@ . $(Kconfig) > .tmp.config - $(Q)if [ -f .config ]; then \ + mkdir -p include/config include/generated + perl $< --$@ . $(Kconfig) > .tmp.config + if [ -f .config ]; then \ cmp -s .tmp.config .config || \ (mv -f .config .config.old.1; \ mv -f .tmp.config .config; \ @@ -57,7 +57,7 @@ localyesconfig localmodconfig: streamline_config.pl conf mv -f .tmp.config .config; \ conf $(silent) --silentoldconfig $(Kconfig); \ fi - $(Q)rm -f .tmp.config + rm -f .tmp.config # These targets map 1:1 to the commandline options of 'conf' @@ -84,22 +84,22 @@ ifeq ($(KBUILD_DEFCONFIG),) else ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),) @$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'" - $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) + $< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) else @$(kecho) "*** Default configuration is based on target '$(KBUILD_DEFCONFIG)'" - $(Q) $(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) + $(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) endif endif %_defconfig: conf - $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) + $< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) %.config: conf $(if $(call configfiles),, $(error No configuration exists for this target on this architecture)) - $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) - +$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig + $(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) + +yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig PHONY += kvmconfig kvmconfig: kvm_guest.config @@ -111,7 +111,7 @@ xenconfig: xen.config PHONY += tinyconfig tinyconfig: - $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config + $(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config # Help text used by make help help: @@ -181,7 +181,7 @@ clean-files += $(conf-objs) $(mconf-objs) conf mconf $(lxdialog) PHONY += dochecklxdialog $(addprefix ,$(lxdialog)): dochecklxdialog dochecklxdialog: - $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(CC) $(CFLAGS) $(LOADLIBES_mconf) + $(CONFIG_SHELL) $(check-lxdialog) -check $(CC) $(CFLAGS) $(LOADLIBES_mconf) always := dochecklxdialog @@ -285,7 +285,7 @@ quiet_cmd_moc = MOC $@ # Extract gconf menu items for i18n support gconf.glade.h: gconf.glade - $(Q)intltool-extract --type=gettext/glade --srcdir=$(srctree) \ + intltool-extract --type=gettext/glade --srcdir=$(srctree) \ gconf.glade From 7b77daaea41274a221930f01d8bc1226ef46a336 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Fri, 11 Nov 2016 10:51:33 +0800 Subject: [PATCH 183/285] wps: add blocking param for API esp_wifi_wps_start() --- components/esp32/include/esp_wps.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/esp32/include/esp_wps.h b/components/esp32/include/esp_wps.h index 0a413a1978..f95eaa5e2a 100644 --- a/components/esp32/include/esp_wps.h +++ b/components/esp32/include/esp_wps.h @@ -83,7 +83,9 @@ esp_err_t esp_wifi_wps_disable(void); * * @attention WPS can only be used when ESP32 station is enabled. * - * @param null + * @param timeout_ms : maximum blocking time before API return. + * - 0 : non-blocking + * - 1~120000 : blocking time (not supported in IDF v1.0) * * @return * - ESP_OK : succeed @@ -92,7 +94,7 @@ esp_err_t esp_wifi_wps_disable(void); * - ESP_ERR_WIFI_WPS_SM : wps state machine is not initialized * - ESP_ERR_WIFI_FAIL : wps initialization fails */ -esp_err_t esp_wifi_wps_start(void); +esp_err_t esp_wifi_wps_start(int timeout_ms); /** * @} From 9aa9086c8b72852a58ded9bb1769640c5eaffee1 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 11 Nov 2016 12:16:54 +0800 Subject: [PATCH 184/285] examples: minor tweaks to comments --- examples/07_nvs_rw_value/README.md | 4 ++-- examples/07_nvs_rw_value/main/component.mk | 8 -------- examples/07_nvs_rw_value/main/nvs_rw_value.c | 5 ++++- examples/08_nvs_rw_blob/main/component.mk | 8 -------- examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 5 ++++- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/examples/07_nvs_rw_value/README.md b/examples/07_nvs_rw_value/README.md index 83dc29fd18..09cd364e8d 100644 --- a/examples/07_nvs_rw_value/README.md +++ b/examples/07_nvs_rw_value/README.md @@ -4,9 +4,9 @@ Demonstrates how to read and write a single integer value using NVS. The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts. -Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVR. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. +Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVS. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. -Check another example *08_nvs_rw_blob*, that shows how to read and write a blob (binary large object). +Check another example *08_nvs_rw_blob*, that shows how to read and write variable length binary data (blob). Detailed functional description of NVS and API is provided in [documentation](http://esp-idf.readthedocs.io/en/latest/api/nvs_flash.html). diff --git a/examples/07_nvs_rw_value/main/component.mk b/examples/07_nvs_rw_value/main/component.mk index 24356f23ed..d33485c26c 100644 --- a/examples/07_nvs_rw_value/main/component.mk +++ b/examples/07_nvs_rw_value/main/component.mk @@ -1,10 +1,2 @@ -# -# Main Makefile. This is basically the same as a component makefile. -# -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# include $(IDF_PATH)/make/component_common.mk diff --git a/examples/07_nvs_rw_value/main/nvs_rw_value.c b/examples/07_nvs_rw_value/main/nvs_rw_value.c index df53d33b4a..978c48edb8 100644 --- a/examples/07_nvs_rw_value/main/nvs_rw_value.c +++ b/examples/07_nvs_rw_value/main/nvs_rw_value.c @@ -55,7 +55,10 @@ void app_main() err = nvs_set_i32(my_handle, "restart_conter", restart_counter); printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); - // Commit + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. printf("Committing updates in NVS ... "); err = nvs_commit(my_handle); printf((err != ESP_OK) ? "Failed!\n" : "Done\n"); diff --git a/examples/08_nvs_rw_blob/main/component.mk b/examples/08_nvs_rw_blob/main/component.mk index 24356f23ed..d33485c26c 100644 --- a/examples/08_nvs_rw_blob/main/component.mk +++ b/examples/08_nvs_rw_blob/main/component.mk @@ -1,10 +1,2 @@ -# -# Main Makefile. This is basically the same as a component makefile. -# -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# include $(IDF_PATH)/make/component_common.mk diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c index 38066bf62f..3fbdfcacd6 100644 --- a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -44,7 +44,10 @@ esp_err_t save_restart_counter(void) err = nvs_set_i32(my_handle, "restart_conter", restart_counter); if (err != ESP_OK) return err; - // Commit + // Commit written value. + // After setting any values, nvs_commit() must be called to ensure changes are written + // to flash storage. Implementations may write to storage at other times, + // but this is not guaranteed. err = nvs_commit(my_handle); if (err != ESP_OK) return err; From fdf3db19590cd722c8d0be11b3649cc2b1ff8c29 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 11 Nov 2016 12:26:42 +0800 Subject: [PATCH 185/285] Comment fix --- components/freertos/xtensa_vectors.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index 2a373810fd..f180705e70 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -117,7 +117,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Macro get_percpu_entry_for - convert a per-core ID into a multicore entry. Basically does reg=reg*portNUM_PROCESSORS+current_core_id - Multiple versions here for multiple + Multiple versions here to optimize for specific portNUM_PROCESSORS values. */ .macro get_percpu_entry_for reg scratch #if (portNUM_PROCESSORS == 1) From 57009aaa7f9138958678d08bc9ecc2f037f4e9da Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 11 Nov 2016 19:20:54 +0800 Subject: [PATCH 186/285] Add a more scalable mechanism for the FreeRTOS tick- and idle hooks; idle handler now uses WAITI to reduce power --- components/esp32/freertos_hooks.c | 81 +++++++++++++++++++ components/esp32/include/esp_freertos_hooks.h | 58 +++++++++++++ components/esp32/int_wdt.c | 64 ++++++++------- components/esp32/task_wdt.c | 24 ++++-- components/freertos/Kconfig | 29 +++++++ .../include/freertos/FreeRTOSConfig.h | 4 +- components/freertos/tasks.c | 20 +++-- 7 files changed, 234 insertions(+), 46 deletions(-) create mode 100644 components/esp32/freertos_hooks.c create mode 100644 components/esp32/include/esp_freertos_hooks.h diff --git a/components/esp32/freertos_hooks.c b/components/esp32/freertos_hooks.c new file mode 100644 index 0000000000..50ebd3d054 --- /dev/null +++ b/components/esp32/freertos_hooks.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include "esp_attr.h" +#include "esp_freertos_hooks.h" + +//We use just a static array here because it's not expected many components will need +//an idle or tick hook. +#define MAX_HOOKS 8 + +static esp_freertos_idle_cb_t idle_cb[MAX_HOOKS]={0}; +static esp_freertos_tick_cb_t tick_cb[MAX_HOOKS]={0}; + +void IRAM_ATTR esp_vApplicationTickHook() +{ + int n; + for (n=0; n +#include "esp_err.h" + + +/* + Definitions for the tickhook and idlehook callbacks +*/ +typedef bool (*esp_freertos_idle_cb_t)(); +typedef void (*esp_freertos_tick_cb_t)(); + +/** + * @brief Register a callback to be called on the freertos idle hook + * The callback should return true if it's okay for the core to + * sleep until an interrupt (or FreeRTOS tick) happens and false + * if it should be called again as fast as possible. + * + * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be called + * + * @return ESP_OK : Callback registered + * @return ESP_ERR_NO_MEM : No more space to register hook + */ +esp_err_t esp_register_freertos_idle_hook(esp_freertos_idle_cb_t new_idle_cb); + +/** + * @brief Register a callback to be called on the freertos tick hook + * + * @param esp_freertos_tick_cb_t new_tick_cb : Callback to be called + * + * @return ESP_OK : Callback registered + * @return ESP_ERR_NO_MEM : No more space to register hook + */ +esp_err_t esp_register_freertos_tick_hook(esp_freertos_tick_cb_t tick_cb); + + +/** + * @brief Unregister an idle callback registered earlier + * + * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be unregistered + * + * @return void + */ +void esp_deregister_freertos_idle_hook(esp_freertos_idle_cb_t old_idle_cb); + + +/** + * @brief Unregister a tick callback registered earlier + * + * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be unregistered + * + * @return void + */ +void esp_deregister_freertos_tick_hook(esp_freertos_tick_cb_t old_tick_cb); + + +#endif \ No newline at end of file diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 11de8f20d2..fe3ddab370 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -25,6 +25,7 @@ #include "esp_err.h" #include "esp_intr.h" #include "esp_attr.h" +#include "esp_freertos_hooks.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" @@ -36,6 +37,38 @@ #define WDT_INT_NUM 24 +//Take care: the tick hook can also be called before esp_int_wdt_init() is called. +#if CONFIG_INT_WDT_CHECK_CPU1 +//Not static; the ISR assembly checks this. +bool int_wdt_app_cpu_ticked=false; + +static void IRAM_ATTR tick_hook(void) { + if (xPortGetCoreID()!=0) { + int_wdt_app_cpu_ticked=true; + } else { + //Only feed wdt if app cpu also ticked. + if (int_wdt_app_cpu_ticked) { + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + int_wdt_app_cpu_ticked=false; + } + } +} +#else +static void IRAM_ATTR tick_hook(void) { + if (xPortGetCoreID()!=0) return; + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; + TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt + TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; +} +#endif + + void esp_int_wdt_init() { TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS @@ -53,6 +86,7 @@ void esp_int_wdt_init() { TIMERG1.wdt_wprotect=0; TIMERG1.int_clr_timers.wdt=1; TIMERG1.int_ena.wdt=1; + esp_register_freertos_tick_hook(tick_hook); ESP_INTR_DISABLE(WDT_INT_NUM); intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); //We do not register a handler for the interrupt because it is interrupt level 4 which @@ -62,35 +96,5 @@ void esp_int_wdt_init() { } -//Take care: the tick hook can also be called before esp_int_wdt_init() is called. -#if CONFIG_INT_WDT_CHECK_CPU1 -//Not static; the ISR assembly checks this. -bool int_wdt_app_cpu_ticked=false; - -void IRAM_ATTR vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) { - int_wdt_app_cpu_ticked=true; - } else { - //Only feed wdt if app cpu also ticked. - if (int_wdt_app_cpu_ticked) { - TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; - TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt - TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; - int_wdt_app_cpu_ticked=false; - } - } -} -#else -void IRAM_ATTR vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) return; - TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; - TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt - TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; -} -#endif #endif \ No newline at end of file diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index bec1cadaa7..f8cfdef26e 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -26,6 +26,7 @@ #include "esp_err.h" #include "esp_intr.h" #include "esp_attr.h" +#include "esp_freertos_hooks.h" #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" #include "esp_log.h" @@ -140,6 +141,18 @@ void esp_task_wdt_delete() { } } + +#if CONFIG_TASK_WDT_CHECK_IDLE_TASK +static bool idle_hook(void) { +#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 + if (xPortGetCoreID()!=0) return; +#endif + esp_task_wdt_feed(); + return true; +} +#endif + + void esp_task_wdt_init() { TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS @@ -153,6 +166,9 @@ void esp_task_wdt_init() { TIMERG0.wdt_config0.en=1; TIMERG0.wdt_feed=1; TIMERG0.wdt_wprotect=0; +#if CONFIG_TASK_WDT_CHECK_IDLE_TASK + esp_register_freertos_idle_hook(idle_hook); +#endif ESP_INTR_DISABLE(ETS_T0_WDT_INUM); intr_matrix_set(xPortGetCoreID(), ETS_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); @@ -161,13 +177,5 @@ void esp_task_wdt_init() { ESP_INTR_ENABLE(ETS_T0_WDT_INUM); } -#if CONFIG_TASK_WDT_CHECK_IDLE_TASK -void vApplicationIdleHook(void) { -#if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 - if (xPortGetCoreID()!=0) return; -#endif - esp_task_wdt_feed(); -} -#endif #endif \ No newline at end of file diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 91e824b2dc..b9db00e50b 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -141,6 +141,35 @@ config FREERTOS_ISR_STACKSIZE The interrupt handlers have their own stack. The size of the stack can be defined here. Each processor has its own stack, so the total size occupied will be twice this. +config FREERTOS_LEGACY_HOOKS + bool "Use FreeRTOS legacy hooks" + default n + help + FreeRTOS offers a number of hooks/callback functions that are called when a timer + tick happens, the idle thread runs etc. esp-idf replaces these by runtime registerable + hooks using the esp_register_freertos_xxx_hook system, but for legacy reasons the old + hooks can also still be enabled. Please enable this only if you have code that for some + reason can't be migrated to the esp_register_freertos_xxx_hook system. + +if FREERTOS_LEGACY_HOOKS + +config FREERTOS_LEGACY_IDLE_HOOK + bool "Enable legacy idle hook" + default n + help + If enabled, FreeRTOS will call a function called vApplicationIdleHook when the idle thread + on a CPU is running. Please make sure your code defines such a function. + +config FREERTOS_LEGACY_TICK_HOOK + bool "Enable legacy tick hook" + default n + help + If enabled, FreeRTOS will call a function called vApplicationTickHook when a FreeRTOS + tick is executed. Please make sure your code defines such a function. + +endif #FREERTOS_LEGACY_HOOKS + + menuconfig FREERTOS_DEBUG_INTERNALS bool "Debug FreeRTOS internals" default n diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 47566ab3b3..13ce73e0a8 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -152,9 +152,9 @@ *----------------------------------------------------------*/ #define configUSE_PREEMPTION 1 -#define configUSE_IDLE_HOOK ( CONFIG_TASK_WDT_CHECK_IDLE_TASK ) +#define configUSE_IDLE_HOOK ( CONFIG_FREERTOS_LEGACY_IDLE_HOOK ) -#define configUSE_TICK_HOOK ( CONFIG_INT_WDT ) +#define configUSE_TICK_HOOK ( CONFIG_FREERTOS_LEGACY_TICK_HOOK ) #define configTICK_RATE_HZ ( CONFIG_FREERTOS_HZ ) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index b79d3a98ba..88aa8d3ef5 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -476,6 +476,7 @@ to its original value when it is released. */ #if configUSE_TICK_HOOK > 0 extern void vApplicationTickHook( void ); #endif +extern void esp_vApplicationTickHook( void ); #if portFIRST_TASK_HOOK extern void vPortFirstTaskHook(TaskFunction_t taskfn); @@ -2360,22 +2361,21 @@ BaseType_t xSwitchRequired = pdFALSE; We can't really calculate what we need, that's done on core 0... just assume we need a switch. ToDo: Make this more intelligent? -- JD */ - //We do need the tick hook to satisfy the int watchdog. - #if ( configUSE_TICK_HOOK == 1 ) { /* Guard against the tick hook being called when the pended tick count is being unwound (when the scheduler is being unlocked). */ if( ( uxSchedulerSuspended[ xPortGetCoreID() ] != ( UBaseType_t ) pdFALSE ) || uxPendedTicks == ( UBaseType_t ) 0U ) { + #if ( configUSE_TICK_HOOK == 1 ) vApplicationTickHook(); + #endif /* configUSE_TICK_HOOK */ + esp_vApplicationTickHook(); } else { mtCOVERAGE_TEST_MARKER(); } } - #endif /* configUSE_TICK_HOOK */ - return pdTRUE; } @@ -2506,20 +2506,21 @@ BaseType_t xSwitchRequired = pdFALSE; } #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ - #if ( configUSE_TICK_HOOK == 1 ) { /* Guard against the tick hook being called when the pended tick count is being unwound (when the scheduler is being unlocked). */ if( uxPendedTicks == ( UBaseType_t ) 0U ) { + #if ( configUSE_TICK_HOOK == 1 ) vApplicationTickHook(); + #endif /* configUSE_TICK_HOOK */ + esp_vApplicationTickHook(); } else { mtCOVERAGE_TEST_MARKER(); } } - #endif /* configUSE_TICK_HOOK */ taskEXIT_CRITICAL_ISR(&xTaskQueueMutex); } else @@ -2533,6 +2534,7 @@ BaseType_t xSwitchRequired = pdFALSE; vApplicationTickHook(); } #endif + esp_vApplicationTickHook(); } #if ( configUSE_PREEMPTION == 1 ) @@ -3270,6 +3272,12 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) vApplicationIdleHook(); } #endif /* configUSE_IDLE_HOOK */ + { + /* Call the esp-idf hook system */ + extern void esp_vApplicationIdleHook( void ); + esp_vApplicationIdleHook(); + } + /* This conditional compilation should use inequality to 0, not equality to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when From cc510c1d95086b6a94ea92c260eceb40f75e3577 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 10:51:53 +1100 Subject: [PATCH 187/285] build system: Remove make 3.81 "component_project_vars.mk: No such file or directory" messages Also add an explicit make version check & warning. --- make/project.mk | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/make/project.mk b/make/project.mk index a273da1c25..a10f6cd24a 100644 --- a/make/project.mk +++ b/make/project.mk @@ -36,6 +36,13 @@ help: @echo "See also 'make bootloader', 'make bootloader-flash', 'make bootloader-clean', " @echo "'make partition_table', etc, etc." +# dependency checks +ifndef MAKE_RESTARTS +ifeq ("$(filter 4.% 3.81 3.82,$(MAKE_VERSION))","") +$(warning "esp-idf build system only supports GNU Make versions 3.81 or newer. You may see unexpected results with other Makes.") +endif +endif + # disable built-in make rules, makes debugging saner MAKEFLAGS_OLD := $(MAKEFLAGS) MAKEFLAGS +=-rR @@ -105,7 +112,8 @@ COMPONENT_LDFLAGS := # See the component_project_vars.mk target in component_wrapper.mk COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE))) COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) -include $(COMPONENT_PROJECT_VARS) +# this line is -include instead of include to prevent a spurious error message on make 3.81 +-include $(COMPONENT_PROJECT_VARS) # Also add top-level project include path, for top-level includes COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/) From f9e9e6b9389e009d45a9c3271180666007eb27f4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 10:57:45 +1100 Subject: [PATCH 188/285] Build system: Change deprecation message to include component path, easier to fix --- make/component_common.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/make/component_common.mk b/make/component_common.mk index 32af3c7c06..187e1ed63a 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -1,3 +1 @@ -$(warning Deprecated feature: It is no longer necessary to include component_common.mk.) -$(warning The line "include $$(IDF_PATH)/make/component_common.mk" can be removed from component.mk files.) - +$(warning Deprecated feature: No longer necessary to include component_common.mk from $(COMPONENT_PATH)/component.mk) From b5de58139919e1143f289efc426569bb2d111106 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 3 Nov 2016 17:33:30 +1100 Subject: [PATCH 189/285] Secure boot: initial image signature support --- .gitmodules | 3 + components/bootloader/Kconfig.projbuild | 16 +++ components/bootloader/Makefile.projbuild | 24 +++-- components/bootloader/src/Makefile | 2 +- .../bootloader/src/main/bootloader_start.c | 25 +++++ .../bootloader_support/Makefile.projbuild | 3 + components/bootloader_support/component.mk | 25 +++++ .../include/esp_secure_boot.h | 10 +- .../src/secure_boot_signatures.c | 99 +++++++++++++++++++ components/esptool_py/Makefile.projbuild | 6 ++ components/esptool_py/esptool | 2 +- components/micro-ecc/component.mk | 8 ++ components/micro-ecc/micro-ecc | 1 + components/partition_table/Makefile.projbuild | 3 + docs/security/secure-boot.rst | 87 +++++++++------- make/common.mk | 20 +++- make/component_common.mk | 6 +- make/project.mk | 1 + 18 files changed, 291 insertions(+), 50 deletions(-) create mode 100644 components/bootloader_support/Makefile.projbuild create mode 100644 components/bootloader_support/src/secure_boot_signatures.c create mode 100644 components/micro-ecc/component.mk create mode 160000 components/micro-ecc/micro-ecc diff --git a/.gitmodules b/.gitmodules index df40848261..c26f92e80c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "components/bt/lib"] path = components/bt/lib url = https://github.com/espressif/esp32-bt-lib.git +[submodule "components/micro-ecc/micro-ecc"] + path = components/micro-ecc/micro-ecc + url = https://github.com/kmackay/micro-ecc.git diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 55c2eebd14..b568f61a04 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -77,6 +77,22 @@ config SECURE_BOOTLOADER_KEY_FILE See docs/security/secure-boot.rst for details. +config SECURE_BOOT_SIGNING_KEY + string "Secure boot signing key" + depends on SECURE_BOOTLOADER_ENABLED + default secure_boot_signing_key.pem + help + Path to the key file used to sign partition tables and app images for secure boot. + + Key file is an ECDSA private key (NIST256p curve) in PEM format. + + Path is evaluated relative to the project directory. + + You can generate a new signing key by running the following command: + espsecure.py generate_signing_key secure_boot_signing_key.pem + + See docs/security/secure-boot.rst for details. + config SECURE_BOOTLOADER_ENABLED bool default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3799e913c8..831d988584 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,13 +15,17 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig -SECURE_BOOT_KEYFILE=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +# both signing key paths are resolved relative to the project directory +SECURE_BOOTLOADER_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY))) +export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) IS_BOOTLOADER_BUILD=1 + .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) $(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig @@ -59,16 +63,15 @@ bootloader: $(BOOTLOADER_BIN) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE -# Reflashable secure bootloader (recommended for testing only) -# generates a digest binary (bootloader + digest) and prints -# instructions for reflashing. User must run commands manually. +# Reflashable secure bootloader +# generates a digest binary (bootloader + digest) BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin bootloader: $(BOOTLOADER_DIGEST_BIN) @echo $(SEPARATOR) @echo "Bootloader built and secure digest generated. First time flash command is:" - @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOT_KEYFILE)" + @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOTLOADER_KEY)" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "To reflash the bootloader after initial flash:" @@ -77,15 +80,16 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." @echo "* Not recommended to re-use the same secure boot keyfile on multiple production devices." -$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) - @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" - $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOT_KEYFILE) -o $@ $< +$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) + @echo "DIGEST $(notdir $@)" + $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< -$(SECURE_BOOT_KEYFILE): +$(SECURE_BOOTLOADER_KEY): @echo $(SEPARATOR) - @echo "Need to generate secure boot digest key first. Run following command:" + @echo "Need to generate secure boot signing key. Run following command:" @echo "$(ESPSECUREPY) generate_key $@" @echo "Keep key file safe after generating." + @echo "(See secure boot documentation for caveats & alternatives.)") @exit 1 else diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index 278e0e9afb..1cfc80c5fe 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader bootloader_support log spi_flash +COMPONENTS := esptool_py bootloader bootloader_support log spi_flash micro-ecc # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 59b180fd84..3ebb8cf520 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -110,6 +110,7 @@ void IRAM_ATTR call_start_cpu0() */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { + esp_err_t err; const esp_partition_info_t *partitions; const int PARTITION_TABLE_SIZE = 0x1000; const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); @@ -118,6 +119,14 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + if(esp_secure_boot_enabled()) { + err = esp_secure_boot_verify_signature(addr, 0x1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table signature."); + return false; + } + } + partitions = bootloader_mmap(addr, 0x1000); if (!partitions) { ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); @@ -334,7 +343,23 @@ void bootloader_main() static void unpack_load_app(const esp_partition_pos_t* partition) { + esp_err_t err; esp_image_header_t image_header; + uint32_t image_length; + + if (esp_secure_boot_enabled()) { + /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ + err = esp_image_basic_verify(partition->offset, &image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); + return; + } + err = esp_secure_boot_verify_signature(partition->offset, image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err); + return; + } + } if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); diff --git a/components/bootloader_support/Makefile.projbuild b/components/bootloader_support/Makefile.projbuild new file mode 100644 index 0000000000..f93967a719 --- /dev/null +++ b/components/bootloader_support/Makefile.projbuild @@ -0,0 +1,3 @@ +# projbuild file for bootloader support +# (included in bootloader & main app) + diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index 2988fe287e..7f546dcba9 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -1,11 +1,36 @@ COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := include_priv +# include configuration macros early +include $(IDF_PATH)/make/common.mk + ifdef IS_BOOTLOADER_BUILD # share "private" headers with the bootloader component +# eventual goal: all functionality that needs this lives in bootloader_support COMPONENT_ADD_INCLUDEDIRS += include_priv endif COMPONENT_SRCDIRS := src +# +# Secure boot signing key support +# +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + +SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) + +COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) + +$(SECURE_BOOT_SIGNING_KEY): + @echo "Need to generate secure boot signing key." + @echo "One way is to run this command:" + @echo "$(ESPSECUREPY) generate_signing_key $@" + @echo "Keep key file safe after generating." + @echo "(See secure boot documentation for risks & alternatives.)" + @exit 1 + +$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) + $(ESPSECUREPY) extract_public_key --keyfile $< $@ +endif + include $(IDF_PATH)/make/component_common.mk diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index 4bf2dc8b23..c1c0171512 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -60,6 +60,14 @@ static inline bool esp_secure_boot_enabled(void) { */ esp_err_t esp_secure_boot_permanently_enable(void); - +/** @brief Verify the signature appended to some binary data in flash. + * + * @param src_addr Starting offset of the data in flash. + * @param length Length of data in bytes. Signature is appended -after- length bytes. + * + * @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if + * signature fails, ESP_FAIL for other failures (ie can't read flash). + */ +esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length); #endif diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c new file mode 100644 index 0000000000..5f02de38b0 --- /dev/null +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -0,0 +1,99 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "sdkconfig.h" + +#include "bootloader_flash.h" +#include "esp_log.h" +#include "esp_image_format.h" +#include "esp_secure_boot.h" + +#include "uECC.h" + +#ifdef BOOTLOADER_BUILD +#include "rom/sha.h" +typedef SHA_CTX sha_context; +#else +#include "hwcrypto/sha.h" +typedef esp_sha_context sha_context; +#endif + +typedef struct { + uint32_t version; + uint8_t signature[64]; +} signature_block_t; + +static const char* TAG = "secure_boot"; + +extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start"); +extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end"); + +#define SIGNATURE_VERIFICATION_KEYLEN 64 + +esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) +{ + sha_context sha; + uint8_t digest[64]; + ptrdiff_t keylen; + const uint8_t *data; + const signature_block_t *sigblock; + bool is_valid; + + ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length); + + data = bootloader_mmap(src_addr, length + sizeof(signature_block_t)); + if(data == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(signature_block_t)); + return ESP_FAIL; + } + + sigblock = (const signature_block_t *)(data + length); + + if (sigblock->version != 0) { + ESP_LOGE(TAG, "src 0x%x has invalid signature version field 0x%08x", src_addr, sigblock->version); + goto unmap_and_fail; + } + +#ifdef BOOTLOADER_BUILD + /* Use ROM SHA functions directly */ + ets_sha_enable(); + ets_sha_init(&sha); + ets_sha_update(&sha, SHA2_512, data, length); + ets_sha_finish(&sha, SHA2_512, digest); + ets_sha_disable(); +#else + /* Use thread-safe esp-idf SHA layer */ + esp_sha512_init(&sha); + esp_sha512_start(&sha, false); + esp_sha512_update(&sha, data, length); + esp_sha512_finish(&sha, digest); + esp_sha512_free(&sha); +#endif + + keylen = signature_verification_key_end - signature_verification_key_start; + if(keylen != SIGNATURE_VERIFICATION_KEYLEN) { + ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen); + goto unmap_and_fail; + } + + is_valid = uECC_verify(signature_verification_key_start, + digest, sizeof(digest), sigblock->signature, + uECC_secp256r1()); + + bootloader_unmap(data); + return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID; + + unmap_and_fail: + bootloader_unmap(data); + return ESP_FAIL; +} diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index dfb9dfc4c2..5807512b86 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -18,6 +18,7 @@ ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) # Supporting esptool command line tools ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py +export ESPSECUREPY # is used in bootloader_support component ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) @@ -33,6 +34,11 @@ ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifndef IS_BOOTLOADER_BUILD + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +endif +endif flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 95dae1651e..68ed7c7a4e 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 95dae1651e5aea1adb2b6018b23f65a305f67387 +Subproject commit 68ed7c7a4e4409899f10dddda1e02b20e5cb32f0 diff --git a/components/micro-ecc/component.mk b/components/micro-ecc/component.mk new file mode 100644 index 0000000000..df29f400d4 --- /dev/null +++ b/components/micro-ecc/component.mk @@ -0,0 +1,8 @@ +# only compile the micro-ecc/uECC.c source file +# (SRCDIRS is needed so build system can find the source file) +COMPONENT_SRCDIRS := micro-ecc +COMPONENT_OBJS := micro-ecc/uECC.o + +COMPONENT_ADD_INCLUDEDIRS := micro-ecc + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/micro-ecc/micro-ecc b/components/micro-ecc/micro-ecc new file mode 160000 index 0000000000..14222e062d --- /dev/null +++ b/components/micro-ecc/micro-ecc @@ -0,0 +1 @@ +Subproject commit 14222e062d77f45321676e813d9525f32a88e8fa diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 98631cc854..e6f3bce8f9 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -21,6 +21,9 @@ PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.cs $(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." $(Q) $(GEN_ESP32PART) $< $@ +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +endif all_binaries: $(PARTITION_TABLE_BIN) diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 5e33367b49..169f2d56bc 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -8,9 +8,9 @@ Secure Boot is separate from the Encrypted Flash feature, and you can use secure Background ---------- -- Most data is stored in flash. Flash access does not need to protected for secure boot to function, because critical data is stored in Efuses internal to the chip. +- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored in Efuses internal to the chip. -- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. +- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. - To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. @@ -21,57 +21,62 @@ This is a high level overview of the secure boot process. Step by step instructi 1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". -2. Bootloader Config includes the path to a ECDSA public key. This "image public key" is compiled into the software bootloader. +2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. -2. The software bootloader image is built by esp-idf with the public key embedded, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. +2. The software bootloader image is built by esp-idf with the public key (signature verification) portion of the secure boot signing key compiled in, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: - - Hardware secure boot support generates a device secure bootloader key (stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. - - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot this bootloader image.) + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - Bootloader also disables JTAG via efuse. -4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the image public key (embedded in the bootloader itself) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key's public key (embedded in the bootloader itself, and therefore validated as part of the bootloader digest) to verify all subsequent partition tables and app images before they are booted. Keys ---- The following keys are used by the secure boot process: -- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from hardware, the user does not need to supply it. The Efuse holding this key must be read & write protected (preventing software access) before secure boot is enabled. +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from a hardware random number generation, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. -- "image public key" is a ECDSA public key (curve TBD) in format TBD. This public key is flashed into the software bootloader and used to verify the remaining parts of the flash (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. +- "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. + + - The public key from this key pair (for signature verificaton but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. + + - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to a bootloader with secure boot using the matching public key. -- "image private key" is a ECDSA private key, a matching pair with "image public key". This private key is used to sign partition tables and app images for the secure boot process. **This private key must be securely kept private** as anyone who has this key can authenticate to the secure boot process. It is acceptable to use the same private key across multiple production devices. How To Enable Secure Boot ------------------------- -1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (For the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) +1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) -2. Select names for public & private image key files "Image Public Key File" and "Image Private Key File". These options will appear after secure boot is enabled. The files can be anywhere on your system. Relative paths are evaluated from the project directory. The files don't need to exist yet. +2. Select a name for the secure boot signing key. This option will appear after secure boot is enabled. The file can be anywhere on your system. A relative path will be evaluated from the project directory. The file does not need to exist yet. 3. Set other config options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration -4. Run ``make generate_image_keypair`` to generate an image public key and a matching image private key. +4. The first time you run ``make``, if the signing key is not found then an error message will be printed with a command to generate a signing key via ``espsecure.py generate_signing_key``. - **IMPORTANT** The key pair is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + **IMPORTANT** A signing key genereated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + + **IMPORTANT** For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See `Generating Secure Boot Signing Key` for more details. 5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of `make` will include a prompt for a flashing command, using `esptool.py write_flash`. -6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not automated) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. +6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not performed by make) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. -7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the private key you generated in step 4. +7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the signing key you generated in step 4. *NOTE*: `make flash` doesn't flash the bootloader if secure boot is enabled. 8. Reset the ESP32 and it will boot the software bootloader you flashed. The software bootloader will enable secure boot on the chip, and then it verifies the app image signature and boots the app. You should watch the serial console output from the ESP32 to verify that secure boot is enabled and no errors have occured due to the build configuration. -**IMPORTANT** Secure boot won't ever be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. +*NOTE* Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the image public key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the signing key). Re-Flashable Software Bootloader -------------------------------- @@ -80,19 +85,34 @@ The "Secure Boot: One-Time Flash" is the recommended software bootloader configu However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. -*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. +In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 value is used to generate an 256-bit value which is then burned to efuse and used to protect the bootloader. This is a convenience so you only need to generate/protect a single private key. + +*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments. + +To enable a reflashable bootloader: 1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable". -2. Select a name for the "Secure bootloader key file". The file can be anywhere on your system, and does not have to exist yet. A path is evaluated relative to the project directory. The file doesn't have to exist yet. +2. Follow the steps shown above to choose a signing key file, and generate the key file. -3. The first time you run ``make bootloader``, the system will prompt you with a ``espsecure.py generate_key`` command that can be used to generate the secure bootloader key. +3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key you generated for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the derived key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the derived key). - **IMPORTANT** The new key is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the secure bootloader key will be weak. +4. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. -4. Run ``make bootloader`` again. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the secure bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the secure bootloader key file). +Generating Secure Boot Signing Key +---------------------------------- -5. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. +The build system will prompt you with a command to generate a new signing key via ``espsecure.py generate_signing_key``. This uses the python-ecdsa library, which in turn uses Python's os.urandom() as a random number source. + +The strength of the signing key is proportional to (a) the random number source of the system, and (b) the correctness of the algorithm used. For production devices, we recommend generating signing keys from a system with a quality entropy source, and using the best available EC key generation utilities. + +For example, to generate a signing key using the openssl command line: + +``` +openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem +``` + +Remember that the strength of the secure boot system depends on keeping the signing key private. Technical Details @@ -107,9 +127,9 @@ The Secure Boot support hardware can perform three basic operations: 1. Generate a random sequence of bytes from a hardware random number generator. -2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read from hardware if Efuse ABS_DONE_0 is *not* burned (ie still 0), to prevent new digests from being calculated on the device after secure boot is enabled. +2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read back by software if Efuse ABS_DONE_0 is *not* burned (ie still 0). -3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. +3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. Secure Bootloader Digest Algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -122,9 +142,9 @@ Items marked with (^) are to fulfill hardware restrictions, as opposed to crypto 1. Prefix the image with a 128 byte randomly generated IV. 2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) -3. For each 16 byte block of the input image: - - Reverse the byte order of the block (^) - - Use the AES256 algorithm in ECB mode to encrypt the block. +3. For each 16 byte plaintext block of the input image: + - Reverse the byte order of the plaintext block (^) + - Apply AES256 in ECB mode to the plaintext block. - Reverse the byte order of the 16 bytes of ciphertext output. (^) - Append to the overall ciphertext output. 4. Byte-swap each 4 byte word of the ciphertext (^) @@ -137,9 +157,10 @@ Image Signing Algorithm Deterministic ECDSA as specified by `RFC6979`. -Curve is TBD. -Key format is TBD. -Output format is TBD. +- Curve is NIST256p (openssl calls this curve "prime256v1", it is also sometimes called secp256r1). +- Key format used for storage is PEM. + - In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes. +- Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. .. _esp-idf boot process: ../boot-process.rst diff --git a/make/common.mk b/make/common.mk index 2b7376a4db..0c523c8775 100644 --- a/make/common.mk +++ b/make/common.mk @@ -53,8 +53,26 @@ endef # convenience variable for printing an 80 asterisk wide separator line SEPARATOR:="*******************************************************************************" -# macro to remove quotes from an argument, ie $(call dequote (CONFIG_BLAH)) +# macro to remove quotes from an argument, ie $(call dequote,$(CONFIG_BLAH)) define dequote $(subst ",,$(1)) endef # " comment kept here to keep syntax highlighting happy + + +# macro to keep an absolute path as-is, but resolve a relative path +# against a particular parent directory +# +# $(1) path to resolve +# $(2) directory to resolve non-absolute path against +# +# Path and directory don't have to exist (definition of a "relative +# path" is one that doesn't start with /) +# +# $(2) can contain a trailing forward slash or not, result will not +# double any path slashes. +# +# example $(call resolvepath,$(CONFIG_PATH),$(CONFIG_DIR)) +define resolvepath +$(if $(filter /%,$(1)),$(1),$(subst //,/,$(2)/$(1))) +endef diff --git a/make/component_common.mk b/make/component_common.mk index 58711b7c6e..70a6f60f07 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -134,10 +134,10 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect # because objcopy generates the symbol name from the full command line # path to the input file. define GenerateEmbedTarget -$(1).$(2).o: $$(COMPONENT_PATH)/$(1) | $$(dir $(1)) +$(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) $$(summary) EMBED $$@ - $$(Q) cp $$< $$(notdir $$<) - $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) + $$(Q) $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir + $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ $$(Q) rm $$(notdir $$<) endef diff --git a/make/project.mk b/make/project.mk index 7e8dc46d54..5a12732a4e 100644 --- a/make/project.mk +++ b/make/project.mk @@ -151,6 +151,7 @@ LDFLAGS ?= -nostdlib \ -L$(IDF_PATH)/ld \ $(addprefix -L$(BUILD_DIR_BASE)/,$(COMPONENTS) $(SRCDIRS)) \ -u call_user_start_cpu0 \ + $(EXTRA_LDFLAGS) \ -Wl,--gc-sections \ -Wl,-static \ -Wl,--start-group \ From 64f3893cb9c7a4c792f4ecbb7c4375f25bf768f7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 4 Nov 2016 16:05:00 +1100 Subject: [PATCH 190/285] secure boot: Derive secure bootloader key from private key Means only one key needs to be managed. --- components/bootloader/Kconfig.projbuild | 23 +++-------------- components/bootloader/Makefile.projbuild | 25 ++++++++----------- .../bootloader_support/Makefile.projbuild | 3 --- components/bootloader_support/component.mk | 8 ++++-- components/esptool_py/esptool | 2 +- make/project.mk | 5 +++- make/project_config.mk | 1 - 7 files changed, 25 insertions(+), 42 deletions(-) delete mode 100644 components/bootloader_support/Makefile.projbuild diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index b568f61a04..536b971268 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -54,29 +54,14 @@ config SECURE_BOOTLOADER_ONE_TIME_FLASH config SECURE_BOOTLOADER_REFLASHABLE bool "Reflashable" help - Generate the bootloader digest key on the computer instead of inside - the chip. Allows the secure bootloader to be re-flashed by using the - same key. + Generate a reusable secure bootloader key, derived (via SHA-256) from the secure boot signing key. - This option is less secure than one-time flash, because a leak of the digest key allows reflashing of any device that uses it. + This allows the secure bootloader to be re-flashed by anyone with access to the secure boot signing key. + + This option is less secure than one-time flash, because a leak of the digest key from one device allows reflashing of any device that uses it. endchoice -config SECURE_BOOTLOADER_KEY_FILE - string "Secure bootloader key file" - depends on SECURE_BOOTLOADER_REFLASHABLE - default secure_boot_key.bin - help - Path to the key file for a reflashable secure bootloader digest. - File must contain 32 randomly generated bytes. - - Path is evaluated relative to the project directory. - - You can generate a new key by running the following command: - espsecure.py generate_key secure_boot_key.bin - - See docs/security/secure-boot.rst for details. - config SECURE_BOOT_SIGNING_KEY string "Secure boot signing key" depends on SECURE_BOOTLOADER_ENABLED diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 831d988584..1b1c07bea3 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,8 +15,7 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig -# both signing key paths are resolved relative to the project directory -SECURE_BOOTLOADER_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +# signing key path is resolved relative to the project directory SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY))) export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component @@ -31,10 +30,6 @@ BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ $(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig $(Q) $(BOOTLOADER_MAKE) $@ -bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean config-clean - $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old - clean: bootloader-clean ifdef CONFIG_SECURE_BOOTLOADER_DISABLED @@ -66,7 +61,11 @@ else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) -BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin +BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin +SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin + +$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY) + $(Q) $(ESPSECUREPY) digest_private_key -k $< $@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo $(SEPARATOR) @@ -84,20 +83,16 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) @echo "DIGEST $(notdir $@)" $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< -$(SECURE_BOOTLOADER_KEY): - @echo $(SEPARATOR) - @echo "Need to generate secure boot signing key. Run following command:" - @echo "$(ESPSECUREPY) generate_key $@" - @echo "Keep key file safe after generating." - @echo "(See secure boot documentation for caveats & alternatives.)") - @exit 1 - else bootloader: @echo "Invalid bootloader target: bad sdkconfig?" @exit 1 endif +bootloader-clean: + $(Q) $(BOOTLOADER_MAKE) app-clean config-clean + $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old $(SECURE_BOOTLOADER_KEY) $(BOOTLOADER_DIGEST_BIN) + all_binaries: $(BOOTLOADER_BIN) # synchronise the project level config to the bootloader's diff --git a/components/bootloader_support/Makefile.projbuild b/components/bootloader_support/Makefile.projbuild deleted file mode 100644 index f93967a719..0000000000 --- a/components/bootloader_support/Makefile.projbuild +++ /dev/null @@ -1,3 +0,0 @@ -# projbuild file for bootloader support -# (included in bootloader & main app) - diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index 7f546dcba9..e68949d82a 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -17,10 +17,9 @@ COMPONENT_SRCDIRS := src # ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +# this path is created relative to the component build directory SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) -COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) - $(SECURE_BOOT_SIGNING_KEY): @echo "Need to generate secure boot signing key." @echo "One way is to run this command:" @@ -31,6 +30,11 @@ $(SECURE_BOOT_SIGNING_KEY): $(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) $(ESPSECUREPY) extract_public_key --keyfile $< $@ + +COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY) + +COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) + endif include $(IDF_PATH)/make/component_common.mk diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 68ed7c7a4e..98e5dbfa78 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 68ed7c7a4e4409899f10dddda1e02b20e5cb32f0 +Subproject commit 98e5dbfa78fa53cebcb4c56530e683f889bf21c3 diff --git a/make/project.mk b/make/project.mk index 5a12732a4e..e0d578c104 100644 --- a/make/project.mk +++ b/make/project.mk @@ -306,6 +306,9 @@ app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) $(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) -clean: app-clean +# NB: this ordering is deliberate (app-clean before config-clean), +# so config remains valid during all component clean targets +config-clean: app-clean +clean: config-clean diff --git a/make/project_config.mk b/make/project_config.mk index 7ca83ce5af..56a05090bc 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -59,7 +59,6 @@ $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(SDKCONFIG) $( # sometimes you can get an infinite make loop on Windows where sdkconfig always gets regenerated newer # than the target(!) -clean: config-clean .PHONY: config-clean config-clean: $(summary RM CONFIG) From 7402a1b9738a7542e6e37de8768c9580a196aee0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 14:35:23 +1100 Subject: [PATCH 191/285] partition_table: Move from 0x4000 to 0x8000 Also fix a bug with correctly padding bootloader image when length is already a multiple of 16. --- components/bootloader_support/src/bootloader_flash.c | 4 ++-- components/bootloader_support/src/esp_image_format.c | 8 +++++++- components/esp32/include/esp_flash_data_types.h | 2 +- components/partition_table/Makefile.projbuild | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index a50cd157e9..fcaf24ad2c 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -94,11 +94,11 @@ void bootloader_unmap(const void *mapping) esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) { if(src_addr & 3) { - ESP_LOGE(TAG, "bootloader_flash_read src_addr not 4-byte aligned"); + ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr); return ESP_FAIL; } if((intptr_t)dest & 3) { - ESP_LOGE(TAG, "bootloader_flash_read dest not 4-byte aligned"); + ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest); return ESP_FAIL; } diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index ad3cd33f13..3e7dcafa9e 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -62,6 +62,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const } for(int i = 0; i <= index && err == ESP_OK; i++) { + ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr); err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t)); if (err == ESP_OK) { if ((segment_header->data_len & 3) != 0 @@ -69,6 +70,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); err = ESP_ERR_IMAGE_INVALID; } + ESP_LOGV(TAG, "segment data length 0x%x", segment_header->data_len); next_addr += sizeof(esp_image_segment_header_t); *segment_data_offset = next_addr; next_addr += segment_header->data_len; @@ -140,7 +142,11 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) } /* image padded to next full 16 byte block, with checksum byte at very end */ - length += 15 - (length % 16); + ESP_LOGV(TAG, "unpadded image length 0x%x", length); + length += 16; /* always pad by at least 1 byte */ + length = length - (length % 16); + ESP_LOGV(TAG, "padded image length 0x%x", length); + ESP_LOGD(TAG, "reading checksum block at 0x%x", src_addr + length - 16); bootloader_flash_read(src_addr + length - 16, buf, 16); if (checksum != buf[15]) { ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index ce4acb7bc9..783f2c59bb 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -21,7 +21,7 @@ extern "C" { #endif -#define ESP_PARTITION_TABLE_ADDR 0x4000 +#define ESP_PARTITION_TABLE_ADDR 0x8000 #define ESP_PARTITION_MAGIC 0x50AA /* OTA selection structure (two copies in the OTA data partition.) diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index e6f3bce8f9..4273f53c3a 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -11,6 +11,8 @@ # NB: gen_esp32part.py lives in the sdk/bin/ dir not component dir GEN_ESP32PART := $(PYTHON) $(COMPONENT_PATH)/gen_esp32part.py -q +PARTITION_TABLE_OFFSET := 0x8000 + # Path to partition CSV file is relative to project path for custom # partition CSV files, but relative to component dir otherwise.$ PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(COMPONENT_PATH))) @@ -27,8 +29,8 @@ endif all_binaries: $(PARTITION_TABLE_BIN) -PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash 0x4000 $(PARTITION_TABLE_BIN) -ESPTOOL_ALL_FLASH_ARGS += 0x4000 $(PARTITION_TABLE_BIN) +PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) partition_table: $(PARTITION_TABLE_BIN) @echo "Partition table binary generated. Contents:" From ff1b2c603911f58f3602fe8d9f7cf6c1cddff63b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:32:21 +1100 Subject: [PATCH 192/285] partition_table: Pad generated table to 0xC00 length, for easier signing --- components/partition_table/gen_esp32part.py | 8 +++++++- .../partition_table/tests/gen_esp32part_tests.py | 15 ++++++++++----- docs/partition-tables.rst | 5 +++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index 8b5df2b3b5..b399f31b78 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -9,6 +9,8 @@ import struct import argparse import sys +MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature + __version__ = '1.0' quiet = False @@ -92,7 +94,11 @@ class PartitionTable(list): return result def to_binary(self): - return "".join(e.to_binary() for e in self) + result = "".join(e.to_binary() for e in self) + if len(result )>= MAX_PARTITION_LENGTH: + raise InputError("Binary partition table length (%d) longer than max" % len(result)) + result += "\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing + return result def to_csv(self, simple_formatting=False): rows = [ "# Espressif ESP32 Partition Table", diff --git a/components/partition_table/tests/gen_esp32part_tests.py b/components/partition_table/tests/gen_esp32part_tests.py index 413f1aac91..d12539ea87 100755 --- a/components/partition_table/tests/gen_esp32part_tests.py +++ b/components/partition_table/tests/gen_esp32part_tests.py @@ -37,6 +37,10 @@ LONGER_BINARY_TABLE += "\xAA\x50\x10\x00" + \ "second" + ("\0"*10) + \ "\x00\x00\x00\x00" +def _strip_trailing_ffs(binary_table): + while binary_table.endswith("\xFF"): + binary_table = binary_table[0:len(binary_table)-1] + return binary_table class CSVParserTests(unittest.TestCase): @@ -156,7 +160,7 @@ class BinaryOutputTests(unittest.TestCase): first, 0x30, 0xEE, 0x100400, 0x300000 """ t = PartitionTable.from_csv(csv) - tb = t.to_binary() + tb = _strip_trailing_ffs(t.to_binary()) self.assertEqual(len(tb), 32) self.assertEqual('\xAA\x50', tb[0:2]) # magic self.assertEqual('\x30\xee', tb[2:4]) # type, subtype @@ -170,7 +174,7 @@ first, 0x30, 0xEE, 0x100400, 0x300000 second,0x31, 0xEF, , 0x100000 """ t = PartitionTable.from_csv(csv) - tb = t.to_binary() + tb = _strip_trailing_ffs(t.to_binary()) self.assertEqual(len(tb), 64) self.assertEqual('\xAA\x50', tb[0:2]) self.assertEqual('\xAA\x50', tb[32:34]) @@ -215,7 +219,7 @@ class BinaryParserTests(unittest.TestCase): self.assertEqual(t[2].type, 0x10) self.assertEqual(t[2].name, "second") - round_trip = t.to_binary() + round_trip = _strip_trailing_ffs(t.to_binary()) self.assertEqual(round_trip, LONGER_BINARY_TABLE) def test_bad_magic(self): @@ -267,7 +271,7 @@ class CSVOutputTests(unittest.TestCase): self.assertEqual(row[0], "factory") self.assertEqual(row[1], "app") self.assertEqual(row[2], "2") - self.assertEqual(row[3], "64K") + self.assertEqual(row[3], "0x10000") self.assertEqual(row[4], "1M") # round trip back to a PartitionTable and check is identical @@ -291,7 +295,7 @@ class CommandLineTests(unittest.TestCase): # reopen the CSV and check the generated binary is identical with open(csvpath, 'r') as f: from_csv = PartitionTable.from_csv(f.read()) - self.assertEqual(from_csv.to_binary(), LONGER_BINARY_TABLE) + self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE) # run gen_esp32part.py to conver the CSV to binary again subprocess.check_call([sys.executable, "../gen_esp32part.py", @@ -299,6 +303,7 @@ class CommandLineTests(unittest.TestCase): # assert that file reads back as identical with open(binpath, 'rb') as f: binary_readback = f.read() + binary_readback = _strip_trailing_ffs(binary_readback) self.assertEqual(binary_readback, LONGER_BINARY_TABLE) finally: diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index 88597532d2..5f5911bd52 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -6,6 +6,8 @@ Overview A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash. +Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data. + Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded. The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables: @@ -130,3 +132,6 @@ Flashing the partition table * ``make flash``: Will flash everything including the partition table. A manual flashing command is also printed as part of ``make partition_table``. + + +.. _secure boot: security/secure-boot.rst From fe66dd85f09881c4ab8c48e37cda715f065fccfe Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:45:26 +1100 Subject: [PATCH 193/285] secure boot: Enable based on sdkconfig, remove "secure boot flag" from binary image --- components/bootloader/Kconfig.projbuild | 35 ++++++++++++++++++- .../bootloader/src/main/bootloader_start.c | 24 +++++++------ .../include/esp_image_format.h | 3 +- .../include/esp_secure_boot.h | 2 +- .../bootloader_support/src/secure_boot.c | 26 ++++++++++++-- 5 files changed, 73 insertions(+), 17 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 536b971268..949638594d 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -28,6 +28,12 @@ config LOG_BOOTLOADER_LEVEL default 4 if LOG_BOOTLOADER_LEVEL_DEBUG default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE +endmenu + + + +menu "Secure boot configuration" + choice SECURE_BOOTLOADER bool "Secure bootloader" default SECURE_BOOTLOADER_DISABLED @@ -78,8 +84,35 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. +config SECURE_BOOT_DISABLE_JTAG + bool "First boot: Permanently disable JTAG" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. + + It is recommended this option remains set for production environments. + +config SECURE_BOOT_DISABLE_UART_BOOTLOADER + bool "First boot: Permanently disable UART bootloader" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disables UART and other bootloader modes when enabling secure boot. This happens on first boot. + + It is recommended this option remains set for production environments. + +config SECURE_BOOT_TEST_MODE + bool "Test mode: don't actually enable secure boot" + depends on SECURE_BOOTLOADER_ENABLED + default N + help + If this option is set, all permanent secure boot changes (via Efuse) are disabled. + + This option is for testing purposes only - it effectively completely disables secure boot protection. + config SECURE_BOOTLOADER_ENABLED bool default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE -endmenu +endmenu \ No newline at end of file diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 3ebb8cf520..6c23257be5 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -316,17 +316,17 @@ void bootloader_main() ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); - if(fhdr.secure_boot_flag == 0x01) { - /* Generate secure digest from this bootloader to protect future - modifications */ - err = esp_secure_boot_permanently_enable(); - if (err != ESP_OK){ - ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); - /* Allow booting to continue, as the failure is probably - due to user-configured EFUSEs for testing... - */ - } +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + /* Generate secure digest from this bootloader to protect future + modifications */ + err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ } +#endif if(fhdr.encrypt_flag == 0x01) { /* encrypt flash */ @@ -354,12 +354,16 @@ static void unpack_load_app(const esp_partition_pos_t* partition) ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); return; } +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); err = esp_secure_boot_verify_signature(partition->offset, image_length); if (err != ESP_OK) { ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err); return; } + ESP_LOGD(TAG, "App signature is valid"); } +#endif if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index a8b1739d73..326ff130d5 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -64,8 +64,7 @@ typedef struct { uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ uint32_t entry_addr; uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secure_boot_flag; /* secure boot flag */ - uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ + uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */ } esp_image_header_t; /* Header of binary image segment */ diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index c1c0171512..eaa1048628 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -32,7 +32,7 @@ * @return true if secure boot is enabled. */ static inline bool esp_secure_boot_enabled(void) { - return REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_RD_ABS_DONE_0); + return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0; } diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index c17aebfbea..2cfe505492 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -110,12 +110,16 @@ static bool secure_boot_generate(uint32_t image_len){ /* Burn values written to the efuse write registers */ static inline void burn_efuses() { +#ifdef CONFIG_SECURE_BOOT_TEST_MODE + ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses!"); +#else REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ +#endif } esp_err_t esp_secure_boot_permanently_enable(void) { @@ -185,10 +189,22 @@ esp_err_t esp_secure_boot_permanently_enable(void) { return ESP_ERR_INVALID_STATE; } - ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); + ESP_LOGI(TAG, "blowing secure boot efuse..."); ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); - REG_WRITE(EFUSE_BLK0_WDATA6_REG, - EFUSE_RD_ABS_DONE_0 | EFUSE_RD_DISABLE_JTAG); + + uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_0; + + #ifdef CONFIG_SECURE_BOOT_DISABLE_JTAG + ESP_LOGI(TAG, "disabling JTAG..."); + new_wdata6 |= EFUSE_RD_DISABLE_JTAG; + #endif + + #ifdef CONFIG_SECURE_BOOT_DISABLE_UART_BOOTLOADER + ESP_LOGI(TAG, "disabling UART bootloader..."); + new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE_S; + #endif + + REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6); burn_efuses(); uint32_t after = REG_READ(EFUSE_BLK0_RDATA6_REG); ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after); @@ -196,7 +212,11 @@ esp_err_t esp_secure_boot_permanently_enable(void) { ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); return ESP_OK; } else { +#ifdef CONFIG_SECURE_BOOT_TEST_MODE + ESP_LOGE(TAG, "secure boot not enabled due to test mode"); +#else ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); +#endif return ESP_ERR_INVALID_STATE; } } From e459f803da9cc3bd6640c9d56215ebeb63c27946 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:45:57 +1100 Subject: [PATCH 194/285] secure boot: Functional partition table & app signature verification --- .../bootloader/src/main/bootloader_start.c | 54 ++++++++++--------- .../include/esp_image_format.h | 2 +- .../include/esp_secure_boot.h | 4 +- .../include_priv/bootloader_flash.h | 8 +-- .../bootloader_support/src/bootloader_flash.c | 6 +-- .../bootloader_support/src/esp_image_format.c | 4 +- .../bootloader_support/src/secure_boot.c | 2 +- .../src/secure_boot_signatures.c | 30 +++++++---- components/esptool_py/Makefile.projbuild | 17 +++--- components/esptool_py/esptool | 2 +- components/partition_table/Makefile.projbuild | 15 ++++-- components/partition_table/gen_esp32part.py | 9 ++-- docs/security/secure-boot.rst | 39 ++++++++------ make/common.mk | 3 +- 14 files changed, 116 insertions(+), 79 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 6c23257be5..36d3ff0d29 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -104,35 +104,38 @@ void IRAM_ATTR call_start_cpu0() * OTA info sector, factory app sector, and test app sector. * * @inputs: bs bootloader state structure used to save the data - * addr address of partition table in flash * @return: return true, if the partition table is loaded (and MD5 checksum is valid) * */ -bool load_partition_table(bootloader_state_t* bs, uint32_t addr) +bool load_partition_table(bootloader_state_t* bs) { esp_err_t err; const esp_partition_info_t *partitions; - const int PARTITION_TABLE_SIZE = 0x1000; - const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); + const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ + const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); char *partition_usage; ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED if(esp_secure_boot_enabled()) { - err = esp_secure_boot_verify_signature(addr, 0x1000); + ESP_LOGI(TAG, "Verifying partition table signature..."); + err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; } + ESP_LOGD(TAG, "Partition table signature verified"); } +#endif - partitions = bootloader_mmap(addr, 0x1000); + partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (!partitions) { - ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); return false; } - ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", addr, (intptr_t)partitions); + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); for(int i = 0; i < MAX_PARTITIONS; i++) { const esp_partition_info_t *partition = &partitions[i]; @@ -197,7 +200,7 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) partition->pos.offset, partition->pos.size); } - bootloader_unmap(partitions); + bootloader_munmap(partitions); ESP_LOGI(TAG,"End of partition table"); return true; @@ -248,7 +251,7 @@ void bootloader_main() update_flash_config(&fhdr); - if (!load_partition_table(&bs, ESP_PARTITION_TABLE_ADDR)) { + if (!load_partition_table(&bs)) { ESP_LOGE(TAG, "load partition table error!"); return; } @@ -268,7 +271,7 @@ void bootloader_main() } sa = ota_select_map[0]; sb = ota_select_map[1]; - bootloader_unmap(ota_select_map); + bootloader_munmap(ota_select_map); if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { // init status flash @@ -336,7 +339,7 @@ void bootloader_main() } } - // copy sections to RAM, set up caches, and start application + // copy loaded segments to RAM, set up caches for mapped segments, and start application unpack_load_app(&load_part_pos); } @@ -347,14 +350,15 @@ static void unpack_load_app(const esp_partition_pos_t* partition) esp_image_header_t image_header; uint32_t image_length; - if (esp_secure_boot_enabled()) { - /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ - err = esp_image_basic_verify(partition->offset, &image_length); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); - return; - } + /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ + err = esp_image_basic_verify(partition->offset, &image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); + return; + } + #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + if (esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); err = esp_secure_boot_verify_signature(partition->offset, image_length); if (err != ESP_OK) { @@ -377,7 +381,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) uint32_t irom_load_addr = 0; uint32_t irom_size = 0; - /* Reload the RTC memory sections whenever a non-deepsleep reset + /* Reload the RTC memory segments whenever a non-deepsleep reset is occurring */ bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; @@ -409,7 +413,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= DROM_LOW && address < DROM_HIGH) { - ESP_LOGD(TAG, "found drom section, map from %08x to %08x", data_offs, + ESP_LOGD(TAG, "found drom segment, map from %08x to %08x", data_offs, segment_header.load_addr); drom_addr = data_offs; drom_load_addr = segment_header.load_addr; @@ -419,7 +423,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= IROM_LOW && address < IROM_HIGH) { - ESP_LOGD(TAG, "found irom section, map from %08x to %08x", data_offs, + ESP_LOGD(TAG, "found irom segment, map from %08x to %08x", data_offs, segment_header.load_addr); irom_addr = data_offs; irom_load_addr = segment_header.load_addr; @@ -429,12 +433,12 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", data_offs); + ESP_LOGD(TAG, "Skipping RTC code segment at %08x\n", data_offs); load = false; } if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) { - ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", data_offs); + ESP_LOGD(TAG, "Skipping RTC data segment at %08x\n", data_offs); load = false; } @@ -449,7 +453,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) return; } memcpy((void *)segment_header.load_addr, data, segment_header.data_len); - bootloader_unmap(data); + bootloader_munmap(data); } } diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index 326ff130d5..a32a50a4a5 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -107,7 +107,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const * * Image validation checks: * - Magic byte - * - No single section longer than 16MB + * - No single segment longer than 16MB * - Total image no longer than 16MB * - 8 bit image checksum is valid * diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index eaa1048628..f9fc57708d 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -60,7 +60,9 @@ static inline bool esp_secure_boot_enabled(void) { */ esp_err_t esp_secure_boot_permanently_enable(void); -/** @brief Verify the signature appended to some binary data in flash. +/** @brief Verify the secure boot signature (determinstic ECDSA w/ SHA256) appended to some binary data in flash. + * + * Public key is compiled into the calling program. See docs/security/secure-boot.rst for details. * * @param src_addr Starting offset of the data in flash. * @param length Length of data in bytes. Signature is appended -after- length bytes. diff --git a/components/bootloader_support/include_priv/bootloader_flash.h b/components/bootloader_support/include_priv/bootloader_flash.h index d70ec22d56..769c47b904 100644 --- a/components/bootloader_support/include_priv/bootloader_flash.h +++ b/components/bootloader_support/include_priv/bootloader_flash.h @@ -29,11 +29,11 @@ /** * @brief Map a region of flash to data memory * - * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_unmapped before another region is mapped. + * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_munmapped before another region is mapped. * * @important In app code, these functions are not thread safe. * - * Call bootloader_unmap once for each successful call to bootloader_mmap. + * Call bootloader_munmap once for each successful call to bootloader_mmap. * * In esp-idf app, this function maps directly to spi_flash_mmap. * @@ -49,9 +49,9 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size); /** * @brief Unmap a previously mapped region of flash * - * Call bootloader_unmap once for each successful call to bootloader_mmap. + * Call bootloader_munmap once for each successful call to bootloader_mmap. */ -void bootloader_unmap(const void *mapping); +void bootloader_munmap(const void *mapping); /** * @brief Read data from Flash. diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index fcaf24ad2c..3fd107dea8 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -38,7 +38,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) return result; } -void bootloader_unmap(const void *mapping) +void bootloader_munmap(const void *mapping) { if(mapping && map) { spi_flash_munmap(map); @@ -68,7 +68,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) } uint32_t src_addr_aligned = src_addr & 0xffff0000; - uint32_t count = (size + 0xffff) / 0x10000; + uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / 0x10000; Cache_Read_Disable(0); Cache_Flush(0); ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count ); @@ -80,7 +80,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) return (void *)(0x3f400000 + (src_addr - src_addr_aligned)); } -void bootloader_unmap(const void *mapping) +void bootloader_munmap(const void *mapping) { if (mapped) { /* Full MMU reset */ diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 3e7dcafa9e..42a3e4ffe2 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -70,8 +70,8 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); err = ESP_ERR_IMAGE_INVALID; } - ESP_LOGV(TAG, "segment data length 0x%x", segment_header->data_len); next_addr += sizeof(esp_image_segment_header_t); + ESP_LOGV(TAG, "segment data length 0x%x data starts 0x%x", segment_header->data_len, next_addr); *segment_data_offset = next_addr; next_addr += segment_header->data_len; } @@ -124,7 +124,7 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) for(int i = 0; i < segment_header.data_len; i++) { checksum ^= segment_data[i]; } - bootloader_unmap(segment_data); + bootloader_munmap(segment_data); } /* End of image, verify checksum */ diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index 2cfe505492..d908d39c38 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -90,7 +90,7 @@ static bool secure_boot_generate(uint32_t image_len){ for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) { ets_secure_boot_hash(image + i/sizeof(void *)); } - bootloader_unmap(image); + bootloader_munmap(image); ets_secure_boot_obtain(); ets_secure_boot_rd_abstract(buf); diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 5f02de38b0..5106eb396f 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -43,9 +43,10 @@ extern const uint8_t signature_verification_key_end[] asm("_binary_signature_ver esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) { sha_context sha; - uint8_t digest[64]; + uint8_t digest[32]; ptrdiff_t keylen; - const uint8_t *data; + const uint8_t *data, *digest_data; + uint32_t digest_len, chunk_len; const signature_block_t *sigblock; bool is_valid; @@ -68,16 +69,23 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) /* Use ROM SHA functions directly */ ets_sha_enable(); ets_sha_init(&sha); - ets_sha_update(&sha, SHA2_512, data, length); - ets_sha_finish(&sha, SHA2_512, digest); + digest_len = length * 8; + digest_data = data; + while (digest_len > 0) { + uint32_t chunk_len = (digest_len > 64) ? 64 : digest_len; + ets_sha_update(&sha, SHA2_256, digest_data, chunk_len); + digest_len -= chunk_len; + digest_data += chunk_len / 8; + } + ets_sha_finish(&sha, SHA2_256, digest); ets_sha_disable(); #else /* Use thread-safe esp-idf SHA layer */ - esp_sha512_init(&sha); - esp_sha512_start(&sha, false); - esp_sha512_update(&sha, data, length); - esp_sha512_finish(&sha, digest); - esp_sha512_free(&sha); + esp_sha256_init(&sha); + esp_sha256_start(&sha, false); + esp_sha256_update(&sha, data, length); + esp_sha256_finish(&sha, digest); + esp_sha256_free(&sha); #endif keylen = signature_verification_key_end - signature_verification_key_start; @@ -90,10 +98,10 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) digest, sizeof(digest), sigblock->signature, uECC_secp256r1()); - bootloader_unmap(data); + bootloader_munmap(data); return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID; unmap_and_fail: - bootloader_unmap(data); + bootloader_munmap(data); return ESP_FAIL; } diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 5807512b86..bfbece45e6 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -24,21 +24,24 @@ ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFRE ESPTOOL_ELF2IMAGE_OPTIONS := -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED -ESPTOOL_ELF2IMAGE_OPTIONS += "--set-secure-boot-flag" -endif - ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) -$(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< ifdef CONFIG_SECURE_BOOTLOADER_ENABLED ifndef IS_BOOTLOADER_BUILD - $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +# for secure boot, add a signing step to get from unsiged app to signed app +APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin) + +$(APP_BIN): $(APP_BIN_UNSIGNED) + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $^ # signed in-place endif endif +# non-secure boot (or bootloader), both these files are the same +APP_BIN_UNSIGNED ?= $(APP_BIN) + +$(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) + $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 98e5dbfa78..d8b1ffdd50 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 98e5dbfa78fa53cebcb4c56530e683f889bf21c3 +Subproject commit d8b1ffdd500bd80a35fb66b64e2733195b39e519 diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 4273f53c3a..f8b822ef2c 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -20,12 +20,19 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) -$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin) +# add an extra signing step for secure partition table +$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $< +else +# secure bootloader disabled, both files are the same +PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN) +endif + +$(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFILE) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." $(Q) $(GEN_ESP32PART) $< $@ -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED - $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place -endif all_binaries: $(PARTITION_TABLE_BIN) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index b399f31b78..cb6a5f24a1 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -86,11 +86,14 @@ class PartitionTable(list): @classmethod def from_binary(cls, b): - if len(b) % 32 != 0: - raise InputError("Partition table length must be a multiple of 32 bytes. Got %d bytes." % len(b)) result = cls() for o in range(0,len(b),32): - result.append(PartitionDefinition.from_binary(b[o:o+32])) + data = b[o:o+32] + if len(data) != 32: + raise InputError("Ran out of partition table data before reaching end marker") + if data == '\xFF'*32: + break # end of partition table + result.append(PartitionDefinition.from_binary(data)) return result def to_binary(self): diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 169f2d56bc..9a22aeca97 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -19,28 +19,28 @@ Secure Boot Process Overview This is a high level overview of the secure boot process. Step by step instructions are supplied under `How To Enable Secure Boot`. Further in-depth details are supplied under `Technical Details`: -1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". +1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration". 2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. -2. The software bootloader image is built by esp-idf with the public key (signature verification) portion of the secure boot signing key compiled in, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. +2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. -3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: +3. On first boot, the software bootloader follows the following process to enable secure boot: - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - - Bootloader also disables JTAG via efuse. + - Depending on menuconfig choices, the bootloader may also disable JTAG and the UART bootloader function, both via efuse. (This is recommended.) 4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the secure boot signing key's public key (embedded in the bootloader itself, and therefore validated as part of the bootloader digest) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify all subsequent partition tables and app images before they are booted. Keys ---- The following keys are used by the secure boot process: -- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from a hardware random number generation, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. - "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. @@ -52,15 +52,15 @@ The following keys are used by the secure boot process: How To Enable Secure Boot ------------------------- -1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) +1. Run ``make menuconfig``, navigate to "Secure Boot Configuration" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) 2. Select a name for the secure boot signing key. This option will appear after secure boot is enabled. The file can be anywhere on your system. A relative path will be evaluated from the project directory. The file does not need to exist yet. -3. Set other config options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration +3. Set other menuconfig options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration 4. The first time you run ``make``, if the signing key is not found then an error message will be printed with a command to generate a signing key via ``espsecure.py generate_signing_key``. - **IMPORTANT** A signing key genereated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. + **IMPORTANT** A signing key generated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak. **IMPORTANT** For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See `Generating Secure Boot Signing Key` for more details. @@ -76,16 +76,16 @@ How To Enable Secure Boot *NOTE* Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the signing key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the public key portion of the secure boot signing key). Re-Flashable Software Bootloader -------------------------------- -The "Secure Boot: One-Time Flash" is the recommended software bootloader configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. +Configuration "Secure Boot: One-Time Flash" is the recommended configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. -In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 value is used to generate an 256-bit value which is then burned to efuse and used to protect the bootloader. This is a convenience so you only need to generate/protect a single private key. +In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 digest is used as the 256-bit secure bootloader key. This is a convenience so you only need to generate/protect a single private key. *NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments. @@ -95,7 +95,7 @@ To enable a reflashable bootloader: 2. Follow the steps shown above to choose a signing key file, and generate the key file. -3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key you generated for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the derived key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the derived key). +3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key that is used for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-calculated digest (generated during the build process). 4. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. @@ -114,6 +114,13 @@ openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key. Remember that the strength of the secure boot system depends on keeping the signing key private. +Secure Boot Best Practices +-------------------------- + +* Generate the signing key on a system with a quality source of entropy. +* Keep the signing key private at all times. A leak of this key will compromise the secure boot system. +* Do not allow any third party to observe any aspects of the key generation or signing process using espsecure.py. Both processes are vulnerable to timing or other side-channel attacks. +* Enable all secure boot options. These include flash encryption, disabling of JTAG, and disabling alternative boot modes. Technical Details ----------------- @@ -143,9 +150,9 @@ Items marked with (^) are to fulfill hardware restrictions, as opposed to crypto 1. Prefix the image with a 128 byte randomly generated IV. 2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) 3. For each 16 byte plaintext block of the input image: - - Reverse the byte order of the plaintext block (^) + - Reverse the byte order of the plaintext input block (^) - Apply AES256 in ECB mode to the plaintext block. - - Reverse the byte order of the 16 bytes of ciphertext output. (^) + - Reverse the byte order of the ciphertext output block. (^) - Append to the overall ciphertext output. 4. Byte-swap each 4 byte word of the ciphertext (^) 5. Calculate SHA-512 of the ciphertext. @@ -158,10 +165,12 @@ Image Signing Algorithm Deterministic ECDSA as specified by `RFC6979`. - Curve is NIST256p (openssl calls this curve "prime256v1", it is also sometimes called secp256r1). +- Hash function is SHA256. - Key format used for storage is PEM. - In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. + .. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 diff --git a/make/common.mk b/make/common.mk index 0c523c8775..bec8151b55 100644 --- a/make/common.mk +++ b/make/common.mk @@ -6,7 +6,8 @@ # # (Note that we only rebuild auto.conf automatically for some targets, # see project_config.mk for details.) --include $(BUILD_DIR_BASE)/include/config/auto.conf +SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf +-include $(SDKCONFIG_MAKEFILE) #Handling of V=1/VERBOSE=1 flag # From 5a5e19cd8b5b1ea09adce8ae73e62e400b09b65a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 11:55:09 +1100 Subject: [PATCH 195/285] Bump esptool revision (espefuse.py fixes) --- components/esptool_py/esptool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index d8b1ffdd50..b1e00025fa 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit d8b1ffdd500bd80a35fb66b64e2733195b39e519 +Subproject commit b1e00025fa6cbc63062b205259ee70d91bfe4989 From bcdebda8e47961751d35031272350f6a33a004b4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 14:44:10 +1100 Subject: [PATCH 196/285] Build system: Don't shell-quote SEPARATOR variable or it evaluates as a bunch of wildcards! --- components/bootloader/Makefile.projbuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 1b1c07bea3..3ed5e18783 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -37,7 +37,7 @@ ifdef CONFIG_SECURE_BOOTLOADER_DISABLED # with 'make flash' and no warnings are printed. bootloader: $(BOOTLOADER_BIN) - @echo "$(SEPARATOR)" + @echo $(SEPARATOR) @echo "Bootloader built. Default flash command is:" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" From 8691b54758102275b7e635bdaa52c2fafe8b4334 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:14:13 +1100 Subject: [PATCH 197/285] secure boot: Rename efuse option for UART bootloader to option for ROM interpreter --- components/bootloader/Kconfig.projbuild | 38 +++++++++---------- .../src/secure_boot_signatures.c | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 949638594d..50165d0e58 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -85,31 +85,31 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. config SECURE_BOOT_DISABLE_JTAG - bool "First boot: Permanently disable JTAG" - depends on SECURE_BOOTLOADER_ENABLED - default Y - help - Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. + bool "First boot: Permanently disable JTAG" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. - It is recommended this option remains set for production environments. + It is recommended this option remains set for production environments. -config SECURE_BOOT_DISABLE_UART_BOOTLOADER - bool "First boot: Permanently disable UART bootloader" - depends on SECURE_BOOTLOADER_ENABLED - default Y - help - Bootloader permanently disables UART and other bootloader modes when enabling secure boot. This happens on first boot. +config SECURE_BOOT_DISABLE_ROM_BASIC + bool "First boot: Permanently disable ROM BASIC fallback" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disables ROM BASIC (on UART console) as a fallback if the bootloader image becomes invalid. This happens on first boot. - It is recommended this option remains set for production environments. + It is recommended this option remains set in production environments. config SECURE_BOOT_TEST_MODE - bool "Test mode: don't actually enable secure boot" - depends on SECURE_BOOTLOADER_ENABLED - default N - help - If this option is set, all permanent secure boot changes (via Efuse) are disabled. + bool "Test mode: don't actually enable secure boot" + depends on SECURE_BOOTLOADER_ENABLED + default N + help + If this option is set, all permanent secure boot changes (via Efuse) are disabled. - This option is for testing purposes only - it effectively completely disables secure boot protection. + This option is for testing purposes only - it effectively completely disables secure boot protection. config SECURE_BOOTLOADER_ENABLED bool diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 5106eb396f..6d47651b2f 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -46,7 +46,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) uint8_t digest[32]; ptrdiff_t keylen; const uint8_t *data, *digest_data; - uint32_t digest_len, chunk_len; + uint32_t digest_len; const signature_block_t *sigblock; bool is_valid; From 734c1dd954a37cc859962f567b2d4b9c148b7657 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 14 Nov 2016 09:40:12 +0800 Subject: [PATCH 198/285] components/openssl: sync the code form esp8266 sdk --- components/openssl/include/internal/ssl_dbg.h | 7 ++-- components/openssl/platform/ssl_pm.c | 35 ++++++++++++++++++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/components/openssl/include/internal/ssl_dbg.h b/components/openssl/include/internal/ssl_dbg.h index 887fe2e82b..b4c0754637 100644 --- a/components/openssl/include/internal/ssl_dbg.h +++ b/components/openssl/include/internal/ssl_dbg.h @@ -55,16 +55,17 @@ #else #ifdef SSL_PRINT_LOG #undef SSL_PRINT_LOG - #define SSL_PRINT_LOG(...) #endif + #define SSL_PRINT_LOG(...) + #ifdef SSL_ERROR_LOG #undef SSL_ERROR_LOG - #define SSL_ERROR_LOG(...) #endif + #define SSL_ERROR_LOG(...) #ifdef SSL_LOCAL_LOG #undef SSL_LOCAL_LOG - #define SSL_LOCAL_LOG(...) #endif + #define SSL_LOCAL_LOG(...) #endif #if SSL_DEBUG_LOCATION_ENABLE diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 92e72bfdb8..091402cda4 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -215,6 +215,31 @@ static int ssl_pm_reload_crt(SSL *ssl) return 0; } +/* + * Perform the mbedtls SSL handshake instead of mbedtls_ssl_handshake. + * We can add debug here. + */ +LOCAL int mbedtls_handshake( mbedtls_ssl_context *ssl ) +{ + int ret = 0; + + if (ssl == NULL || ssl->conf == NULL) + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + + while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) + { + ret = mbedtls_ssl_handshake_step(ssl); + + SSL_DEBUG(1, "ssl ret %d state %d heap %d\n", + ret, ssl->state, system_get_free_heap_size()); + + if (ret != 0) + break; + } + + return ret; +} + int ssl_pm_handshake(SSL *ssl) { int ret, mbed_ret; @@ -224,13 +249,19 @@ int ssl_pm_handshake(SSL *ssl) if (mbed_ret) return 0; + SSL_DEBUG(1, "ssl_speed_up_enter "); ssl_speed_up_enter(); - while((mbed_ret = mbedtls_ssl_handshake(&ssl_pm->ssl)) != 0) { + SSL_DEBUG(1, "OK\n"); + + while((mbed_ret = mbedtls_handshake(&ssl_pm->ssl)) != 0) { if (mbed_ret != MBEDTLS_ERR_SSL_WANT_READ && mbed_ret != MBEDTLS_ERR_SSL_WANT_WRITE) { break; } } + + SSL_DEBUG(1, "ssl_speed_up_exit "); ssl_speed_up_exit(); + SSL_DEBUG(1, "OK\n"); if (!mbed_ret) { struct x509_pm *x509_pm = (struct x509_pm *)ssl->session->peer->x509_pm; @@ -492,6 +523,7 @@ int x509_pm_load(X509 *x, const unsigned char *buffer, int len) return 0; failed2: + mbedtls_x509_crt_free(x509_pm->x509_crt); ssl_mem_free(x509_pm->x509_crt); x509_pm->x509_crt = NULL; failed1: @@ -567,6 +599,7 @@ int pkey_pm_load(EVP_PKEY *pk, const unsigned char *buffer, int len) return 0; failed2: + mbedtls_pk_free(pkey_pm->pkey); ssl_mem_free(pkey_pm->pkey); pkey_pm->pkey = NULL; failed1: From 045a53a7e5aeb1263ee24671473e3a705d9df6e3 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 14 Nov 2016 10:20:49 +0800 Subject: [PATCH 199/285] Fix wdt idle hook --- components/esp32/task_wdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index f8cfdef26e..549b7f58b2 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -145,7 +145,7 @@ void esp_task_wdt_delete() { #if CONFIG_TASK_WDT_CHECK_IDLE_TASK static bool idle_hook(void) { #if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 - if (xPortGetCoreID()!=0) return; + if (xPortGetCoreID()!=0) return true; #endif esp_task_wdt_feed(); return true; From 572f62928b9dd6a5036b98893a6a2059c34d9e87 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:40:29 +1100 Subject: [PATCH 200/285] Secure boot: Documentation tweaks --- docs/index.rst | 1 + docs/security/secure-boot.rst | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index c973950615..30cfa177c3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ Contents: partition-tables build_system openocd + Secure Boot .. toctree:: :caption: API Reference diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 9a22aeca97..c08b17cb47 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -3,17 +3,20 @@ Secure Boot Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset. -Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However for maximum protection we recommend using both features together. +Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. + Background ---------- -- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored in Efuses internal to the chip. +- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored (non-software-accessible) in Efuses internal to the chip. - Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. - To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. +- Both stages of the boot process (initial software bootloader load, and subsequent partition & app loading) are verified by the secure boot process, in a "chain of trust" relationship. + Secure Boot Process Overview ---------------------------- @@ -21,19 +24,19 @@ This is a high level overview of the secure boot process. Step by step instructi 1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration". -2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. +2. Secure Boot Configuration includes "Secure boot signing key", which is a file path. This file is a ECDSA public/private key pair in a PEM format file. 2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader follows the following process to enable secure boot: - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. + - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - - Depending on menuconfig choices, the bootloader may also disable JTAG and the UART bootloader function, both via efuse. (This is recommended.) -4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify the signature appended to all subsequent partition tables and app images before they are booted. Keys ---- @@ -42,11 +45,11 @@ The following keys are used by the secure boot process: - "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. -- "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. +- "secure boot signing key" is a standard ECDSA public/private key pair (see `Image Signing Algorithm`) in PEM format. - The public key from this key pair (for signature verificaton but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. - - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to a bootloader with secure boot using the matching public key. + - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to any bootloader that is configured with secure boot and the matching public key. How To Enable Secure Boot @@ -76,7 +79,7 @@ How To Enable Secure Boot *NOTE* Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the public key portion of the secure boot signing key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader has not changed (using the secure bootloader key) and then the software bootloader will verify the signed partition table and app image (using the public key portion of the secure boot signing key). Re-Flashable Software Bootloader -------------------------------- @@ -120,7 +123,7 @@ Secure Boot Best Practices * Generate the signing key on a system with a quality source of entropy. * Keep the signing key private at all times. A leak of this key will compromise the secure boot system. * Do not allow any third party to observe any aspects of the key generation or signing process using espsecure.py. Both processes are vulnerable to timing or other side-channel attacks. -* Enable all secure boot options. These include flash encryption, disabling of JTAG, and disabling alternative boot modes. +* Enable all secure boot options in the Secure Boot Configuration. These include flash encryption, disabling of JTAG, disabling BASIC ROM interpeter, and disabling the UART bootloader encrypted flash access. Technical Details ----------------- @@ -136,19 +139,19 @@ The Secure Boot support hardware can perform three basic operations: 2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read back by software if Efuse ABS_DONE_0 is *not* burned (ie still 0). -3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. +3. Generate a digest from data (usually the bootloader image from flash) using the same algorithm as step 2 and compare it to a pre-calculated digest supplied in a buffer (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. Secure Bootloader Digest Algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Starting with an "image" of binary data as input, this algorithm generates a digest as output. +Starting with an "image" of binary data as input, this algorithm generates a digest as output. The digest is sometimes referred to as an "abstract" in hardware documentation. For a Python version of this algorithm, see the `espsecure.py` tool in the components/esptool_py directory. Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions. 1. Prefix the image with a 128 byte randomly generated IV. -2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) +2. If the image length is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) 3. For each 16 byte plaintext block of the input image: - Reverse the byte order of the plaintext input block (^) - Apply AES256 in ECB mode to the plaintext block. @@ -171,6 +174,5 @@ Deterministic ECDSA as specified by `RFC6979`. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. - .. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 From 0b4fe9dd6dc62571abcc66c0235582f00d94d2e5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:40:58 +1100 Subject: [PATCH 201/285] secure boot: Add warnings this feature is not finished yet --- components/bootloader/Makefile.projbuild | 21 +++++++++++++++++++++ docs/security/secure-boot.rst | 1 + 2 files changed, 22 insertions(+) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3ed5e18783..551bceb981 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -47,6 +47,16 @@ bootloader-flash: $(BOOTLOADER_BIN) $(BOOTLOADER_MAKE) flash else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH + +#### TEMPORARILY DISABLE THIS OPTION +ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") +bootloader: + @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device" + @echo "If you flash this bootloader, you will be left with an non-updateable bootloader that is missing features." + @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." + exit 1 +else + # One time flashing requires user to run esptool.py command themselves, # and warning is printed about inability to reflash. @@ -57,10 +67,20 @@ bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" +endif # IDF_INSECURE_SECURE_BOOT else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) +#### TEMPORARILY DISABLE THIS OPTION +ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") +bootloader: + @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device." + @echo "If using this feature, expect to reflash the bootloader at least one more time." + @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." + exit 1 +else + BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin @@ -83,6 +103,7 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) @echo "DIGEST $(notdir $@)" $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< +endif # IDF_INSECURE_SECURE_BOOT else bootloader: @echo "Invalid bootloader target: bad sdkconfig?" diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index c08b17cb47..bdc1b71699 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -5,6 +5,7 @@ Secure Boot is a feature for ensuring only your code can run on the chip. Data l Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. +**IMPORTANT: As Encrypted Flash feature and related security features are not yet released, Secure Boot should not be considered sufficient for a secure device and we strongly recommend not enabling the one-time secure bootloader feature until it is mature.** Background ---------- From 09c7ccfa2cd2c21f14c0c599e25e3bb473dd64eb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:47:38 +1100 Subject: [PATCH 202/285] bootloader: Fix unused variable errors when secure boot is disabled --- components/bootloader/src/main/bootloader_start.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 36d3ff0d29..a811294a8f 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -109,7 +109,6 @@ void IRAM_ATTR call_start_cpu0() */ bool load_partition_table(bootloader_state_t* bs) { - esp_err_t err; const esp_partition_info_t *partitions; const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); @@ -121,7 +120,7 @@ bool load_partition_table(bootloader_state_t* bs) #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED if(esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying partition table signature..."); - err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; @@ -227,7 +226,6 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); - esp_err_t err; esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; @@ -322,7 +320,7 @@ void bootloader_main() #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED /* Generate secure digest from this bootloader to protect future modifications */ - err = esp_secure_boot_permanently_enable(); + esp_err_t err = esp_secure_boot_permanently_enable(); if (err != ESP_OK) { ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); /* Allow booting to continue, as the failure is probably From c04f05ee84754a378d0d25eddf2d00d8a2e52226 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 15:29:27 +1100 Subject: [PATCH 203/285] build examples: Only build verbose on failure, save on log output --- make/build_examples.sh | 19 +++++++++---------- make/component_wrapper.mk | 20 +++++++++++++------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index 53cba23b1e..a522666a98 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -15,16 +15,15 @@ RESULT=0 set -e for example in ${IDF_PATH}/examples/*; do - [ -f ${example}/Makefile ] || continue - echo "Building ${example} as ${EXAMPLE_NUM}..." - mkdir ${EXAMPLE_NUM} - cp -r ${example} ${EXAMPLE_NUM} - pushd ${EXAMPLE_NUM}/`basename ${example}` - # can't do "make defconfig all" as this will trip menuconfig - # sometimes - make defconfig V=1 && make V=1 || RESULT=$? - popd - EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) + [ -f ${example}/Makefile ] || continue + echo "Building ${example} as ${EXAMPLE_NUM}..." + mkdir ${EXAMPLE_NUM} + cp -r ${example} ${EXAMPLE_NUM} + pushd ${EXAMPLE_NUM}/`basename ${example}` + # build non-verbose first, only build verbose if there's an error + make defconfig all || (RESULT=$?; make V=1) + popd + EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) done exit $RESULT diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index e1283bda8f..55a135158a 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -41,8 +41,9 @@ COMPONENT_LIBRARY = lib$(COMPONENT_NAME).a # Source dirs a component has. Default to root directory of component. COMPONENT_SRCDIRS = . -#Names of binary files to embed as symbols in the component library +#Names of binary & text files to embed as raw content in the component library COMPONENT_EMBED_FILES ?= +COMPONENT_EMBED_TXTFILES ?= # By default, include only the include/ dir. COMPONENT_ADD_INCLUDEDIRS = include @@ -72,6 +73,11 @@ COMPONENT_OBJS += $(foreach compsrcdir,$(COMPONENT_SRCDIRS),$(patsubst %.S,%.o,$ COMPONENT_OBJS := $(patsubst $(COMPONENT_PATH)/%,%,$(COMPONENT_OBJS)) endif +# Object files with embedded binaries to add to the component library +# Correspond to the files named in COMPONENT_EMBED_FILES & COMPONENT_EMBED_TXTFILES +COMPONENT_EMBED_OBJS ?= $(addsuffix .bin.o,$(COMPONENT_EMBED_FILES)) $(addsuffix .txt.o,$(COMPONENT_EMBED_TXTFILES)) + + # If we're called to compile something, we'll get passed the COMPONENT_INCLUDES # variable with all the include dirs from all the components in random order. This # means we can accidentally grab a header from another component before grabbing our own. @@ -133,7 +139,7 @@ build: $(COMPONENT_LIBRARY) $(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(COMPONENT_EMBED_OBJS) $(summary) AR $@ rm -f $@ - $(AR) cru $@ $(COMPONENT_OBJS) + $(AR) cru $@ $^ endif # If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target @@ -187,11 +193,11 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect # path to the input file. define GenerateEmbedTarget $(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) - $$(summary) EMBED $$@ - $$(Q) $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir - $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output - $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ - $$(Q) rm $$(notdir $$<) + $(summary) EMBED $$@ + $$(if $$(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir + $$(if $$(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output + $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ + rm $$(notdir $$<) endef # generate targets to embed binary & text files From f7af4ddc89d553c04bddb5e070dc0c26e5b1d0c2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 15:54:33 +1100 Subject: [PATCH 204/285] Examples: Remove deprecated system_init() call --- examples/01_hello_world/main/hello_world_main.c | 1 - examples/02_blink/main/blink.c | 1 - examples/03_http_request/main/http_request_main.c | 1 - examples/04_https_request/main/https_request_main.c | 1 - examples/06_sntp/main/sntp_main.c | 1 - 5 files changed, 5 deletions(-) diff --git a/examples/01_hello_world/main/hello_world_main.c b/examples/01_hello_world/main/hello_world_main.c index ad2c0acbbd..1ff190acef 100644 --- a/examples/01_hello_world/main/hello_world_main.c +++ b/examples/01_hello_world/main/hello_world_main.c @@ -27,6 +27,5 @@ void hello_task(void *pvParameter) void app_main() { nvs_flash_init(); - system_init(); xTaskCreate(&hello_task, "hello_task", 2048, NULL, 5, NULL); } diff --git a/examples/02_blink/main/blink.c b/examples/02_blink/main/blink.c index 6de0e1fa36..1e49e51b2f 100644 --- a/examples/02_blink/main/blink.c +++ b/examples/02_blink/main/blink.c @@ -43,6 +43,5 @@ void blink_task(void *pvParameter) void app_main() { nvs_flash_init(); - system_init(); xTaskCreate(&blink_task, "blink_task", 512, NULL, 5, NULL); } diff --git a/examples/03_http_request/main/http_request_main.c b/examples/03_http_request/main/http_request_main.c index 32f75c0da2..2c203f8394 100644 --- a/examples/03_http_request/main/http_request_main.c +++ b/examples/03_http_request/main/http_request_main.c @@ -175,7 +175,6 @@ static void http_get_task(void *pvParameters) void app_main() { nvs_flash_init(); - system_init(); initialise_wifi(); xTaskCreate(&http_get_task, "http_get_task", 2048, NULL, 5, NULL); } diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index 7f302409d8..2ad56681d0 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -369,7 +369,6 @@ static void https_get_task(void *pvParameters) void app_main() { nvs_flash_init(); - system_init(); initialise_wifi(); xTaskCreate(&https_get_task, "https_get_task", 8192, NULL, 5, NULL); } diff --git a/examples/06_sntp/main/sntp_main.c b/examples/06_sntp/main/sntp_main.c index 7f516625e3..83f33b9656 100644 --- a/examples/06_sntp/main/sntp_main.c +++ b/examples/06_sntp/main/sntp_main.c @@ -94,7 +94,6 @@ void app_main() static void obtain_time(void) { nvs_flash_init(); - system_init(); initialise_wifi(); xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); From 751736f902f9790f69e8f1cb43348ef35d8f077b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 14 Nov 2016 15:55:26 +1100 Subject: [PATCH 205/285] ble_adv example: Remove int return value from app_main() --- examples/05_ble_adv/main/app_bt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/05_ble_adv/main/app_bt.c b/examples/05_ble_adv/main/app_bt.c index 7f5dda5ec5..bfdd0b8c68 100755 --- a/examples/05_ble_adv/main/app_bt.c +++ b/examples/05_ble_adv/main/app_bt.c @@ -197,10 +197,9 @@ void bleAdvtTask(void *pvParameters) } } -int app_main() +void app_main() { bt_controller_init(); xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0); - return 0; } From 9a7db9ca85f9471de4d8019aead68a1176cf62d3 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Mon, 14 Nov 2016 14:34:09 +0800 Subject: [PATCH 206/285] add comments for system_event_sta_wps_er_pin_t --- components/esp32/include/esp_event.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/include/esp_event.h b/components/esp32/include/esp_event.h index 3b2b54acca..55575b13a8 100644 --- a/components/esp32/include/esp_event.h +++ b/components/esp32/include/esp_event.h @@ -102,7 +102,7 @@ typedef union { system_event_sta_scan_done_t scan_done; /**< ESP32 station scan (APs) done */ system_event_sta_authmode_change_t auth_change; /**< the auth mode of AP ESP32 station connected to changed */ system_event_sta_got_ip_t got_ip; /**< ESP32 station got IP */ - system_event_sta_wps_er_pin_t sta_er_pin; + system_event_sta_wps_er_pin_t sta_er_pin; /**< ESP32 station WPS enrollee mode PIN code received */ system_event_ap_staconnected_t sta_connected; /**< a station connected to ESP32 soft-AP */ system_event_ap_stadisconnected_t sta_disconnected; /**< a station disconnected to ESP32 soft-AP */ system_event_ap_probe_req_rx_t ap_probereqrecved; /**< ESP32 soft-AP receive probe request packet */ From 794b4ff578c6c79416d616659250299d6eab087b Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Mon, 14 Nov 2016 15:11:22 +0800 Subject: [PATCH 207/285] components/openssl: add client and server demo --- examples/09_openssl/Makefile | 9 + examples/09_openssl/README.md | 21 + examples/09_openssl/main/Kconfig.projbuild | 27 ++ examples/09_openssl/main/component.mk | 10 + examples/09_openssl/main/openssl_demo.c | 430 +++++++++++++++++++++ examples/09_openssl/main/openssl_demo.h | 66 ++++ 6 files changed, 563 insertions(+) create mode 100644 examples/09_openssl/Makefile create mode 100644 examples/09_openssl/README.md create mode 100644 examples/09_openssl/main/Kconfig.projbuild create mode 100644 examples/09_openssl/main/component.mk create mode 100644 examples/09_openssl/main/openssl_demo.c create mode 100644 examples/09_openssl/main/openssl_demo.h diff --git a/examples/09_openssl/Makefile b/examples/09_openssl/Makefile new file mode 100644 index 0000000000..8987be554c --- /dev/null +++ b/examples/09_openssl/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := openssl + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/09_openssl/README.md b/examples/09_openssl/README.md new file mode 100644 index 0000000000..54b0133420 --- /dev/null +++ b/examples/09_openssl/README.md @@ -0,0 +1,21 @@ +# Openssl Example + +The Example contains of OpenSSL client and server demo. + +First you should config the project by "menuconfig": + Example Configuration -> + 1. Openssl demo: select your demo (client or server) + 2. WiFi SSID: you own wifi, which you pc is connected to alse. + 3. WiFi Password: wifi password + +If you want to test the OpenSSL client demo: + 1. compile the code and load the firmware + 2. you can see it will download the "www.baidu.com" main page and print the context + +IF you want to test the openSSL client demo: + 1. compile the code and load the firmware + 2. You should input the context of "https://192.168.17.128", the IP of your module may not be 192.168.17.128, you should input your module's IP + 3. You may see that it shows the website is not able to be trusted, but you should select that "go on to visit it" + 4. You should wait for a moment until your see the "OpenSSL server demo!" in your IE page + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/09_openssl/main/Kconfig.projbuild b/examples/09_openssl/main/Kconfig.projbuild new file mode 100644 index 0000000000..b42529ca19 --- /dev/null +++ b/examples/09_openssl/main/Kconfig.projbuild @@ -0,0 +1,27 @@ +menu "Example Configuration" + +choice OPENSSL_DEMO + prompt "Openssl demo" + default OPENSSL_CLIENT_DEMO + help + Openssl test demo mode, client or server. + +config OPENSSL_CLIENT_DEMO + bool "client demo" +config OPENSSL_SERVER_DEMO + bool "server demon" +endchoice + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "myssid" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/09_openssl/main/component.mk b/examples/09_openssl/main/component.mk new file mode 100644 index 0000000000..24356f23ed --- /dev/null +++ b/examples/09_openssl/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/examples/09_openssl/main/openssl_demo.c b/examples/09_openssl/main/openssl_demo.c new file mode 100644 index 0000000000..d541b26253 --- /dev/null +++ b/examples/09_openssl/main/openssl_demo.c @@ -0,0 +1,430 @@ +#include +#include + +#include "openssl_demo.h" +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_types.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" + +#include "lwip/sockets.h" +#include "lwip/api.h" + +#include "nvs_flash.h" + + +#define os_printf(fmt, ...) ESP_LOGI("openssl_demo", fmt, ##__VA_ARGS__) + +#define IP_ADDR(ip) ip.u_addr.ip4.addr + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define OPENSSL_DEMO_THREAD_STACK_WORDS 8192 +#define OPENSSL_DEMO_THREAD_PRORIOTY 6 + +#define OPENSSL_DEMO_FRAGMENT_SIZE 8192 + +#define OPENSSL_DEMO_RECV_BUF_LEN 1024 + +#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 + +#ifdef CONFIG_OPENSSL_CLIENT_DEMO + +#define OPENSSL_DEMO_THREAD_NAME "ssl_client" + +#define OPENSSL_DEMO_TARGET_NAME "www.baidu.com" +#define OPENSSL_DEMO_TARGET_TCP_PORT 443 + +#define OPENSSL_DEMO_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" + +LOCAL void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket; + struct sockaddr_in sock_addr; + + ip_addr_t target_ip; + + int recv_bytes = 0; + + LOCAL char send_data[] = OPENSSL_DEMO_REQUEST; + LOCAL int send_bytes = sizeof(send_data); + + LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + os_printf("OpenSSL demo thread start\n"); + + do { + ret = netconn_gethostbyname(OPENSSL_DEMO_TARGET_NAME, &target_ip); + } while(ret); + os_printf("get target IP is %d.%d.%d.%d\n", + (unsigned char)((IP_ADDR(target_ip) & 0x000000ff) >> 0), + (unsigned char)((IP_ADDR(target_ip) & 0x0000ff00) >> 8), + (unsigned char)((IP_ADDR(target_ip) & 0x00ff0000) >> 16), + (unsigned char)((IP_ADDR(target_ip) & 0xff000000) >> 24)); + + os_printf("create SSL context ......"); + ctx = SSL_CTX_new(TLSv1_1_client_method()); + if (!ctx) { + os_printf("failed\n"); + goto failed1; + } + os_printf("OK\n"); + + /** + * The openssl does not support "SSL_CTX_set_default_read_buffer_len" + * at the platform of ESP32 esp_idf now. + * + * So you should not care it now. And We my let it work later. + */ + os_printf("set SSL context read buffer size ......"); + SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); + ret = 0; + if (ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + + os_printf("bind socket ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + os_printf("failed\n"); + goto failed4; + } + os_printf("OK\n"); + + os_printf("socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = IP_ADDR(target_ip); + sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); + ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + os_printf("failed\n"); + goto failed5; + } + os_printf("OK\n"); + + os_printf("create SSL ......"); + ssl = SSL_new(ctx); + if (!ssl) { + os_printf("failed\n"); + goto failed6; + } + os_printf("OK\n"); + + SSL_set_fd(ssl, socket); + + os_printf("SSL connected to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_connect(ssl); + if (!ret) { + os_printf("failed, return [-0x%x]\n", -ret); + goto failed7; + } + os_printf("OK\n"); + + os_printf("send https request to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_write(ssl, send_data, send_bytes); + if (ret <= 0) { + os_printf("failed\n"); + goto failed8; + } + os_printf("OK\n\n"); + + do { + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + recv_bytes += ret; + os_printf("%s", recv_buf); + } while (1); + + os_printf("\r\ntotaly read %d bytes data from %s ......\n", recv_bytes, OPENSSL_DEMO_TARGET_NAME); + +failed8: + SSL_shutdown(ssl); +failed7: + SSL_free(ssl); + ssl = NULL; +failed6: +failed5: +failed4: + close(socket); + socket = -1; +failed3: +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + + os_printf("task exit\n"); + + return ; +} + +#elif defined(CONFIG_OPENSSL_SERVER_DEMO) + +#define OPENSSL_DEMO_THREAD_NAME "openssl_server" + +#define OPENSSL_DEMO_CLIENT_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" + +#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/html\r\n" \ + "Content-Length: 98\r\n" \ + "\r\n" \ + "\r\n" \ + "openSSL demo\r\n" \ + "OpenSSL server demo!\r\n" \ + "\r\n" \ + "\r\n" + +LOCAL void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket, new_socket; + socklen_t addr_len; + struct sockaddr_in sock_addr; + + LOCAL char send_data[] = OPENSSL_DEMO_SERVER_ACK; + LOCAL int send_bytes = sizeof(send_data); + + LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + os_printf("server SSL context create ......"); + ctx = SSL_CTX_new(SSLv3_server_method()); + if (!ctx) { + os_printf("failed\n"); + goto failed1; + } + os_printf("OK\n"); + + /** + * The openssl does not support "SSL_CTX_set_default_read_buffer_len" + * at the platform of ESP32 esp_idf now. + * + * So you should not care it now. And We my let it work later. + */ + os_printf("server SSL context set fragment ......"); + SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); + ret = 0; + if (ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server SSL context set own certification......"); + ret = SSL_CTX_use_certificate_ASN1(ctx, cert_bytes, cert_ctx); + if (!ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server SSL context set private key......"); + ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, key_ctx, key_bytes); + if (!ret) { + os_printf("failed, return %d\n", ret); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + os_printf("failed\n"); + goto failed2; + } + os_printf("OK\n"); + + os_printf("server socket bind ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + + os_printf("server socket listen ......"); + ret = listen(socket, 32); + if (ret) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + +reconnect: + os_printf("server SSL create ......"); + ssl = SSL_new(ctx); + if (!ssl) { + os_printf("failed\n"); + goto failed3; + } + os_printf("OK\n"); + + os_printf("server socket accept client ......"); + new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); + if (new_socket < 0) { + os_printf("failed, return [-0x%x]\n", -new_socket); + goto failed4; + } + os_printf("OK\n"); + + SSL_set_fd(ssl, new_socket); + + os_printf("server SSL accept client ......"); + ret = SSL_accept(ssl); + if (!ret) { + os_printf("failed\n"); + goto failed5; + } + os_printf("OK\n"); + + os_printf("server SSL read message ......"); + do { + memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + if (strstr(recv_buf, "GET / HTTP/1.1")) { + SSL_write(ssl, send_data, send_bytes); + break; + } + } while (1); + + os_printf("result %d\n", ret); + + SSL_shutdown(ssl); +failed5: + close(new_socket); + new_socket = -1; +failed4: + SSL_free(ssl); + ssl = NULL; + goto reconnect; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + + return ; +} + +#else + #error "you must choose the right demo type" +#endif + +LOCAL void demo_init(void) +{ + int ret = pdFALSE; + +#if defined(CONFIG_OPENSSL_CLIENT_DEMO) || defined(CONFIG_OPENSSL_SERVER_DEMO) + xTaskHandle openssl_handle; + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); +#endif + + if (ret != pdPASS) { + os_printf("create thread %s failed\n", OPENSSL_DEMO_THREAD_NAME); + return ; + } +} + +LOCAL esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + demo_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + break; + default: + break; + } + return ESP_OK; +} + +LOCAL void wifi_conn_init(void) +{ + tcpip_adapter_init(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + os_printf("start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} + diff --git a/examples/09_openssl/main/openssl_demo.h b/examples/09_openssl/main/openssl_demo.h new file mode 100644 index 0000000000..89cd264b46 --- /dev/null +++ b/examples/09_openssl/main/openssl_demo.h @@ -0,0 +1,66 @@ +#ifndef _OPENSSL_DEMO_H_ +#define _OPENSSL_DEMO_H_ + +const static unsigned char key_ctx[] = + "-----BEGIN PRIVATE KEY-----\r\n" + "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQyyF0WBb2XbkL\r\n" + "wYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijBR0Rr\r\n" + "0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe6eFG\r\n" + "AGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wHzywA\r\n" + "DvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwbrdcD\r\n" + "HNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gmpqfh\r\n" + "CwEPoKCNAgMBAAECggEBAIVr+LHXBL1kQLZhbiKC5t1FrMY8FLKYGM3JCevciMlk\r\n" + "lhIXwR7p29vsRYZfHBv7zWWyI9/C04JG31MiGOMaCbBxfnA2HBrVEqFwwxqnKVi8\r\n" + "CxzwGQkfwu3Y9j7TEj0PipfQYo+aKzmDrN/FrXnHjyEJryxAQbAZPVLW+Z7OR41R\r\n" + "ZOwtZLeVqmbeARGpu2Xd9SKAhbjdLSz96IdUcNrwbP/lzUgrKaiUioBMVFfIG5ce\r\n" + "4Mm2seCwaWxI8k24q0keSjsjV+5IxatVUNtJ9vYv6Tzo+3oqGvPeUBO7w9xhbLKf\r\n" + "jw1uEykcs0wcftWb1iB7r78bMPZ/KYhnSFsjT+vnIOECgYEA9LM5p63sn6OWrULd\r\n" + "doBBLaIw1ZK9rDUFLLRwv0e6+7tuuB08ok6D7PSfOpx0bb10lIx7epOE8ogGSmLS\r\n" + "w0rMbmcKAlTLAJ/0nX1ierZkb+Ay21F398tKL3igEfnaB9CzuOHF8XhbsTqeGFDJ\r\n" + "HFBMXxTbo4kfkUmZNYxwTombzkkCgYEA2m9teqjEOVZItqghOicWZ68JhWxBJFme\r\n" + "oSfzJKLd8atfUOnBLqAhPzvg1PvdIxjLWBqy28tEJf+jdSQCNe9BmhlptOwbFrJy\r\n" + "IyCXj6QTApSKTxyzIjMvzQkv1m8CxeCq5T64hvJ2++i7dlhumh21c7oL8aLeTnoe\r\n" + "AG1dBLJ9UCUCgYAhSlDJsyvB/Ct/nt0qRgCYCLzEEZakWwI9Nr8wBr41iSZi/fdF\r\n" + "zZC9J/qRqr9cPq4hl4sk/fTUWhUhAZjS4NY3HuWJs6d6ikhpNKm1MCMx5TqGA+ti\r\n" + "VtHc63g7edZjwczxliWr2EgBMIxZmoQByhrZxKis8vbMeUrSsiyFQstjoQKBgD3k\r\n" + "2Paqn39Hra7kERYzQSv78wy1UfgE1OgBM+orpAv4bTe2JKEbipAqXVi8TTrGqce7\r\n" + "OPcCr7q8pwpoO6AgvUv263byd/KEecbuU0PGUASpJk+oaDHGo0LL2Zw/NF/xezsd\r\n" + "/JdwWLqkhYnRIPXWeTXjf8LmTWubOqkQVA0irlNpAoGAJ+9N/NF3XAW0BroiVYLZ\r\n" + "p0Btcgt+b4LWrBlm0XqHhzKUlqhfibAr3OtUkFjo/509ncYPiuOzVSNosyjXFJ86\r\n" + "2kQ88fB3eeLnBAcbBXQKiOBPU2y6bCCfgdo+JEOK/cxVslaxMAyKSnFi9gdgzScd\r\n" + "k+hOlkflXQVkic3W358kFto=\r\n" + "-----END PRIVATE KEY-----\r\n" + ; +static int key_bytes = sizeof(key_ctx); + +const static unsigned char cert_ctx[] = + "-----BEGIN CERTIFICATE-----\r\n" + "MIID7jCCAtYCAQEwDQYJKoZIhvcNAQELBQAwgbwxCzAJBgNVBAYTAkNOMRAwDgYD\r\n" + "VQQIDAdKaWFuZ3N1MQ0wCwYDVQQHDARXdXhpMSYwJAYDVQQKDB1Fc3ByZXNzaWYg\r\n" + "Um9vdCBSU0EyMDQ4IHNoYTI1NjEcMBoGA1UECwwTUm9vdCBSU0EyMDQ4IHNoYTI1\r\n" + "NjEfMB0GA1UEAwwWcm9vdGNlcnQuZXNwcmVzc2lmLmNvbTElMCMGCSqGSIb3DQEJ\r\n" + "ARYWcm9vdGNlcnRAZXNwcmVzc2lmLmNvbTAeFw0xNjA2MjgwMjMxMjlaFw0yNjA2\r\n" + "MjYwMjMxMjlaMIG8MQswCQYDVQQGEwJDTjEQMA4GA1UECAwHSmlhbmdzdTENMAsG\r\n" + "A1UEBwwEV3V4aTEmMCQGA1UECgwdRXNwcmVzc2lmIFJvb3QgUlNBMjA0OCBzaGEy\r\n" + "NTYxHDAaBgNVBAsME1Jvb3QgUlNBMjA0OCBzaGEyNTYxHzAdBgNVBAMMFnJvb3Rj\r\n" + "ZXJ0LmVzcHJlc3NpZi5jb20xJTAjBgkqhkiG9w0BCQEWFnJvb3RjZXJ0QGVzcHJl\r\n" + "c3NpZi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQyyF0WBb2\r\n" + "XbkLwYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijB\r\n" + "R0Rr0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe\r\n" + "6eFGAGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wH\r\n" + "zywADvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwb\r\n" + "rdcDHNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gm\r\n" + "pqfhCwEPoKCNAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABTYZLiFHq51lqaa0nHI\r\n" + "aDMAb29DfO93fqp+oHZYO4xKyEeLr8EhD39GjnQmhz710wO0TBCYV7nD+xnJ1h5F\r\n" + "IbQUAQZO9NIy3ns4mYVRUWjnWYAo+evGeKgRrxvh7sjNLPBPzs9tg/u7XjBp/nor\r\n" + "8JnnFFT0wXPyi/qg8J3QutqJvWRQGRRx2AP93F44+Zcj7ReFMVSmOXyzT4aNJL0+\r\n" + "Ls+baKwA4pnyVRoAaKbs/JYDgd0/DunuktVKuhyvK/qOGjJSRLPhdrXbvSAegpiM\r\n" + "4xIm6ZWKtTv8VvkGgXUVQ7RpruP6nV6506gDcUgecbEq7H2VDhEzUYcMmGCUQZlG\r\n" + "sJ8=\r\n" + "-----END CERTIFICATE-----\r\n" + ; +static int cert_bytes = sizeof(cert_ctx); + + + +#endif From 64a2f0ee0bf31f7921069f601515f9bc638ccfff Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 15 Nov 2016 10:29:52 +0800 Subject: [PATCH 208/285] Amend gpio driver to work around SoC bug with some pullups/pulldowns in the GPIO peripheral; mark existing function that does not always work as deprecated --- components/driver/gpio.c | 92 ++++++++++++++++++++--- components/driver/include/driver/gpio.h | 84 +++++++++++++++++++-- components/esp32/gdbstub.c | 3 +- components/esp32/include/soc/io_mux_reg.h | 39 +++++++++- 4 files changed, 194 insertions(+), 24 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index b445d3df03..ddbcb352c0 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -69,6 +69,74 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_39 }; +const gpio_pu_pd_desc_t gpio_pu_pd_desc[GPIO_PIN_COUNT]={ + {RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M}, + {PERIPHS_IO_MUX_U0TXD_U, FUN_PU, FUN_PD}, + {RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M}, + {PERIPHS_IO_MUX_U0RXD_U, FUN_PU, FUN_PD}, + {RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M}, + {PERIPHS_IO_MUX_GPIO5_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_CLK_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA0_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA1_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA2_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_DATA3_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_SD_CMD_U, FUN_PU, FUN_PD}, + {RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M}, + {RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M}, + {RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M}, + {RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M}, + {PERIPHS_IO_MUX_GPIO16_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO17_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO18_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO19_U, FUN_PU, FUN_PD}, + {0,0,0}, + {PERIPHS_IO_MUX_GPIO21_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO22_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO23_U, FUN_PU, FUN_PD}, + {0,0,0}, + {RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M}, + {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M}, + {RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M}, + {0,0,0}, + {0,0,0}, + {0,0,0}, + {0,0,0}, + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M}, + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M}, + {PERIPHS_IO_MUX_GPIO34_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO35_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO36_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO37_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO38_U, FUN_PU, FUN_PD}, + {PERIPHS_IO_MUX_GPIO39_U, FUN_PU, FUN_PD} +}; + + +esp_err_t gpio_pullup_en(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + return ESP_OK; +} + +esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + return ESP_OK; +} + +esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); + return ESP_OK; +} + +esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) { + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); + return ESP_OK; +} + esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); @@ -152,20 +220,20 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull) esp_err_t ret = ESP_OK; switch(pull) { case GPIO_PULLUP_ONLY: - PIN_PULLUP_EN(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; case GPIO_PULLDOWN_ONLY: - PIN_PULLUP_DIS(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_EN(GPIO_PIN_MUX_REG[gpio_num]); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; case GPIO_PULLUP_PULLDOWN: - PIN_PULLUP_EN(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_EN(GPIO_PIN_MUX_REG[gpio_num]); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; case GPIO_FLOATING: - PIN_PULLUP_DIS(GPIO_PIN_MUX_REG[gpio_num]); - PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); + REG_CLR_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pd); break; default: ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u",gpio_num,pull); @@ -253,15 +321,15 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) } if(pGPIOConfig->pull_up_en) { pu_en = 1; - PIN_PULLUP_EN(io_reg); + REG_SET_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } else { - PIN_PULLUP_DIS(io_reg); + REG_CLR_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } if(pGPIOConfig->pull_down_en) { pd_en = 1; - PIN_PULLDWN_EN(io_reg); + REG_SET_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } else { - PIN_PULLDWN_DIS(io_reg); + REG_CLR_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); } ESP_LOGI(GPIO_TAG, "GPIO[%d]| InputEn: %d| OutputEn: %d| OpenDrain: %d| Pullup: %d| Pulldown: %d| Intr:%d ", io_num, input_en, output_en, od_en, pu_en, pd_en, pGPIOConfig->intr_type); gpio_set_intr_type(io_num, pGPIOConfig->intr_type); diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 73efeaa342..5dad11f2ff 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -117,6 +117,29 @@ extern const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT]; #define GPIO_IS_VALID_GPIO(gpio_num) ((gpio_num < GPIO_PIN_COUNT && GPIO_PIN_MUX_REG[gpio_num] != 0)) //to decide whether it is a valid GPIO number #define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((GPIO_IS_VALID_GPIO(gpio_num)) && (gpio_num < 34)) //to decide whether it can be a valid GPIO number of output mode +typedef struct { + uint32_t reg; /*!< Register to modify to enable or disable pullups or pulldowns */ + uint32_t pu; /*!< Bit to set or clear in the above register to enable or disable the pullup, respectively */ + uint32_t pd; /*!< Bit to set or clear in the above register to enable or disable the pulldown, respectively */ +} gpio_pu_pd_desc_t; + + +/** + * Per-GPIO pullup/pulldown information + * On the ESP32, some GPIOs need their pullups and pulldowns enabled and disabled in the RTC + * peripheral instead of in the GPIO peripheral. This array documents for every GPIO what bit + * to set or clear. + * + * This array is non-static, so if you need a very quick way of toggling the pull-up/downs, you can just + * do e.g. REG_SET_BIT(gpio_pu_pd_desc[gpio_num].reg, gpio_pu_pd_desc[gpio_num].pu); inline. + * + * ToDo: Functions using the contents of this array will do a read/modify/write on GPIO as well as RTC + * registers. We may need to look into muxes/locks for other code that accesses these RTC registers when we + * write drivers for the RTC stuff. + */ +extern const gpio_pu_pd_desc_t gpio_pu_pd_desc[GPIO_PIN_COUNT]; + + typedef enum { GPIO_NUM_0 = 0, /*!< GPIO0, input and output */ GPIO_NUM_1 = 1, /*!< GPIO1, input and output */ @@ -220,7 +243,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig); /** * @brief GPIO set interrupt trigger type * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set the trigger type of e.g. of GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param intr_type Interrupt type, select from gpio_int_type_t * * @return @@ -233,7 +256,7 @@ esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type); /** * @brief Enable GPIO module interrupt signal * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to enable an interrupt on e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return * - ESP_OK Success @@ -245,7 +268,7 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num); /** * @brief Disable GPIO module interrupt signal * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to disable the interrupt of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return * - ESP_OK success @@ -257,7 +280,7 @@ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); /** * @brief GPIO set output level * - * @param gpio_num GPIO number. If you want to set output level of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set the output level of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param level Output level. 0: low ; 1: high * * @return @@ -270,7 +293,7 @@ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); /** * @brief GPIO get input level * - * @param gpio_num GPIO number. If you want to get level of pin GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to get the logic level of e.g. pin GPIO16, gpio_num should be GPIO_NUM_16 (16); * * @return * - 0 the GPIO input level is 0 @@ -284,7 +307,7 @@ int gpio_get_level(gpio_num_t gpio_num); * * Configure GPIO direction,such as output_only,input_only,output_and_input * - * @param gpio_num Configure GPIO pins number, it should be GPIO number. If you want to set direction of GPIO16, gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num Configure GPIO pins number, it should be GPIO number. If you want to set direction of e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param mode GPIO direction * * @return @@ -299,7 +322,7 @@ esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode); * * User this Function,configure GPIO pull mode,such as pull-up,pull-down * - * @param gpio_num GPIO number. If you want to set pull up or down mode for GPIO16,gpio_num should be GPIO_NUM_16 (16); + * @param gpio_num GPIO number. If you want to set pull up or down mode for e.g. GPIO16, gpio_num should be GPIO_NUM_16 (16); * @param pull GPIO pull up/down mode. * * @return @@ -354,6 +377,53 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); */ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg); + + +/** + * @brief Enable pull-up on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pullup_en(gpio_num_t gpio_num); + +/** + * @brief Disable pull-up on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pullup_dis(gpio_num_t gpio_num); + +/** + * @brief Enable pull-down on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pulldown_en(gpio_num_t gpio_num); + +/** + * @brief Disable pull-down on GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); + + /** * *************** ATTENTION ********************/ /** diff --git a/components/esp32/gdbstub.c b/components/esp32/gdbstub.c index a43793f835..819944a902 100644 --- a/components/esp32/gdbstub.c +++ b/components/esp32/gdbstub.c @@ -25,6 +25,7 @@ #include "soc/io_mux_reg.h" #include "esp_gdbstub.h" +#include "driver/gpio.h" //Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which //implies a minimum size of about 320 bytes. @@ -354,7 +355,7 @@ static int gdbReadCommand() { void esp_gdbstub_panic_handler(XtExcFrame *frame) { dumpHwToRegfile(frame); //Make sure txd/rxd are enabled - PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); + gpio_pullup_dis(1); PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); diff --git a/components/esp32/include/soc/io_mux_reg.h b/components/esp32/include/soc/io_mux_reg.h index 208a60703a..de8fe7ec99 100644 --- a/components/esp32/include/soc/io_mux_reg.h +++ b/components/esp32/include/soc/io_mux_reg.h @@ -34,10 +34,41 @@ #define PIN_INPUT_ENABLE(PIN_NAME) SET_PERI_REG_MASK(PIN_NAME,FUN_IE) #define PIN_INPUT_DISABLE(PIN_NAME) CLEAR_PERI_REG_MASK(PIN_NAME,FUN_IE) #define PIN_SET_DRV(PIN_NAME, drv) REG_SET_FIELD(PIN_NAME, FUN_DRV, (drv)); -#define PIN_PULLUP_DIS(PIN_NAME) REG_CLR_BIT(PIN_NAME, FUN_PU) -#define PIN_PULLUP_EN(PIN_NAME) REG_SET_BIT(PIN_NAME, FUN_PU) -#define PIN_PULLDWN_DIS(PIN_NAME) REG_CLR_BIT(PIN_NAME, FUN_PD) -#define PIN_PULLDWN_EN(PIN_NAME) REG_SET_BIT(PIN_NAME, FUN_PD) + +/* + * @attention + * The PIN_PULL[UP|DWN]_[EN|DIS]() functions used to exist as macros in previous SDK versions. + * Unfortunately, however, they do not work for some GPIOs on the ESP32 chip, which needs pullups + * and -downs turned on and off through RTC registers. The functions still exist for compatibility + * with older code, but are marked as deprecated in order to generate a warning. + * Please replace them in this fashion: (make sure to include driver/gpio.h as well) + * PIN_PULLUP_EN(GPIO_PIN_MUX_REG[x]) -> gpio_pullup_en(x) + * PIN_PULLUP_DIS(GPIO_PIN_MUX_REG[x]) -> gpio_pullup_dis(x) + * PIN_PULLDWN_EN(GPIO_PIN_MUX_REG[x]) -> gpio_pulldown_en(x) + * PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[x]) -> gpio_pulldown_dis(x) + * +*/ +static inline void __attribute__ ((deprecated)) PIN_PULLUP_DIS(uint32_t PIN_NAME) +{ + REG_CLR_BIT(PIN_NAME, FUN_PU); +} + +static inline void __attribute__ ((deprecated)) PIN_PULLUP_EN(uint32_t PIN_NAME) +{ + REG_SET_BIT(PIN_NAME, FUN_PU); +} + +static inline void __attribute__ ((deprecated)) PIN_PULLDWN_DIS(uint32_t PIN_NAME) +{ + REG_CLR_BIT(PIN_NAME, FUN_PD); +} + +static inline void __attribute__ ((deprecated)) PIN_PULLDWN_EN(uint32_t PIN_NAME) +{ + REG_SET_BIT(PIN_NAME, FUN_PD); +} + + #define PIN_FUNC_SELECT(PIN_NAME, FUNC) REG_SET_FIELD(PIN_NAME, MCU_SEL, FUNC) #define PIN_FUNC_GPIO 2 From 858fe9815b37068bef9abd8b99e534876d7e3caa Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 15 Nov 2016 11:12:58 +0800 Subject: [PATCH 209/285] feature/openssl: add openssl client demo --- examples/09_openssl/README.md | 21 - examples/09_openssl/main/Kconfig.projbuild | 27 -- examples/09_openssl/main/openssl_demo.c | 430 ------------------ examples/09_openssl/main/openssl_demo.h | 66 --- .../Makefile | 2 +- examples/09_openssl_client/README.md | 16 + .../09_openssl_client/main/Kconfig.projbuild | 28 ++ .../main/component.mk | 2 +- .../09_openssl_client/main/openssl_client.c | 235 ++++++++++ .../09_openssl_client/main/openssl_client.h | 34 ++ 10 files changed, 315 insertions(+), 546 deletions(-) delete mode 100644 examples/09_openssl/README.md delete mode 100644 examples/09_openssl/main/Kconfig.projbuild delete mode 100644 examples/09_openssl/main/openssl_demo.c delete mode 100644 examples/09_openssl/main/openssl_demo.h rename examples/{09_openssl => 09_openssl_client}/Makefile (83%) create mode 100644 examples/09_openssl_client/README.md create mode 100644 examples/09_openssl_client/main/Kconfig.projbuild rename examples/{09_openssl => 09_openssl_client}/main/component.mk (86%) create mode 100644 examples/09_openssl_client/main/openssl_client.c create mode 100644 examples/09_openssl_client/main/openssl_client.h diff --git a/examples/09_openssl/README.md b/examples/09_openssl/README.md deleted file mode 100644 index 54b0133420..0000000000 --- a/examples/09_openssl/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Openssl Example - -The Example contains of OpenSSL client and server demo. - -First you should config the project by "menuconfig": - Example Configuration -> - 1. Openssl demo: select your demo (client or server) - 2. WiFi SSID: you own wifi, which you pc is connected to alse. - 3. WiFi Password: wifi password - -If you want to test the OpenSSL client demo: - 1. compile the code and load the firmware - 2. you can see it will download the "www.baidu.com" main page and print the context - -IF you want to test the openSSL client demo: - 1. compile the code and load the firmware - 2. You should input the context of "https://192.168.17.128", the IP of your module may not be 192.168.17.128, you should input your module's IP - 3. You may see that it shows the website is not able to be trusted, but you should select that "go on to visit it" - 4. You should wait for a moment until your see the "OpenSSL server demo!" in your IE page - -See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/09_openssl/main/Kconfig.projbuild b/examples/09_openssl/main/Kconfig.projbuild deleted file mode 100644 index b42529ca19..0000000000 --- a/examples/09_openssl/main/Kconfig.projbuild +++ /dev/null @@ -1,27 +0,0 @@ -menu "Example Configuration" - -choice OPENSSL_DEMO - prompt "Openssl demo" - default OPENSSL_CLIENT_DEMO - help - Openssl test demo mode, client or server. - -config OPENSSL_CLIENT_DEMO - bool "client demo" -config OPENSSL_SERVER_DEMO - bool "server demon" -endchoice - -config WIFI_SSID - string "WiFi SSID" - default "myssid" - help - SSID (network name) for the example to connect to. - -config WIFI_PASSWORD - string "WiFi Password" - default "myssid" - help - WiFi password (WPA or WPA2) for the example to use. - -endmenu \ No newline at end of file diff --git a/examples/09_openssl/main/openssl_demo.c b/examples/09_openssl/main/openssl_demo.c deleted file mode 100644 index d541b26253..0000000000 --- a/examples/09_openssl/main/openssl_demo.c +++ /dev/null @@ -1,430 +0,0 @@ -#include -#include - -#include "openssl_demo.h" -#include "openssl/ssl.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "esp_types.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" - -#include "lwip/sockets.h" -#include "lwip/api.h" - -#include "nvs_flash.h" - - -#define os_printf(fmt, ...) ESP_LOGI("openssl_demo", fmt, ##__VA_ARGS__) - -#define IP_ADDR(ip) ip.u_addr.ip4.addr - -/* The examples use simple WiFi configuration that you can set via - 'make menuconfig'. - - If you'd rather not, just change the below entries to strings with - the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" -*/ -#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID -#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD - -#define OPENSSL_DEMO_THREAD_STACK_WORDS 8192 -#define OPENSSL_DEMO_THREAD_PRORIOTY 6 - -#define OPENSSL_DEMO_FRAGMENT_SIZE 8192 - -#define OPENSSL_DEMO_RECV_BUF_LEN 1024 - -#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 - -#ifdef CONFIG_OPENSSL_CLIENT_DEMO - -#define OPENSSL_DEMO_THREAD_NAME "ssl_client" - -#define OPENSSL_DEMO_TARGET_NAME "www.baidu.com" -#define OPENSSL_DEMO_TARGET_TCP_PORT 443 - -#define OPENSSL_DEMO_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" - -LOCAL void openssl_demo_thread(void *p) -{ - int ret; - - SSL_CTX *ctx; - SSL *ssl; - - int socket; - struct sockaddr_in sock_addr; - - ip_addr_t target_ip; - - int recv_bytes = 0; - - LOCAL char send_data[] = OPENSSL_DEMO_REQUEST; - LOCAL int send_bytes = sizeof(send_data); - - LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - os_printf("OpenSSL demo thread start\n"); - - do { - ret = netconn_gethostbyname(OPENSSL_DEMO_TARGET_NAME, &target_ip); - } while(ret); - os_printf("get target IP is %d.%d.%d.%d\n", - (unsigned char)((IP_ADDR(target_ip) & 0x000000ff) >> 0), - (unsigned char)((IP_ADDR(target_ip) & 0x0000ff00) >> 8), - (unsigned char)((IP_ADDR(target_ip) & 0x00ff0000) >> 16), - (unsigned char)((IP_ADDR(target_ip) & 0xff000000) >> 24)); - - os_printf("create SSL context ......"); - ctx = SSL_CTX_new(TLSv1_1_client_method()); - if (!ctx) { - os_printf("failed\n"); - goto failed1; - } - os_printf("OK\n"); - - /** - * The openssl does not support "SSL_CTX_set_default_read_buffer_len" - * at the platform of ESP32 esp_idf now. - * - * So you should not care it now. And We my let it work later. - */ - os_printf("set SSL context read buffer size ......"); - SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); - ret = 0; - if (ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - - os_printf("bind socket ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - os_printf("failed\n"); - goto failed4; - } - os_printf("OK\n"); - - os_printf("socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = IP_ADDR(target_ip); - sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); - ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - os_printf("failed\n"); - goto failed5; - } - os_printf("OK\n"); - - os_printf("create SSL ......"); - ssl = SSL_new(ctx); - if (!ssl) { - os_printf("failed\n"); - goto failed6; - } - os_printf("OK\n"); - - SSL_set_fd(ssl, socket); - - os_printf("SSL connected to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_connect(ssl); - if (!ret) { - os_printf("failed, return [-0x%x]\n", -ret); - goto failed7; - } - os_printf("OK\n"); - - os_printf("send https request to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_write(ssl, send_data, send_bytes); - if (ret <= 0) { - os_printf("failed\n"); - goto failed8; - } - os_printf("OK\n\n"); - - do { - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - recv_bytes += ret; - os_printf("%s", recv_buf); - } while (1); - - os_printf("\r\ntotaly read %d bytes data from %s ......\n", recv_bytes, OPENSSL_DEMO_TARGET_NAME); - -failed8: - SSL_shutdown(ssl); -failed7: - SSL_free(ssl); - ssl = NULL; -failed6: -failed5: -failed4: - close(socket); - socket = -1; -failed3: -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - - os_printf("task exit\n"); - - return ; -} - -#elif defined(CONFIG_OPENSSL_SERVER_DEMO) - -#define OPENSSL_DEMO_THREAD_NAME "openssl_server" - -#define OPENSSL_DEMO_CLIENT_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" - -#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/html\r\n" \ - "Content-Length: 98\r\n" \ - "\r\n" \ - "\r\n" \ - "openSSL demo\r\n" \ - "OpenSSL server demo!\r\n" \ - "\r\n" \ - "\r\n" - -LOCAL void openssl_demo_thread(void *p) -{ - int ret; - - SSL_CTX *ctx; - SSL *ssl; - - int socket, new_socket; - socklen_t addr_len; - struct sockaddr_in sock_addr; - - LOCAL char send_data[] = OPENSSL_DEMO_SERVER_ACK; - LOCAL int send_bytes = sizeof(send_data); - - LOCAL char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - os_printf("server SSL context create ......"); - ctx = SSL_CTX_new(SSLv3_server_method()); - if (!ctx) { - os_printf("failed\n"); - goto failed1; - } - os_printf("OK\n"); - - /** - * The openssl does not support "SSL_CTX_set_default_read_buffer_len" - * at the platform of ESP32 esp_idf now. - * - * So you should not care it now. And We my let it work later. - */ - os_printf("server SSL context set fragment ......"); - SSL_CTX_set_default_read_buffer_len(ctx, OPENSSL_DEMO_FRAGMENT_SIZE); - ret = 0; - if (ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server SSL context set own certification......"); - ret = SSL_CTX_use_certificate_ASN1(ctx, cert_bytes, cert_ctx); - if (!ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server SSL context set private key......"); - ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, key_ctx, key_bytes); - if (!ret) { - os_printf("failed, return %d\n", ret); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - os_printf("failed\n"); - goto failed2; - } - os_printf("OK\n"); - - os_printf("server socket bind ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - - os_printf("server socket listen ......"); - ret = listen(socket, 32); - if (ret) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - -reconnect: - os_printf("server SSL create ......"); - ssl = SSL_new(ctx); - if (!ssl) { - os_printf("failed\n"); - goto failed3; - } - os_printf("OK\n"); - - os_printf("server socket accept client ......"); - new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); - if (new_socket < 0) { - os_printf("failed, return [-0x%x]\n", -new_socket); - goto failed4; - } - os_printf("OK\n"); - - SSL_set_fd(ssl, new_socket); - - os_printf("server SSL accept client ......"); - ret = SSL_accept(ssl); - if (!ret) { - os_printf("failed\n"); - goto failed5; - } - os_printf("OK\n"); - - os_printf("server SSL read message ......"); - do { - memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - if (strstr(recv_buf, "GET / HTTP/1.1")) { - SSL_write(ssl, send_data, send_bytes); - break; - } - } while (1); - - os_printf("result %d\n", ret); - - SSL_shutdown(ssl); -failed5: - close(new_socket); - new_socket = -1; -failed4: - SSL_free(ssl); - ssl = NULL; - goto reconnect; -failed3: - close(socket); - socket = -1; -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - - return ; -} - -#else - #error "you must choose the right demo type" -#endif - -LOCAL void demo_init(void) -{ - int ret = pdFALSE; - -#if defined(CONFIG_OPENSSL_CLIENT_DEMO) || defined(CONFIG_OPENSSL_SERVER_DEMO) - xTaskHandle openssl_handle; - - ret = xTaskCreate(openssl_demo_thread, - OPENSSL_DEMO_THREAD_NAME, - OPENSSL_DEMO_THREAD_STACK_WORDS, - NULL, - OPENSSL_DEMO_THREAD_PRORIOTY, - &openssl_handle); -#endif - - if (ret != pdPASS) { - os_printf("create thread %s failed\n", OPENSSL_DEMO_THREAD_NAME); - return ; - } -} - -LOCAL esp_err_t wifi_event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - demo_init(); - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - break; - default: - break; - } - return ESP_OK; -} - -LOCAL void wifi_conn_init(void) -{ - tcpip_adapter_init(); - ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - wifi_config_t wifi_config = { - .sta = { - .ssid = EXAMPLE_WIFI_SSID, - .password = EXAMPLE_WIFI_PASS, - }, - }; - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - os_printf("start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - -void app_main(void) -{ - nvs_flash_init(); - wifi_conn_init(); -} - diff --git a/examples/09_openssl/main/openssl_demo.h b/examples/09_openssl/main/openssl_demo.h deleted file mode 100644 index 89cd264b46..0000000000 --- a/examples/09_openssl/main/openssl_demo.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef _OPENSSL_DEMO_H_ -#define _OPENSSL_DEMO_H_ - -const static unsigned char key_ctx[] = - "-----BEGIN PRIVATE KEY-----\r\n" - "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQyyF0WBb2XbkL\r\n" - "wYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijBR0Rr\r\n" - "0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe6eFG\r\n" - "AGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wHzywA\r\n" - "DvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwbrdcD\r\n" - "HNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gmpqfh\r\n" - "CwEPoKCNAgMBAAECggEBAIVr+LHXBL1kQLZhbiKC5t1FrMY8FLKYGM3JCevciMlk\r\n" - "lhIXwR7p29vsRYZfHBv7zWWyI9/C04JG31MiGOMaCbBxfnA2HBrVEqFwwxqnKVi8\r\n" - "CxzwGQkfwu3Y9j7TEj0PipfQYo+aKzmDrN/FrXnHjyEJryxAQbAZPVLW+Z7OR41R\r\n" - "ZOwtZLeVqmbeARGpu2Xd9SKAhbjdLSz96IdUcNrwbP/lzUgrKaiUioBMVFfIG5ce\r\n" - "4Mm2seCwaWxI8k24q0keSjsjV+5IxatVUNtJ9vYv6Tzo+3oqGvPeUBO7w9xhbLKf\r\n" - "jw1uEykcs0wcftWb1iB7r78bMPZ/KYhnSFsjT+vnIOECgYEA9LM5p63sn6OWrULd\r\n" - "doBBLaIw1ZK9rDUFLLRwv0e6+7tuuB08ok6D7PSfOpx0bb10lIx7epOE8ogGSmLS\r\n" - "w0rMbmcKAlTLAJ/0nX1ierZkb+Ay21F398tKL3igEfnaB9CzuOHF8XhbsTqeGFDJ\r\n" - "HFBMXxTbo4kfkUmZNYxwTombzkkCgYEA2m9teqjEOVZItqghOicWZ68JhWxBJFme\r\n" - "oSfzJKLd8atfUOnBLqAhPzvg1PvdIxjLWBqy28tEJf+jdSQCNe9BmhlptOwbFrJy\r\n" - "IyCXj6QTApSKTxyzIjMvzQkv1m8CxeCq5T64hvJ2++i7dlhumh21c7oL8aLeTnoe\r\n" - "AG1dBLJ9UCUCgYAhSlDJsyvB/Ct/nt0qRgCYCLzEEZakWwI9Nr8wBr41iSZi/fdF\r\n" - "zZC9J/qRqr9cPq4hl4sk/fTUWhUhAZjS4NY3HuWJs6d6ikhpNKm1MCMx5TqGA+ti\r\n" - "VtHc63g7edZjwczxliWr2EgBMIxZmoQByhrZxKis8vbMeUrSsiyFQstjoQKBgD3k\r\n" - "2Paqn39Hra7kERYzQSv78wy1UfgE1OgBM+orpAv4bTe2JKEbipAqXVi8TTrGqce7\r\n" - "OPcCr7q8pwpoO6AgvUv263byd/KEecbuU0PGUASpJk+oaDHGo0LL2Zw/NF/xezsd\r\n" - "/JdwWLqkhYnRIPXWeTXjf8LmTWubOqkQVA0irlNpAoGAJ+9N/NF3XAW0BroiVYLZ\r\n" - "p0Btcgt+b4LWrBlm0XqHhzKUlqhfibAr3OtUkFjo/509ncYPiuOzVSNosyjXFJ86\r\n" - "2kQ88fB3eeLnBAcbBXQKiOBPU2y6bCCfgdo+JEOK/cxVslaxMAyKSnFi9gdgzScd\r\n" - "k+hOlkflXQVkic3W358kFto=\r\n" - "-----END PRIVATE KEY-----\r\n" - ; -static int key_bytes = sizeof(key_ctx); - -const static unsigned char cert_ctx[] = - "-----BEGIN CERTIFICATE-----\r\n" - "MIID7jCCAtYCAQEwDQYJKoZIhvcNAQELBQAwgbwxCzAJBgNVBAYTAkNOMRAwDgYD\r\n" - "VQQIDAdKaWFuZ3N1MQ0wCwYDVQQHDARXdXhpMSYwJAYDVQQKDB1Fc3ByZXNzaWYg\r\n" - "Um9vdCBSU0EyMDQ4IHNoYTI1NjEcMBoGA1UECwwTUm9vdCBSU0EyMDQ4IHNoYTI1\r\n" - "NjEfMB0GA1UEAwwWcm9vdGNlcnQuZXNwcmVzc2lmLmNvbTElMCMGCSqGSIb3DQEJ\r\n" - "ARYWcm9vdGNlcnRAZXNwcmVzc2lmLmNvbTAeFw0xNjA2MjgwMjMxMjlaFw0yNjA2\r\n" - "MjYwMjMxMjlaMIG8MQswCQYDVQQGEwJDTjEQMA4GA1UECAwHSmlhbmdzdTENMAsG\r\n" - "A1UEBwwEV3V4aTEmMCQGA1UECgwdRXNwcmVzc2lmIFJvb3QgUlNBMjA0OCBzaGEy\r\n" - "NTYxHDAaBgNVBAsME1Jvb3QgUlNBMjA0OCBzaGEyNTYxHzAdBgNVBAMMFnJvb3Rj\r\n" - "ZXJ0LmVzcHJlc3NpZi5jb20xJTAjBgkqhkiG9w0BCQEWFnJvb3RjZXJ0QGVzcHJl\r\n" - "c3NpZi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQyyF0WBb2\r\n" - "XbkLwYFgyoPOanYvbb/qwbAkGf1zSPX35xruZmjszjcverMoyF6x2MBxD3gP1ijB\r\n" - "R0Rr0J0CfluABDLkzqpF5smOVX9k8W7ePClm91NhcASuF+CaZOe6B+6vOYShYjhe\r\n" - "6eFGAGk8SP4zSrG2XHNKXlR3w8duK9fyOOZLWRjL3T6+++HEaly1p4ujKZhrm5wH\r\n" - "zywADvjDdvIWBCW1Z+8j7Q9qUITjlsDWHjrXCpyEfclE1WQxTP/W7rBLxNVxTfwb\r\n" - "rdcDHNrKTOXtN+oDmCruvmBnTkz9x4Te6wJuvtFd0fBtW1kWsMzomvOlKmvHo0gm\r\n" - "pqfhCwEPoKCNAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABTYZLiFHq51lqaa0nHI\r\n" - "aDMAb29DfO93fqp+oHZYO4xKyEeLr8EhD39GjnQmhz710wO0TBCYV7nD+xnJ1h5F\r\n" - "IbQUAQZO9NIy3ns4mYVRUWjnWYAo+evGeKgRrxvh7sjNLPBPzs9tg/u7XjBp/nor\r\n" - "8JnnFFT0wXPyi/qg8J3QutqJvWRQGRRx2AP93F44+Zcj7ReFMVSmOXyzT4aNJL0+\r\n" - "Ls+baKwA4pnyVRoAaKbs/JYDgd0/DunuktVKuhyvK/qOGjJSRLPhdrXbvSAegpiM\r\n" - "4xIm6ZWKtTv8VvkGgXUVQ7RpruP6nV6506gDcUgecbEq7H2VDhEzUYcMmGCUQZlG\r\n" - "sJ8=\r\n" - "-----END CERTIFICATE-----\r\n" - ; -static int cert_bytes = sizeof(cert_ctx); - - - -#endif diff --git a/examples/09_openssl/Makefile b/examples/09_openssl_client/Makefile similarity index 83% rename from examples/09_openssl/Makefile rename to examples/09_openssl_client/Makefile index 8987be554c..7e2f4fe7f8 100644 --- a/examples/09_openssl/Makefile +++ b/examples/09_openssl_client/Makefile @@ -3,7 +3,7 @@ # project subdirectory. # -PROJECT_NAME := openssl +PROJECT_NAME := openssl_client include $(IDF_PATH)/make/project.mk diff --git a/examples/09_openssl_client/README.md b/examples/09_openssl_client/README.md new file mode 100644 index 0000000000..85d9575d6e --- /dev/null +++ b/examples/09_openssl_client/README.md @@ -0,0 +1,16 @@ +# Openssl Example + +The Example contains of OpenSSL client demo. + +First you should config the project by "make menuconfig": + Example Configuration -> + 1. Target Domain : the domain that you want to connect to, and default is "www.baidu.com". + 2. Target port number : the port number of the target domain, and default is 443. + 3. WiFi SSID : you own wifi, which you pc is connected to alse, and default is "myssid". + 4. WiFi Password : wifi password, and default is "mypassword" + +If you want to test the OpenSSL client demo: + 1. compile the code and load the firmware + 2. open the UART TTY, then you can see it print the context of target domain + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/09_openssl_client/main/Kconfig.projbuild b/examples/09_openssl_client/main/Kconfig.projbuild new file mode 100644 index 0000000000..1767923ad1 --- /dev/null +++ b/examples/09_openssl_client/main/Kconfig.projbuild @@ -0,0 +1,28 @@ +menu "Example Configuration" + +config TARGET_DOMAIN + string "Target Domain" + default "www.baidu.com" + help + Target domain for the example to connect to. + +config TARGET_PORT_NUMBER + int "Target port number" + range 0 65535 + default 433 + help + Target port number for the example to connect to. + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/09_openssl/main/component.mk b/examples/09_openssl_client/main/component.mk similarity index 86% rename from examples/09_openssl/main/component.mk rename to examples/09_openssl_client/main/component.mk index 24356f23ed..973de1d6f0 100644 --- a/examples/09_openssl/main/component.mk +++ b/examples/09_openssl_client/main/component.mk @@ -1,7 +1,7 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, # this will take the sources in the src/ directory, compile them and link them into # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c new file mode 100644 index 0000000000..9d4b8b395d --- /dev/null +++ b/examples/09_openssl_client/main/openssl_client.c @@ -0,0 +1,235 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_client.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_types.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" + +#include "nvs_flash.h" +#include "tcpip_adapter.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +void openssl_demo_thread(void *p) +{ + int ret; + SSL_CTX *ctx; + SSL *ssl; + int socket; + struct sockaddr_in sock_addr; + struct hostent *hp; + struct ip4_addr *ip4_addr; + + int recv_bytes = 0; + char send_data[] = OPENSSL_DEMO_REQUEST; + int send_bytes = sizeof(send_data); + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + ESP_LOGI(TAG, "OpenSSL demo thread start OK"); + + ESP_LOGI(TAG, "get target IP address"); + hp = gethostbyname(OPENSSL_DEMO_TARGET_NAME); + if (!hp) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ip4_addr = (struct ip4_addr *)hp->h_addr; + ESP_LOGI(TAG, IPSTR, IP2STR(ip4_addr)); + + ESP_LOGI(TAG, "create SSL context ......"); + ctx = SSL_CTX_new(TLSv1_1_client_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "bind socket ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = ip4_addr->addr; + sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); + ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create SSL ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, socket); + + ESP_LOGI(TAG, "SSL connected to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_connect(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed " ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "send https request to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_write(ssl, send_data, send_bytes); + if (ret <= 0) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + do { + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + recv_bytes += ret; + ESP_LOGI(TAG, "%s", recv_buf); + } while (1); + + ESP_LOGI(TAG, "totaly read %d bytes data from %s ......", recv_bytes, OPENSSL_DEMO_TARGET_NAME); + +failed5: + SSL_shutdown(ssl); +failed4: + SSL_free(ssl); + ssl = NULL; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + extern void openssl_demo_thread(void *p); + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + return ; + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/09_openssl_client/main/openssl_client.h b/examples/09_openssl_client/main/openssl_client.h new file mode 100644 index 0000000000..5bc69a0aca --- /dev/null +++ b/examples/09_openssl_client/main/openssl_client.h @@ -0,0 +1,34 @@ +#ifndef _OPENSSL_DEMO_H_ +#define _OPENSSL_DEMO_H_ + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +/* The examples use domain of "www.baidu.com" and port number of 433 that + you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define OPENSSL_DEMO_TARGET_NAME "www.baidu.com" + and ie #define OPENSSL_DEMO_TARGET_TCP_PORT 433 +*/ +#define OPENSSL_DEMO_TARGET_NAME CONFIG_TARGET_DOMAIN +#define OPENSSL_DEMO_TARGET_TCP_PORT CONFIG_TARGET_PORT_NUMBER + +#define OPENSSL_DEMO_REQUEST "{\"path\": \"/v1/ping/\", \"method\": \"GET\"}\r\n" + +#define OPENSSL_DEMO_THREAD_NAME "OpenSSL_demo" +#define OPENSSL_DEMO_THREAD_STACK_WORDS 10240 +#define OPENSSL_DEMO_THREAD_PRORIOTY 8 + +#define OPENSSL_DEMO_RECV_BUF_LEN 1024 + +#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 + +#endif + From adb4be859cb74cefbe81527a23effaeb05abe355 Mon Sep 17 00:00:00 2001 From: Xia Xiao Tian Date: Tue, 15 Nov 2016 11:19:15 +0800 Subject: [PATCH 210/285] wps: update wifi lib -- add wps lib --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 84af0ed366..01f5c068e1 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 84af0ed366e1ba38984f7df517a77f8ec4fa27ed +Subproject commit 01f5c068e1ac3968add98439ee2f1748b9e391fa From fa1d5bfbc78797ac261ab5d63662cdbf1b68f254 Mon Sep 17 00:00:00 2001 From: liuhan Date: Thu, 3 Nov 2016 13:30:32 +0800 Subject: [PATCH 211/285] tcpip_adapter: add set hostname interface --- .../tcpip_adapter/include/tcpip_adapter.h | 13 +++++++++ components/tcpip_adapter/tcpip_adapter_lwip.c | 28 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index e847016884..45d6ade305 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -372,6 +372,19 @@ wifi_interface_t tcpip_adapter_get_wifi_if(void *dev); */ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapter_sta_list_t *tcpip_sta_list); +#define TCPIP_HOSTNAME_MAX_SIZE 31 +/** + * @brief Set the hostname to the interface + * + * @param[in] tcpip_if: the interface which we will set the hostname + * @param[in] hostname: the host name for set the interfce + * + * @return ESP_OK:success + * ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY:interface status error + * ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS:parameter error + */ +esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *hostname); + #ifdef __cplusplus } #endif diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 9b6e9d94fa..3edc90509e 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -607,4 +607,32 @@ esp_err_t tcpip_adapter_get_sta_list(wifi_sta_list_t *wifi_sta_list, tcpip_adapt return ESP_OK; } +esp_err_t tcpip_adapter_set_hostname(tcpip_adapter_if_t tcpip_if, const char *hostname) +{ + struct netif *p_netif; + static char hostinfo[TCPIP_HOSTNAME_MAX_SIZE + 1]; + + if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || hostname == NULL) { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } + + if (strlen(hostname) >= TCPIP_HOSTNAME_MAX_SIZE) { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } + + p_netif = esp_netif[tcpip_if]; + if (p_netif != NULL) { + if (netif_is_up(p_netif)) { + return ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY; + } else { + memset(hostinfo, 0, sizeof(hostinfo)); + memcpy(hostinfo, hostname, strlen(hostname)); + p_netif->hostname = hostinfo; + return ESP_OK; + } + } else { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } +} + #endif From 9a2887c458c20b05ffad8f7ef8dbd54a20b97920 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 15 Nov 2016 12:10:02 +0800 Subject: [PATCH 212/285] Add comments/headers according to merge request comments --- components/esp32/freertos_hooks.c | 15 ++++++++++ components/esp32/include/esp_freertos_hooks.h | 29 +++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/components/esp32/freertos_hooks.c b/components/esp32/freertos_hooks.c index 50ebd3d054..d59a20363d 100644 --- a/components/esp32/freertos_hooks.c +++ b/components/esp32/freertos_hooks.c @@ -1,3 +1,18 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + #include #include #include diff --git a/components/esp32/include/esp_freertos_hooks.h b/components/esp32/include/esp_freertos_hooks.h index 1bb5ab194a..45a1649723 100644 --- a/components/esp32/include/esp_freertos_hooks.h +++ b/components/esp32/include/esp_freertos_hooks.h @@ -1,9 +1,27 @@ -#ifndef ESP_FREERTOS_HOOKS_H -#define ESP_FREERTOS_HOOKS_H +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_FREERTOS_HOOKS_H__ +#define __ESP_FREERTOS_HOOKS_H__ #include #include "esp_err.h" +#ifdef __cplusplus +extern "C" +{ +#endif /* Definitions for the tickhook and idlehook callbacks @@ -17,6 +35,9 @@ typedef void (*esp_freertos_tick_cb_t)(); * sleep until an interrupt (or FreeRTOS tick) happens and false * if it should be called again as fast as possible. * + * @warning Idle callbacks MUST NOT, UNDER ANY CIRCUMSTANCES, CALL + * A FUNCTION THAT MIGHT BLOCK. + * * @param esp_freertos_idle_cb_t new_idle_cb : Callback to be called * * @return ESP_OK : Callback registered @@ -54,5 +75,9 @@ void esp_deregister_freertos_idle_hook(esp_freertos_idle_cb_t old_idle_cb); */ void esp_deregister_freertos_tick_hook(esp_freertos_tick_cb_t old_tick_cb); +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file From a8a51a2786a3b2f1814a111af10f66d45f47a4c1 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 15 Nov 2016 13:47:51 +0800 Subject: [PATCH 213/285] bugfix/uart_isr_switch_context: add switching context in uart ISR. 1. add switching context in uart ISR 2. remove duplicated #include in uart.c 3. modify example in uart.h(will later add examples to idf/examples) --- components/driver/include/driver/uart.h | 13 ++++++---- components/driver/uart.c | 34 +++++++++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 7dccf1666c..906bdcb9e9 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -686,13 +686,14 @@ esp_err_t uart_flush(uart_port_t uart_num); * { * int uart_num = (int)pvParameters; * uart_event_t event; - * uint8_t dtmp[1000]; + * size_t size = 1024; + * uint8_t* dtmp = (uint8_t*)malloc(size); * for(;;) { * //Waiting for UART event. * if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { * ESP_LOGI(TAG, "uart[%d] event:", uart_num); * switch(event.type) { - * memset(dtmp, 0, sizeof(dtmp)); + * memset(dtmp, 0, size); * //Event of UART receving data * case UART_DATA: * ESP_LOGI(TAG,"data, len: %d", event.size); @@ -727,6 +728,8 @@ esp_err_t uart_flush(uart_port_t uart_num); * } * } * } + * free(dtmp); + * dtmp = NULL; * vTaskDelete(NULL); * } * @@ -744,13 +747,13 @@ esp_err_t uart_flush(uart_port_t uart_num); * //Set UART parameters * uart_param_config(uart_num, &uart_config); * //Set UART pins,(-1: default pin, no change.) - * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13); + * uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); * //Set UART log level * esp_log_level_set(TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. - * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF); + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue); * //Create a task to handler UART event from ISR - * xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL); + * xTaskCreate(uart_task, "uTask", 1024, (void*)uart_num, 10, NULL); * } * @endcode * diff --git a/components/driver/uart.c b/components/driver/uart.c index d9e3fd64ca..a8d28ff295 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -23,11 +23,9 @@ #include "freertos/task.h" #include "freertos/ringbuf.h" #include "soc/dport_reg.h" -#include "rom/ets_sys.h" #include "soc/uart_struct.h" #include "driver/uart.h" #include "driver/gpio.h" -#include "soc/uart_struct.h" static const char* UART_TAG = "UART"; #define UART_CHECK(a, str, ret) if (!(a)) { \ @@ -458,17 +456,20 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.txfifo_empty = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); if(p_uart->tx_waiting_brk) { - return; + continue; } //TX semaphore will only be used when tx_buf_size is zero. if(p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size == 0) { p_uart->tx_waiting_fifo = false; - xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, NULL); + xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } else { - //We don't use TX ring buffer, because the size if zero. + //We don't use TX ring buffer, because the size is zero. if(p_uart->tx_buf_size == 0) { - return; + continue; } int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt; bool en_tx_flg = false; @@ -492,6 +493,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } //We have saved the data description from the 1st item, return buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } }else if(p_uart->tx_ptr == NULL) { //Update the TX item pointer, we will need this to return item to buffer. p_uart->tx_ptr = (uint8_t*) p_uart->tx_head; @@ -501,7 +505,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } else { //Can not get data from ring buffer, return; - return; + break; } } if(p_uart->tx_len_tot > 0 && p_uart->tx_ptr && p_uart->tx_len_cur > 0) { @@ -516,6 +520,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(p_uart->tx_len_cur == 0) { //Return item to ring buffer. vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } p_uart->tx_head = NULL; p_uart->tx_ptr = NULL; //Sending item done, now we need to send break if there is a record. @@ -529,7 +536,6 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_ena.tx_brk_done = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); p_uart->tx_waiting_brk = 1; - return; } else { //enable TX empty interrupt en_tx_flg = true; @@ -576,6 +582,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } else { uart_event.type = UART_DATA; } + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } else { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.rxfifo_full = 0; @@ -614,6 +623,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) p_uart->tx_waiting_brk = 0; } else { xSemaphoreGiveFromISR(p_uart->tx_brk_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } } else if(uart_intr_status & UART_TX_BRK_IDLE_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); @@ -626,6 +638,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.tx_done = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); xSemaphoreGiveFromISR(p_uart_obj[uart_num]->tx_done_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } else { uart_reg->int_clr.val = uart_intr_status; /*simply clear all other intr status*/ @@ -634,6 +649,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(uart_event.type != UART_EVENT_MAX && p_uart->xQueueUart) { xQueueSendFromISR(p_uart->xQueueUart, (void * )&uart_event, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR() ; + } } uart_intr_status = uart_reg->int_st.val; } From dfaac25a37340a37ae808d2c12c9a37d268cbc48 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 15 Nov 2016 15:04:21 +0800 Subject: [PATCH 214/285] feature/openssl: add openssl server demo and remove some check function --- components/openssl/platform/ssl_pm.c | 4 ---- examples/09_openssl_client/main/component.mk | 2 -- 2 files changed, 6 deletions(-) diff --git a/components/openssl/platform/ssl_pm.c b/components/openssl/platform/ssl_pm.c index 091402cda4..a5986dc3ee 100644 --- a/components/openssl/platform/ssl_pm.c +++ b/components/openssl/platform/ssl_pm.c @@ -90,10 +90,6 @@ int ssl_pm_new(SSL *ssl) if (!ssl_pm) SSL_ERR(ret, failed1, "ssl_mem_zalloc\n"); - if (ssl->ctx->read_buffer_len < 2048 || - ssl->ctx->read_buffer_len > 8192) - return -1; - max_content_len = ssl->ctx->read_buffer_len; mbedtls_net_init(&ssl_pm->fd); diff --git a/examples/09_openssl_client/main/component.mk b/examples/09_openssl_client/main/component.mk index 973de1d6f0..9c21f4a8d4 100644 --- a/examples/09_openssl_client/main/component.mk +++ b/examples/09_openssl_client/main/component.mk @@ -6,5 +6,3 @@ # lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, # please read the ESP-IDF documents if you need to do this. # - -include $(IDF_PATH)/make/component_common.mk From a2b1f4221b39e61d0ad41c55f523b08d4a6a2605 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 15 Nov 2016 15:08:51 +0800 Subject: [PATCH 215/285] feature/openssl: add the openssl server demo --- examples/10_openssl_server/Makefile | 9 + examples/10_openssl_server/README.md | 20 ++ .../10_openssl_server/main/Kconfig.projbuild | 15 + examples/10_openssl_server/main/cacert.pem | 21 ++ examples/10_openssl_server/main/component.mk | 11 + .../10_openssl_server/main/openssl_server.c | 258 ++++++++++++++++++ .../10_openssl_server/main/openssl_server.h | 22 ++ examples/10_openssl_server/main/prvtkey.pem | 27 ++ 8 files changed, 383 insertions(+) create mode 100644 examples/10_openssl_server/Makefile create mode 100644 examples/10_openssl_server/README.md create mode 100644 examples/10_openssl_server/main/Kconfig.projbuild create mode 100644 examples/10_openssl_server/main/cacert.pem create mode 100644 examples/10_openssl_server/main/component.mk create mode 100644 examples/10_openssl_server/main/openssl_server.c create mode 100644 examples/10_openssl_server/main/openssl_server.h create mode 100644 examples/10_openssl_server/main/prvtkey.pem diff --git a/examples/10_openssl_server/Makefile b/examples/10_openssl_server/Makefile new file mode 100644 index 0000000000..f65f11a562 --- /dev/null +++ b/examples/10_openssl_server/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := openssl_server + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md new file mode 100644 index 0000000000..ae5c8da0c7 --- /dev/null +++ b/examples/10_openssl_server/README.md @@ -0,0 +1,20 @@ +# Openssl Example + +The Example contains of OpenSSL server demo. + +First you should configure the project by "make menuconfig": + Example Configuration -> + 1. WiFi SSID: you own wifi that you pc is connected to alse. + 1. WiFi Password: wifi password + +IF you want to test the OpenSSL server demo: + 1. compile the code and load the firmware + 2. input the context of "https://192.168.17.128" into your web browser, the IP of your module may not be 192.168.17.128, you should input your module's IP + 3. You may see that it shows the website is not able to be trusted, but you should select that "go on to visit it" + 4. You should wait for a moment until your see the "OpenSSL server demo!" in your web browser + +Note: + The private key and certification at the example are not trusted by web browser, because they are not created by CA official, just by ourselves. + You can alse create your own private key and ceritification by "openssl at ubuntu or others". + +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/10_openssl_server/main/Kconfig.projbuild b/examples/10_openssl_server/main/Kconfig.projbuild new file mode 100644 index 0000000000..7a9cb97a0e --- /dev/null +++ b/examples/10_openssl_server/main/Kconfig.projbuild @@ -0,0 +1,15 @@ +menu "Example Configuration" + +config WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + +config WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + +endmenu \ No newline at end of file diff --git a/examples/10_openssl_server/main/cacert.pem b/examples/10_openssl_server/main/cacert.pem new file mode 100644 index 0000000000..e09c3989cd --- /dev/null +++ b/examples/10_openssl_server/main/cacert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIJAPMMNobNczaUMA0GCSqGSIb3DQEBBAUAMHQxEzARBgNV +BAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEcMBoGCSqG +SIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNjExMTUwNTA0MThaFw0xOTExMTUwNTA0MThaMHQx +EzARBgNVBAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEc +MBoGCSqGSIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALDjSPDlomepHCzbw4MUrquQAU0xTV4/Npb27k9I5TRVTjIoOs/5hNI2LPFW +e4CREx09ZrT8K3NFOBoSy7bhPAsjGaFxCYYWc9tiX1m5gq3ToVRSmbZ65fE3kvnI +8E/d5VyzA0OMmWbfaolBSTMoWgqRynEaT+z1Eh2yDTzVFy9eov1DdQFUqGDqbH5b +QYvTY5Fyem7UcKWAe2yS0j3H4dVtVBKNY7qV3Px08yGAs5fQFgUwhyB5+qwhvkeL +JdgapGaSTwLgoQKWHbe/lA3NiBIB9hznFUGKo3hmniAvYZbrQcn3tc0l/J4I39v2 +Pm29FAyjWvQyBkGktz2q4elOZYkCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQQFAAOCAQEAJCJ+97oae/FcOLbPpjCpUQnWqYydgSChgalkZNvr4fVp +TnuNg471l0Y2oTJLoWn2YcbPSFVOEeKkU47mpjMzucHHp0zGaW9SdzhZalWwmbgK +q2ijecIbuFHFNedYTk/03K7eaAcjVhD8e0oOJImeLOL6DAFivA1LUnSgXsdGPDtD +zhISsCPTu+cL1j0yP6HBvLeAyb8kaCWJ05RtiVLRANNHQn/keHajJYpMwnEEbJdG +cqN3whfJoGVbZ6isEf2RQJ0pYRnP7uGLW3wGkLWxfdto8uER8HVDx7fZpevLIqGd +1OoSEi3cIJXWBAjx0TLzzhtb6aeIxBJWQqHThtkKdg== +-----END CERTIFICATE----- diff --git a/examples/10_openssl_server/main/component.mk b/examples/10_openssl_server/main/component.mk new file mode 100644 index 0000000000..4a891d52f7 --- /dev/null +++ b/examples/10_openssl_server/main/component.mk @@ -0,0 +1,11 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +COMPONENT_EMBED_TXTFILES := cacert.pem +COMPONENT_EMBED_TXTFILES += prvtkey.pem diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c new file mode 100644 index 0000000000..4fc841ac79 --- /dev/null +++ b/examples/10_openssl_server/main/openssl_server.c @@ -0,0 +1,258 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_server.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_types.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" + +#include "nvs_flash.h" +#include "tcpip_adapter.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/html\r\n" \ + "Content-Length: 98\r\n" \ + "\r\n" \ + "\r\n" \ + "OpenSSL demo\r\n" \ + "OpenSSL server demo!\r\n" \ + "\r\n" \ + "\r\n" + +static void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket, new_socket; + socklen_t addr_len; + struct sockaddr_in sock_addr; + + char send_data[] = OPENSSL_DEMO_SERVER_ACK; + int send_bytes = sizeof(send_data); + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; + + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; + + ESP_LOGI(TAG, "SSL server context create ......"); + ctx = SSL_CTX_new(SSLv3_server_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set own certification......"); + ret = SSL_CTX_use_certificate_ASN1(ctx, cacert_pem_bytes, cacert_pem_start); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set private key......"); + ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, prvtkey_pem_start, prvtkey_pem_bytes); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket bind ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket listen ......"); + ret = listen(socket, 32); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + +reconnect: + ESP_LOGI(TAG, "SSL server create ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket accept client ......"); + new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); + if (new_socket < 0) { + ESP_LOGI(TAG, "failed" ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, new_socket); + + ESP_LOGI(TAG, "SSL server accept client ......"); + ret = SSL_accept(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server read message ......"); + do { + memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + if (strstr(recv_buf, "GET / HTTP/1.1")) { + SSL_write(ssl, send_data, send_bytes); + break; + } + } while (1); + + ESP_LOGI(TAG, "result %d", ret); + + SSL_shutdown(ssl); +failed5: + close(new_socket); + new_socket = -1; +failed4: + SSL_free(ssl); + ssl = NULL; + goto reconnect; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + extern void openssl_demo_thread(void *p); + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + return ; + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/10_openssl_server/main/openssl_server.h b/examples/10_openssl_server/main/openssl_server.h new file mode 100644 index 0000000000..e87f5e482e --- /dev/null +++ b/examples/10_openssl_server/main/openssl_server.h @@ -0,0 +1,22 @@ +#ifndef _OPENSSL_DEMO_H_ +#define _OPENSSL_DEMO_H_ + +/* The examples use simple WiFi configuration that you can set via + 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID +#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD + +#define OPENSSL_DEMO_THREAD_NAME "OpenSSL_demo" +#define OPENSSL_DEMO_THREAD_STACK_WORDS 10240 +#define OPENSSL_DEMO_THREAD_PRORIOTY 8 + +#define OPENSSL_DEMO_RECV_BUF_LEN 1024 + +#define OPENSSL_DEMO_LOCAL_TCP_PORT 443 + +#endif + diff --git a/examples/10_openssl_server/main/prvtkey.pem b/examples/10_openssl_server/main/prvtkey.pem new file mode 100644 index 0000000000..4ead61f6ff --- /dev/null +++ b/examples/10_openssl_server/main/prvtkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsONI8OWiZ6kcLNvDgxSuq5ABTTFNXj82lvbuT0jlNFVOMig6 +z/mE0jYs8VZ7gJETHT1mtPwrc0U4GhLLtuE8CyMZoXEJhhZz22JfWbmCrdOhVFKZ +tnrl8TeS+cjwT93lXLMDQ4yZZt9qiUFJMyhaCpHKcRpP7PUSHbINPNUXL16i/UN1 +AVSoYOpsfltBi9NjkXJ6btRwpYB7bJLSPcfh1W1UEo1jupXc/HTzIYCzl9AWBTCH +IHn6rCG+R4sl2BqkZpJPAuChApYdt7+UDc2IEgH2HOcVQYqjeGaeIC9hlutByfe1 +zSX8ngjf2/Y+bb0UDKNa9DIGQaS3Parh6U5liQIDAQABAoIBAB9K9jp3xXVlO3DM +KBhmbkg3n6NSV4eW00d9w8cO9E1/0eeZql3knJS7tNO1IwApqiIAHM1j1yP7WONz +88oUqpSlzwD6iF7KVhC3pHqxEOdDi0Tpn/viXg+Ab2X1IF5guRTfLnKiyviiCazi +edqtBtDb3d6Icx9Oc7gBKcpbQFDGt++wSOb5L+xhRm9B5B4l/6byikiPeKqIK5tC +SoP9Zr1mvpNoGm1P4LvEunFJcRBqVI010VNwfO9P98oVyzJu9/FZZrQxXoY9JdXF +OM6nbl+hMDM3TkEOda9NvBhImozEAvuc97CaaXyR3XivxMqNqNIb4+syUPa2PCS3 +ZztI5qECgYEA1gbVG6ifpvpbBkDPi3Im8fM3F7FLLrQc48FdFjdMvDhHD9lVKucD +Uaa8PF9dbbvlu2cwMyfBOKSuWaXxRxRsiqiPmTunS1MvPzQcSrGwUrL2AogGucn6 ++NrLQf5P4H5IpkDQ9ih3zwjO6xKFK1WeYnYpHM8qUBtl6q0YFyVBPu0CgYEA05Pn +StWA4D7VSbNnVi6lvFyEOUsTrK3v419598TFiq4eXLq6aV8/CQYzKsSzoG+aOZhX +Li+0uyT5cNzUcXYhTsW1hA/pNhMfxMrYiB1x14zlLp2WRGg4vd/+SxX6d9Yd3acX +7QzPKgdDicXs9QN8ozJOICKvNbUI53AJdATVEY0CgYEAwvpGeoQLrdq1weSZLrg3 +soOX1QW3MDz1dKdbXjnStkWut0mOxR7fbysuoPFf8/ARQcCnsHKvHCMqkpESVWbN +2yPkbfxiU8Tcbf/TJljqAOz4ISY6ula/RKZONTixHBrvpEW4GAiV3Q5xMsYUe33s +ZFaw7YXtTj0ng7tdDvjpj6ECgYEApHdUU9ejVq2BHslWiqe4LbO9FMxHfvO2hgix +xugupp6y+2Irhb2EQn+PRq+g8hXOzPaezkhHNTKItDL08T3iplkJwJ6dqmszRsZn +i2dYFzZu8M2PAZ4CfZahFbz/9id7D9HTx3EtmH4NAgvZJpyPRkzUbiaIDDettDpj +Hsyi1AECgYAPLvjBzQj4kPF8Zo9pQEUcz4pmupRVfv3aRfjnahDK4qZHEePDRj+J +W7pzayrs1dyN9QLB8pTc424z7f8MB3llCICN+ohs8CR/eW0NEobE9ldDOeoCr1Vh +NhNSbrN1iZ8U4oLkRTMaDKkVngGffvjGi/q0tOU7hJdZOqNlk2Iahg== +-----END RSA PRIVATE KEY----- From d7599ab16d1504e2d75211f05562548665d905d3 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Wed, 16 Nov 2016 01:31:02 +0800 Subject: [PATCH 216/285] driver: fix header file warnings for Doxygen. --- components/driver/include/driver/gpio.h | 2 +- components/driver/include/driver/ledc.h | 2 + components/driver/include/driver/uart.h | 233 ++++++++++++------------ 3 files changed, 122 insertions(+), 115 deletions(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 73efeaa342..2d92dc50a9 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -314,7 +314,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull); * * @param gpio_num GPIO number. * - * @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL\GPIO_INTR_HIGH_LEVEL can be used. + * @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL or GPIO_INTR_HIGH_LEVEL can be used. * * @return * - ESP_OK Success diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 3ab0ebff1e..317a599fbc 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -150,6 +150,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); * * @param channel LEDC channel(0-7), select from ledc_channel_t * + * @param idle_level Set output idle level after LEDC stops. + * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 906bdcb9e9..687ae71aad 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -113,11 +113,11 @@ typedef struct { } uart_event_t; /** - * @brief Set UART data bits. + * @brief Set UART data bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param data_bit UART data bits + * @param data_bit UART data bits * * @return * - ESP_OK Success @@ -126,9 +126,11 @@ typedef struct { esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit); /** - * @brief Get UART data bits. + * @brief Get UART data bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param data_bit Pointer to accept value of UART data bits. * * @return * - ESP_FAIL Parameter error @@ -137,22 +139,24 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit); /** - * @brief Set UART stop bits. + * @brief Set UART stop bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param bit_num UART stop bits + * @param bit_num UART stop bits * * @return * - ESP_OK Success * - ESP_FAIL Fail */ -esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); +esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t bit_num); /** - * @brief Set UART stop bits. + * @brief Set UART stop bits. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param stop_bit Pointer to accept value of UART stop bits. * * @return * - ESP_FAIL Parameter error @@ -161,22 +165,24 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num); esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); /** - * @brief Set UART parity. + * @brief Set UART parity. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param parity_mode the enum of uart parity configuration + * @param parity_mode the enum of uart parity configuration * * @return * - ESP_FAIL Parameter error * - ESP_OK Success */ -esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); +esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode); /** - * @brief Get UART parity mode. + * @brief Get UART parity mode. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param parity_mode Pointer to accept value of UART parity mode. * * @return * - ESP_FAIL Parameter error @@ -186,22 +192,24 @@ esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode); esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); /** - * @brief Set UART baud rate. + * @brief Set UART baud rate. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param baud_rate UART baud-rate. + * @param baud_rate UART baud-rate. * * @return * - ESP_FAIL Parameter error * - ESP_OK Success */ -esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); +esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate); /** - * @brief Get UART bit-rate. + * @brief Get UART bit-rate. * - * @param uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param baudrate Pointer to accept value of UART baud rate * * @return * - ESP_FAIL Parameter error @@ -211,11 +219,11 @@ esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate); esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); /** - * @brief Set UART line inverse mode + * @brief Set UART line inverse mode * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param inverse_mask Choose the wires that need to be inversed. + * @param Inverse_mask Choose the wires that need to be inversed. * * (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION) * @@ -223,16 +231,16 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask); +esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask); /** - * @brief Set hardware flow control. + * @brief Set hardware flow control. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param flow_ctrl Hardware flow control mode + * @param flow_ctrl Hardware flow control mode * - * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) + * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) * * Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. * @@ -240,12 +248,14 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask); * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); +esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh); /** - * @brief Get hardware flow control mode + * @brief Get hardware flow control mode * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @param flow_ctrl Option for different flow control mode. * * @return * - ESP_FAIL Parameter error @@ -254,11 +264,11 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl); /** - * @brief Clear UART interrupt status + * @brief Clear UART interrupt status * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param clr_mask Bit mask of the status that to be cleared. + * @param clr_mask Bit mask of the status that to be cleared. * * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) * @@ -269,11 +279,11 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); /** - * @brief Set UART interrupt enable + * @brief Set UART interrupt enable * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param enable_mask Bit mask of the enable bits. + * @param enable_mask Bit mask of the enable bits. * * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) * @@ -284,11 +294,11 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); /** - * @brief Clear UART interrupt enable bits + * @brief Clear UART interrupt enable bits * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param disable_mask Bit mask of the disable bits. + * @param disable_mask Bit mask of the disable bits. * * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) * @@ -300,9 +310,9 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); /** - * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -311,9 +321,9 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask); esp_err_t uart_enable_rx_intr(uart_port_t uart_num); /** - * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -322,9 +332,9 @@ esp_err_t uart_enable_rx_intr(uart_port_t uart_num); esp_err_t uart_disable_rx_intr(uart_port_t uart_num); /** - * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -333,13 +343,13 @@ esp_err_t uart_disable_rx_intr(uart_port_t uart_num); esp_err_t uart_disable_tx_intr(uart_port_t uart_num); /** - * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) + * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param enable 1: enable; 0: disable + * @param enable 1: enable; 0: disable * - * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN + * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * * @return * - ESP_OK Success @@ -348,21 +358,20 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); /** -* @brief register UART interrupt handler(ISR). +* @brief register UART interrupt handler(ISR). * @note * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * - * @param fn Interrupt handler function. + * @param fn Interrupt handler function. * @attention * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. - * @param arg parameter for handler function + * @param arg parameter for handler function * * @return * - ESP_OK Success @@ -371,21 +380,21 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); /** - * @brief Set UART pin number + * @brief Set UART pin number * * @note * Internal signal can be output to multiple GPIO pads * Only one GPIO pad can connect with input signal * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * - * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. + * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * * @return * - ESP_OK Success @@ -394,12 +403,12 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num); /** - * @brief UART set RTS level (before inverse) + * @brief UART set RTS level (before inverse) * UART rx hardware flow control should not be set. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param level 1: RTS output low(active); 0: RTS output high(block) + * @param level 1: RTS output low(active); 0: RTS output high(block) * * @return * - ESP_OK Success @@ -408,11 +417,11 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r esp_err_t uart_set_rts(uart_port_t uart_num, int level); /** - * @brief UART set DTR level (before inverse) + * @brief UART set DTR level (before inverse) * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param level 1: DTR output low; 0: DTR output high + * @param level 1: DTR output low; 0: DTR output high * * @return * - ESP_OK Success @@ -421,11 +430,11 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level); esp_err_t uart_set_dtr(uart_port_t uart_num, int level); /** -* @brief UART parameter configure +* @brief UART parameter configure * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param uart_config UART parameter settings + * @param uart_config UART parameter settings * * @return * - ESP_OK Success @@ -434,11 +443,11 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config); /** -* @brief UART interrupt configure +* @brief UART interrupt configure * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param intr_conf UART interrupt settings + * @param intr_conf UART interrupt settings * * @return * - ESP_OK Success @@ -447,25 +456,25 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf); /** - * @brief Install UART driver. + * @brief Install UART driver. * * UART ISR handler will be attached to the same CPU core that this function is running on. * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param rx_buffer_size UART RX ring buffer size + * @param rx_buffer_size UART RX ring buffer size * - * @param tx_buffer_size UART TX ring buffer size. + * @param tx_buffer_size UART TX ring buffer size. * * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * - * @param queue_size UART event queue size/depth. + * @param queue_size UART event queue size/depth. * - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details + * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * - * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. * * @return * - ESP_OK Success @@ -474,9 +483,9 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue); /** - * @brief Uninstall UART driver. + * @brief Uninstall UART driver. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success @@ -485,11 +494,11 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b esp_err_t uart_driver_delete(uart_port_t uart_num); /** - * @brief Wait UART TX FIFO empty + * @brief Wait UART TX FIFO empty * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param ticks_to_wait Timeout, count in RTOS ticks + * @param ticks_to_wait Timeout, count in RTOS ticks * * @return * - ESP_OK Success @@ -499,26 +508,25 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** - * @brief Send data to the UART port from a given buffer and length, + * @brief Send data to the UART port from a given buffer and length, * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. * @note * This function should only be used when UART TX buffer is not enabled. * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param buffer data buffer address * - * @param buffer data buffer address - * - * @param len data length to send + * @param len data length to send * * @return * - (-1) Parameter error * - OTHERS(>=0) The number of data that pushed to the TX FIFO */ -int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); +int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len); /** - * @brief Send data to the UART port from a given buffer and length, + * @brief Send data to the UART port from a given buffer and length, * * If parameter tx_buffer_size is set to zero: * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. @@ -526,11 +534,11 @@ int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, * then, UART ISR will move data from ring buffer to TX FIFO gradually. * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param src data buffer address + * @param src data buffer address * - * @param size data length to send + * @param size data length to send * * @return * - (-1) Parameter error @@ -539,7 +547,7 @@ int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len); int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); /** - * @brief Send data to the UART port from a given buffer and length, + * @brief Send data to the UART port from a given buffer and length, * * If parameter tx_buffer_size is set to zero: * This function will not return until all the data and the break signal have been sent out. @@ -549,15 +557,13 @@ int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); * then, UART ISR will move data from ring buffer to TX FIFO gradually. * After all data send out, send a break signal. * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * + * @param src data buffer address * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param size data length to send * - * @param src data buffer address - * - * @param size data length to send - * - * @param brk_len break signal length (unit: one bit's time@current_baudrate) + * @param brk_len break signal length (unit: time of one data bit at current_baudrate) * * @return * - (-1) Parameter error @@ -567,16 +573,15 @@ int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len); /** -* @brief UART read bytes from UART buffer + * @brief UART read bytes from UART buffer * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * - * @param buf pointer to the buffer. + * @param buf pointer to the buffer. * - * @param length data length - * - * @param ticks_to_wait sTimeout, count in RTOS ticks + * @param length data length * + * @param ticks_to_wait sTimeout, count in RTOS ticks * * @return * - (-1) Error @@ -585,9 +590,9 @@ int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t si int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait); /** - * @brief UART ring buffer flush + * @brief UART ring buffer flush * - * @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * * @return * - ESP_OK Success From 656543c5ca9838497da76673ebd408e22d3d3baf Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 16 Nov 2016 10:46:03 +0800 Subject: [PATCH 217/285] feature/openssl: fixup some insufficient --- examples/09_openssl_client/README.md | 2 +- examples/09_openssl_client/main/component.mk | 5 ----- examples/09_openssl_client/main/openssl_client.c | 7 +++---- examples/10_openssl_server/README.md | 2 +- examples/10_openssl_server/main/component.mk | 5 ----- examples/10_openssl_server/main/openssl_server.c | 7 +++---- 6 files changed, 8 insertions(+), 20 deletions(-) diff --git a/examples/09_openssl_client/README.md b/examples/09_openssl_client/README.md index 85d9575d6e..a131cb2472 100644 --- a/examples/09_openssl_client/README.md +++ b/examples/09_openssl_client/README.md @@ -6,7 +6,7 @@ First you should config the project by "make menuconfig": Example Configuration -> 1. Target Domain : the domain that you want to connect to, and default is "www.baidu.com". 2. Target port number : the port number of the target domain, and default is 443. - 3. WiFi SSID : you own wifi, which you pc is connected to alse, and default is "myssid". + 3. WiFi SSID : you own wifi, which is connected to the Internet, and default is "myssid". 4. WiFi Password : wifi password, and default is "mypassword" If you want to test the OpenSSL client demo: diff --git a/examples/09_openssl_client/main/component.mk b/examples/09_openssl_client/main/component.mk index 9c21f4a8d4..44bd2b5273 100644 --- a/examples/09_openssl_client/main/component.mk +++ b/examples/09_openssl_client/main/component.mk @@ -1,8 +1,3 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c index 9d4b8b395d..890962f5d9 100644 --- a/examples/09_openssl_client/main/openssl_client.c +++ b/examples/09_openssl_client/main/openssl_client.c @@ -55,9 +55,10 @@ void openssl_demo_thread(void *p) struct ip4_addr *ip4_addr; int recv_bytes = 0; - char send_data[] = OPENSSL_DEMO_REQUEST; - int send_bytes = sizeof(send_data); char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + const char send_data[] = OPENSSL_DEMO_REQUEST; + const int send_bytes = sizeof(send_data); ESP_LOGI(TAG, "OpenSSL demo thread start OK"); @@ -171,7 +172,6 @@ static void openssl_client_init(void) { int ret; xTaskHandle openssl_handle; - extern void openssl_demo_thread(void *p); ret = xTaskCreate(openssl_demo_thread, OPENSSL_DEMO_THREAD_NAME, @@ -182,7 +182,6 @@ static void openssl_client_init(void) if (ret != pdPASS) { ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - return ; } } diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md index ae5c8da0c7..a8c16d4ed0 100644 --- a/examples/10_openssl_server/README.md +++ b/examples/10_openssl_server/README.md @@ -4,7 +4,7 @@ The Example contains of OpenSSL server demo. First you should configure the project by "make menuconfig": Example Configuration -> - 1. WiFi SSID: you own wifi that you pc is connected to alse. + 1. WiFi SSID: WiFi network to which your PC is also connected to. 1. WiFi Password: wifi password IF you want to test the OpenSSL server demo: diff --git a/examples/10_openssl_server/main/component.mk b/examples/10_openssl_server/main/component.mk index 4a891d52f7..80af01cb53 100644 --- a/examples/10_openssl_server/main/component.mk +++ b/examples/10_openssl_server/main/component.mk @@ -1,11 +1,6 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(IDF_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# COMPONENT_EMBED_TXTFILES := cacert.pem COMPONENT_EMBED_TXTFILES += prvtkey.pem diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c index 4fc841ac79..6dc28182d7 100644 --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -65,10 +65,11 @@ static void openssl_demo_thread(void *p) socklen_t addr_len; struct sockaddr_in sock_addr; - char send_data[] = OPENSSL_DEMO_SERVER_ACK; - int send_bytes = sizeof(send_data); char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + const char send_data[] = OPENSSL_DEMO_SERVER_ACK; + const int send_bytes = sizeof(send_data); + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; @@ -194,7 +195,6 @@ static void openssl_client_init(void) { int ret; xTaskHandle openssl_handle; - extern void openssl_demo_thread(void *p); ret = xTaskCreate(openssl_demo_thread, OPENSSL_DEMO_THREAD_NAME, @@ -205,7 +205,6 @@ static void openssl_client_init(void) if (ret != pdPASS) { ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - return ; } } From 2ed9e2d9a8712df9bf8f00d9d23d6f3522fe6e50 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 16 Nov 2016 11:11:01 +0800 Subject: [PATCH 218/285] feature/openssl: fixup the file and code style --- examples/09_openssl_client/README.md | 4 +- .../09_openssl_client/main/openssl_client.c | 464 ++++++++-------- examples/10_openssl_server/README.md | 7 +- .../10_openssl_server/main/openssl_server.c | 510 +++++++++--------- 4 files changed, 489 insertions(+), 496 deletions(-) diff --git a/examples/09_openssl_client/README.md b/examples/09_openssl_client/README.md index a131cb2472..2a6ac19881 100644 --- a/examples/09_openssl_client/README.md +++ b/examples/09_openssl_client/README.md @@ -6,8 +6,8 @@ First you should config the project by "make menuconfig": Example Configuration -> 1. Target Domain : the domain that you want to connect to, and default is "www.baidu.com". 2. Target port number : the port number of the target domain, and default is 443. - 3. WiFi SSID : you own wifi, which is connected to the Internet, and default is "myssid". - 4. WiFi Password : wifi password, and default is "mypassword" + 3. WIFI SSID : your own WIFI, which is connected to the Internet, and default is "myssid". + 4. WIFI Password : WIFI password, and default is "mypassword" If you want to test the OpenSSL client demo: 1. compile the code and load the firmware diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c index 890962f5d9..c804b6c4fd 100644 --- a/examples/09_openssl_client/main/openssl_client.c +++ b/examples/09_openssl_client/main/openssl_client.c @@ -1,234 +1,230 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "openssl_client.h" - -#include - -#include "openssl/ssl.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "esp_types.h" -#include "esp_log.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" - -#include "nvs_flash.h" -#include "tcpip_adapter.h" - -#include "lwip/sockets.h" -#include "lwip/netdb.h" - -static EventGroupHandle_t wifi_event_group; - -/* The event group allows multiple bits for each event, - but we only care about one event - are we connected - to the AP with an IP? */ -const static int CONNECTED_BIT = BIT0; - -const static char *TAG = "Openssl_demo"; - -void openssl_demo_thread(void *p) -{ - int ret; - SSL_CTX *ctx; - SSL *ssl; - int socket; - struct sockaddr_in sock_addr; - struct hostent *hp; - struct ip4_addr *ip4_addr; - - int recv_bytes = 0; - char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - const char send_data[] = OPENSSL_DEMO_REQUEST; - const int send_bytes = sizeof(send_data); - - ESP_LOGI(TAG, "OpenSSL demo thread start OK"); - - ESP_LOGI(TAG, "get target IP address"); - hp = gethostbyname(OPENSSL_DEMO_TARGET_NAME); - if (!hp) { - ESP_LOGI(TAG, "failed"); - goto failed1; - } - ESP_LOGI(TAG, "OK"); - - ip4_addr = (struct ip4_addr *)hp->h_addr; - ESP_LOGI(TAG, IPSTR, IP2STR(ip4_addr)); - - ESP_LOGI(TAG, "create SSL context ......"); - ctx = SSL_CTX_new(TLSv1_1_client_method()); - if (!ctx) { - ESP_LOGI(TAG, "failed"); - goto failed1; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "bind socket ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = ip4_addr->addr; - sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); - ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "create SSL ......"); - ssl = SSL_new(ctx); - if (!ssl) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - SSL_set_fd(ssl, socket); - - ESP_LOGI(TAG, "SSL connected to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_connect(ssl); - if (!ret) { - ESP_LOGI(TAG, "failed " ); - goto failed4; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "send https request to %s port %d ......", - OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); - ret = SSL_write(ssl, send_data, send_bytes); - if (ret <= 0) { - ESP_LOGI(TAG, "failed"); - goto failed5; - } - ESP_LOGI(TAG, "OK"); - - do { - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - recv_bytes += ret; - ESP_LOGI(TAG, "%s", recv_buf); - } while (1); - - ESP_LOGI(TAG, "totaly read %d bytes data from %s ......", recv_bytes, OPENSSL_DEMO_TARGET_NAME); - -failed5: - SSL_shutdown(ssl); -failed4: - SSL_free(ssl); - ssl = NULL; -failed3: - close(socket); - socket = -1; -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - return ; -} - -static void openssl_client_init(void) -{ - int ret; - xTaskHandle openssl_handle; - - ret = xTaskCreate(openssl_demo_thread, - OPENSSL_DEMO_THREAD_NAME, - OPENSSL_DEMO_THREAD_STACK_WORDS, - NULL, - OPENSSL_DEMO_THREAD_PRORIOTY, - &openssl_handle); - - if (ret != pdPASS) { - ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - } -} - -static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - openssl_client_init(); - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); - break; - default: - break; - } - return ESP_OK; -} - -static void wifi_conn_init(void) -{ - tcpip_adapter_init(); - wifi_event_group = xEventGroupCreate(); - ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - wifi_config_t wifi_config = { - .sta = { - .ssid = EXAMPLE_WIFI_SSID, - .password = EXAMPLE_WIFI_PASS, - }, - }; - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - -void app_main(void) -{ - nvs_flash_init(); - wifi_conn_init(); -} +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_client.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +void openssl_demo_thread(void *p) +{ + int ret; + SSL_CTX *ctx; + SSL *ssl; + int socket; + struct sockaddr_in sock_addr; + struct hostent *hp; + struct ip4_addr *ip4_addr; + + int recv_bytes = 0; + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + const char send_data[] = OPENSSL_DEMO_REQUEST; + const int send_bytes = sizeof(send_data); + + ESP_LOGI(TAG, "OpenSSL demo thread start OK"); + + ESP_LOGI(TAG, "get target IP address"); + hp = gethostbyname(OPENSSL_DEMO_TARGET_NAME); + if (!hp) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ip4_addr = (struct ip4_addr *)hp->h_addr; + ESP_LOGI(TAG, IPSTR, IP2STR(ip4_addr)); + + ESP_LOGI(TAG, "create SSL context ......"); + ctx = SSL_CTX_new(TLSv1_1_client_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "bind socket ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "socket connect to remote %s ......", OPENSSL_DEMO_TARGET_NAME); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = ip4_addr->addr; + sock_addr.sin_port = htons(OPENSSL_DEMO_TARGET_TCP_PORT); + ret = connect(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "create SSL ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, socket); + + ESP_LOGI(TAG, "SSL connected to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_connect(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed " ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "send https request to %s port %d ......", + OPENSSL_DEMO_TARGET_NAME, OPENSSL_DEMO_TARGET_TCP_PORT); + ret = SSL_write(ssl, send_data, send_bytes); + if (ret <= 0) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + do { + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + recv_bytes += ret; + ESP_LOGI(TAG, "%s", recv_buf); + } while (1); + + ESP_LOGI(TAG, "totaly read %d bytes data from %s ......", recv_bytes, OPENSSL_DEMO_TARGET_NAME); + +failed5: + SSL_shutdown(ssl); +failed4: + SSL_free(ssl); + ssl = NULL; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} diff --git a/examples/10_openssl_server/README.md b/examples/10_openssl_server/README.md index a8c16d4ed0..333cb3d6a6 100644 --- a/examples/10_openssl_server/README.md +++ b/examples/10_openssl_server/README.md @@ -4,8 +4,8 @@ The Example contains of OpenSSL server demo. First you should configure the project by "make menuconfig": Example Configuration -> - 1. WiFi SSID: WiFi network to which your PC is also connected to. - 1. WiFi Password: wifi password + 1. WIFI SSID: WIFI network to which your PC is also connected to. + 1. WIFI Password: WIFI password IF you want to test the OpenSSL server demo: 1. compile the code and load the firmware @@ -15,6 +15,7 @@ IF you want to test the OpenSSL server demo: Note: The private key and certification at the example are not trusted by web browser, because they are not created by CA official, just by ourselves. - You can alse create your own private key and ceritification by "openssl at ubuntu or others". + You can alse create your own private key and ceritification by "openssl at ubuntu or others". + We have the document of "ESP8266_SDKSSL_User_Manual_EN_v1.4.pdf" at "http://www.espressif.com/en/support/download/documents". By it you can gernerate the private key and certification with the fomate of ".pem" See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c index 6dc28182d7..7f4b7d6b6e 100644 --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -1,257 +1,253 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "openssl_server.h" - -#include - -#include "openssl/ssl.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "esp_types.h" -#include "esp_log.h" -#include "esp_system.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "esp_log.h" - -#include "nvs_flash.h" -#include "tcpip_adapter.h" - -#include "lwip/sockets.h" -#include "lwip/netdb.h" - -static EventGroupHandle_t wifi_event_group; - -/* The event group allows multiple bits for each event, - but we only care about one event - are we connected - to the AP with an IP? */ -const static int CONNECTED_BIT = BIT0; - -const static char *TAG = "Openssl_demo"; - -#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/html\r\n" \ - "Content-Length: 98\r\n" \ - "\r\n" \ - "\r\n" \ - "OpenSSL demo\r\n" \ - "OpenSSL server demo!\r\n" \ - "\r\n" \ - "\r\n" - -static void openssl_demo_thread(void *p) -{ - int ret; - - SSL_CTX *ctx; - SSL *ssl; - - int socket, new_socket; - socklen_t addr_len; - struct sockaddr_in sock_addr; - - char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; - - const char send_data[] = OPENSSL_DEMO_SERVER_ACK; - const int send_bytes = sizeof(send_data); - - extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); - extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); - const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; - - extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); - extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); - const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; - - ESP_LOGI(TAG, "SSL server context create ......"); - ctx = SSL_CTX_new(SSLv3_server_method()); - if (!ctx) { - ESP_LOGI(TAG, "failed"); - goto failed1; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server context set own certification......"); - ret = SSL_CTX_use_certificate_ASN1(ctx, cacert_pem_bytes, cacert_pem_start); - if (!ret) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server context set private key......"); - ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, prvtkey_pem_start, prvtkey_pem_bytes); - if (!ret) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server create socket ......"); - socket = socket(AF_INET, SOCK_STREAM, 0); - if (socket < 0) { - ESP_LOGI(TAG, "failed"); - goto failed2; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server socket bind ......"); - memset(&sock_addr, 0, sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_addr.s_addr = 0; - sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); - ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server socket listen ......"); - ret = listen(socket, 32); - if (ret) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - -reconnect: - ESP_LOGI(TAG, "SSL server create ......"); - ssl = SSL_new(ctx); - if (!ssl) { - ESP_LOGI(TAG, "failed"); - goto failed3; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server socket accept client ......"); - new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); - if (new_socket < 0) { - ESP_LOGI(TAG, "failed" ); - goto failed4; - } - ESP_LOGI(TAG, "OK"); - - SSL_set_fd(ssl, new_socket); - - ESP_LOGI(TAG, "SSL server accept client ......"); - ret = SSL_accept(ssl); - if (!ret) { - ESP_LOGI(TAG, "failed"); - goto failed5; - } - ESP_LOGI(TAG, "OK"); - - ESP_LOGI(TAG, "SSL server read message ......"); - do { - memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); - ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); - if (ret <= 0) { - break; - } - if (strstr(recv_buf, "GET / HTTP/1.1")) { - SSL_write(ssl, send_data, send_bytes); - break; - } - } while (1); - - ESP_LOGI(TAG, "result %d", ret); - - SSL_shutdown(ssl); -failed5: - close(new_socket); - new_socket = -1; -failed4: - SSL_free(ssl); - ssl = NULL; - goto reconnect; -failed3: - close(socket); - socket = -1; -failed2: - SSL_CTX_free(ctx); - ctx = NULL; -failed1: - vTaskDelete(NULL); - return ; -} - -static void openssl_client_init(void) -{ - int ret; - xTaskHandle openssl_handle; - - ret = xTaskCreate(openssl_demo_thread, - OPENSSL_DEMO_THREAD_NAME, - OPENSSL_DEMO_THREAD_STACK_WORDS, - NULL, - OPENSSL_DEMO_THREAD_PRORIOTY, - &openssl_handle); - - if (ret != pdPASS) { - ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); - } -} - -static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_GOT_IP: - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - openssl_client_init(); - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); - break; - default: - break; - } - return ESP_OK; -} - -static void wifi_conn_init(void) -{ - tcpip_adapter_init(); - wifi_event_group = xEventGroupCreate(); - ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); - ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); - wifi_config_t wifi_config = { - .sta = { - .ssid = EXAMPLE_WIFI_SSID, - .password = EXAMPLE_WIFI_PASS, - }, - }; - ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); - ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); - ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); - ESP_ERROR_CHECK( esp_wifi_start() ); -} - -void app_main(void) -{ - nvs_flash_init(); - wifi_conn_init(); -} +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "openssl_server.h" + +#include + +#include "openssl/ssl.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" + +#include "nvs_flash.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const static int CONNECTED_BIT = BIT0; + +const static char *TAG = "Openssl_demo"; + +#define OPENSSL_DEMO_SERVER_ACK "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/html\r\n" \ + "Content-Length: 98\r\n" \ + "\r\n" \ + "\r\n" \ + "OpenSSL demo\r\n" \ + "OpenSSL server demo!\r\n" \ + "\r\n" \ + "\r\n" + +static void openssl_demo_thread(void *p) +{ + int ret; + + SSL_CTX *ctx; + SSL *ssl; + + int socket, new_socket; + socklen_t addr_len; + struct sockaddr_in sock_addr; + + char recv_buf[OPENSSL_DEMO_RECV_BUF_LEN]; + + const char send_data[] = OPENSSL_DEMO_SERVER_ACK; + const int send_bytes = sizeof(send_data); + + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + const unsigned int cacert_pem_bytes = cacert_pem_end - cacert_pem_start; + + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + const unsigned int prvtkey_pem_bytes = prvtkey_pem_end - prvtkey_pem_start; + + ESP_LOGI(TAG, "SSL server context create ......"); + ctx = SSL_CTX_new(SSLv3_server_method()); + if (!ctx) { + ESP_LOGI(TAG, "failed"); + goto failed1; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set own certification......"); + ret = SSL_CTX_use_certificate_ASN1(ctx, cacert_pem_bytes, cacert_pem_start); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server context set private key......"); + ret = SSL_CTX_use_PrivateKey_ASN1(0, ctx, prvtkey_pem_start, prvtkey_pem_bytes); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server create socket ......"); + socket = socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + ESP_LOGI(TAG, "failed"); + goto failed2; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket bind ......"); + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sin_family = AF_INET; + sock_addr.sin_addr.s_addr = 0; + sock_addr.sin_port = htons(OPENSSL_DEMO_LOCAL_TCP_PORT); + ret = bind(socket, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket listen ......"); + ret = listen(socket, 32); + if (ret) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + +reconnect: + ESP_LOGI(TAG, "SSL server create ......"); + ssl = SSL_new(ctx); + if (!ssl) { + ESP_LOGI(TAG, "failed"); + goto failed3; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server socket accept client ......"); + new_socket = accept(socket, (struct sockaddr *)&sock_addr, &addr_len); + if (new_socket < 0) { + ESP_LOGI(TAG, "failed" ); + goto failed4; + } + ESP_LOGI(TAG, "OK"); + + SSL_set_fd(ssl, new_socket); + + ESP_LOGI(TAG, "SSL server accept client ......"); + ret = SSL_accept(ssl); + if (!ret) { + ESP_LOGI(TAG, "failed"); + goto failed5; + } + ESP_LOGI(TAG, "OK"); + + ESP_LOGI(TAG, "SSL server read message ......"); + do { + memset(recv_buf, 0, OPENSSL_DEMO_RECV_BUF_LEN); + ret = SSL_read(ssl, recv_buf, OPENSSL_DEMO_RECV_BUF_LEN - 1); + if (ret <= 0) { + break; + } + if (strstr(recv_buf, "GET / HTTP/1.1")) { + SSL_write(ssl, send_data, send_bytes); + break; + } + } while (1); + + ESP_LOGI(TAG, "result %d", ret); + + SSL_shutdown(ssl); +failed5: + close(new_socket); + new_socket = -1; +failed4: + SSL_free(ssl); + ssl = NULL; + goto reconnect; +failed3: + close(socket); + socket = -1; +failed2: + SSL_CTX_free(ctx); + ctx = NULL; +failed1: + vTaskDelete(NULL); + return ; +} + +static void openssl_client_init(void) +{ + int ret; + xTaskHandle openssl_handle; + + ret = xTaskCreate(openssl_demo_thread, + OPENSSL_DEMO_THREAD_NAME, + OPENSSL_DEMO_THREAD_STACK_WORDS, + NULL, + OPENSSL_DEMO_THREAD_PRORIOTY, + &openssl_handle); + + if (ret != pdPASS) { + ESP_LOGI(TAG, "create thread %s failed", OPENSSL_DEMO_THREAD_NAME); + } +} + +static esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + openssl_client_init(); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + break; + default: + break; + } + return ESP_OK; +} + +static void wifi_conn_init(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) ); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); + ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_WIFI_SSID, + .password = EXAMPLE_WIFI_PASS, + }, + }; + ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); + ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", EXAMPLE_WIFI_SSID, EXAMPLE_WIFI_PASS); + ESP_ERROR_CHECK( esp_wifi_start() ); +} + +void app_main(void) +{ + nvs_flash_init(); + wifi_conn_init(); +} From 9ae07082ff97ed712a4ee99c38c3a8d874454c17 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 14:20:47 +1100 Subject: [PATCH 219/285] examples: Update component.mk for new conventions --- examples/01_hello_world/main/component.mk | 2 +- examples/02_blink/main/component.mk | 2 +- examples/03_http_request/main/component.mk | 2 +- examples/04_https_request/main/component.mk | 2 +- examples/05_ble_adv/main/component.mk | 2 +- examples/06_sntp/main/component.mk | 2 +- examples/07_nvs_rw_value/main/component.mk | 5 ++++- examples/08_nvs_rw_blob/main/component.mk | 5 ++++- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/01_hello_world/main/component.mk b/examples/01_hello_world/main/component.mk index 4d3b30caf3..0b9d7585e7 100644 --- a/examples/01_hello_world/main/component.mk +++ b/examples/01_hello_world/main/component.mk @@ -1,5 +1,5 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/02_blink/main/component.mk b/examples/02_blink/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/02_blink/main/component.mk +++ b/examples/02_blink/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/03_http_request/main/component.mk b/examples/03_http_request/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/03_http_request/main/component.mk +++ b/examples/03_http_request/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/04_https_request/main/component.mk b/examples/04_https_request/main/component.mk index f4502c25db..818e2a1822 100644 --- a/examples/04_https_request/main/component.mk +++ b/examples/04_https_request/main/component.mk @@ -1,5 +1,5 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/05_ble_adv/main/component.mk b/examples/05_ble_adv/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/05_ble_adv/main/component.mk +++ b/examples/05_ble_adv/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/06_sntp/main/component.mk b/examples/06_sntp/main/component.mk index b4fa72791c..a98f634eae 100644 --- a/examples/06_sntp/main/component.mk +++ b/examples/06_sntp/main/component.mk @@ -1,4 +1,4 @@ # -# Main Makefile. This is basically the same as a component makefile. +# "main" pseudo-component makefile. # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/07_nvs_rw_value/main/component.mk b/examples/07_nvs_rw_value/main/component.mk index d33485c26c..0b9d7585e7 100644 --- a/examples/07_nvs_rw_value/main/component.mk +++ b/examples/07_nvs_rw_value/main/component.mk @@ -1,2 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk diff --git a/examples/08_nvs_rw_blob/main/component.mk b/examples/08_nvs_rw_blob/main/component.mk index d33485c26c..0b9d7585e7 100644 --- a/examples/08_nvs_rw_blob/main/component.mk +++ b/examples/08_nvs_rw_blob/main/component.mk @@ -1,2 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -include $(IDF_PATH)/make/component_common.mk From 60f29236f607c81929c391da263e10f2cc495063 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 15:42:38 +1100 Subject: [PATCH 220/285] Build system: Raise warning level Default esp-idf builds now show -Wextra warnings (except for a few: signed/unsigned comparison, unused parameters, old-style C declarations.) CI building of examples runs with that level raised to -Werror, to catch those changes going into the main repo. --- .../bootloader_support/src/esp_image_format.c | 2 +- .../src/secure_boot_signatures.c | 7 ++- components/expat/port/minicheck.c | 63 ++++++++++--------- components/freertos/ringbuf.c | 4 +- components/freertos/tasks.c | 4 +- make/build_examples.sh | 9 ++- make/project.mk | 16 +++-- 7 files changed, 58 insertions(+), 47 deletions(-) diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 42a3e4ffe2..ca75f6ae45 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -17,7 +17,7 @@ #include #include -const static char *TAG = "esp_image"; +static const char *TAG = "esp_image"; #define SIXTEEN_MB 0x1000000 #define ESP_ROM_CHECKSUM_INITIAL 0xEF diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 6d47651b2f..f7e435e86d 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -45,10 +45,13 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) sha_context sha; uint8_t digest[32]; ptrdiff_t keylen; - const uint8_t *data, *digest_data; - uint32_t digest_len; + const uint8_t *data; const signature_block_t *sigblock; bool is_valid; +#ifdef BOOTLOADER_BUILD + const uint8_t *digest_data; + uint32_t digest_len; +#endif ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length); diff --git a/components/expat/port/minicheck.c b/components/expat/port/minicheck.c index 5a1f5ed0e2..95a4939bdb 100644 --- a/components/expat/port/minicheck.c +++ b/components/expat/port/minicheck.c @@ -108,44 +108,45 @@ add_failure(SRunner *runner, int verbosity) } } +static void run_test(SRunner *runner, int verbosity, TCase *tc, int i) +{ + if (tc->setup != NULL) { + /* setup */ + if (setjmp(env)) { + add_failure(runner, verbosity); + return; + } + tc->setup(); + } + /* test */ + if (setjmp(env)) { + add_failure(runner, verbosity); + return; + } + (tc->tests[i])(); + + /* teardown */ + if (tc->teardown != NULL) { + if (setjmp(env)) { + add_failure(runner, verbosity); + return; + } + tc->teardown(); + } +} + void srunner_run_all(SRunner *runner, int verbosity) { - Suite *suite; - TCase *tc; assert(runner != NULL); - suite = runner->suite; - tc = suite->tests; + assert(runner->suite != NULL); + TCase *tc = runner->suite->tests; while (tc != NULL) { - int i; - for (i = 0; i < tc->ntests; ++i) { + for (int i = 0; i < tc->ntests; ++i) { runner->nchecks++; - - if (tc->setup != NULL) { - /* setup */ - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - tc->setup(); - } - /* test */ - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - (tc->tests[i])(); - - /* teardown */ - if (tc->teardown != NULL) { - if (setjmp(env)) { - add_failure(runner, verbosity); - continue; - } - tc->teardown(); - } + run_test(runner, verbosity, tc, i); + tc = tc->next_tcase; } - tc = tc->next_tcase; } if (verbosity) { int passed = runner->nchecks - runner->nfailures; diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index a4205d88dc..062c52023f 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -495,8 +495,8 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, //we will need to wait some more. ticks_to_wait = ticks_end - xTaskGetTickCount(); } - } while (ringbufferFreeMem(rb) < needed_size && ticks_to_wait>=0); - + } while (ringbufferFreeMem(rb) < needed_size && ticks_end >= xTaskGetTickCount()); + //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 88aa8d3ef5..26103ee236 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -2704,7 +2704,7 @@ void vTaskSwitchContext( void ) taskENTER_CRITICAL_ISR(&xTaskQueueMutex); unsigned portBASE_TYPE foundNonExecutingWaiter = pdFALSE, ableToSchedule = pdFALSE, resetListHead; - unsigned portBASE_TYPE uxDynamicTopReady = uxTopReadyPriority; + portBASE_TYPE uxDynamicTopReady = uxTopReadyPriority; unsigned portBASE_TYPE holdTop=pdFALSE; /* @@ -2717,8 +2717,6 @@ void vTaskSwitchContext( void ) while ( ableToSchedule == pdFALSE && uxDynamicTopReady >= 0 ) { - configASSERT( uxTopReadyPriority>=0 ); - configASSERT( uxDynamicTopReady>=0 ); resetListHead = pdFALSE; // Nothing to do for empty lists if (!listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxDynamicTopReady ] ) )) { diff --git a/make/build_examples.sh b/make/build_examples.sh index a522666a98..8345dab502 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -20,8 +20,13 @@ for example in ${IDF_PATH}/examples/*; do mkdir ${EXAMPLE_NUM} cp -r ${example} ${EXAMPLE_NUM} pushd ${EXAMPLE_NUM}/`basename ${example}` - # build non-verbose first, only build verbose if there's an error - make defconfig all || (RESULT=$?; make V=1) + + # be stricter in the CI build than the default IDF settings + export EXTRA_CFLAGS="-Werror -Werror=deprecated-declarations" + export EXTRA_CXXFLAGS=${EXTRA_CFLAGS} + + # build non-verbose first, only build verbose if there's an error + (make clean defconfig && make all ) || (RESULT=$?; make V=1) popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) done diff --git a/make/project.mk b/make/project.mk index 08c7dd89a9..a081992804 100644 --- a/make/project.mk +++ b/make/project.mk @@ -158,14 +158,16 @@ LDFLAGS ?= -nostdlib \ # CPPFLAGS used by C preprocessor # If any flags are defined in application Makefile, add them at the end. -CPPFLAGS := -DESP_PLATFORM $(CPPFLAGS) +CPPFLAGS := -DESP_PLATFORM $(CPPFLAGS) $(EXTRA_CPPFLAGS) # Warnings-related flags relevant both for C and C++ -COMMON_WARNING_FLAGS = -Wall -Werror \ +COMMON_WARNING_FLAGS = -Wall -Werror=all \ -Wno-error=unused-function \ -Wno-error=unused-but-set-variable \ -Wno-error=unused-variable \ - -Wno-error=deprecated-declarations + -Wno-error=deprecated-declarations \ + -Wextra \ + -Wno-unused-parameter -Wno-sign-compare # Flags which control code generation and dependency generation, both for C and C++ COMMON_FLAGS = \ @@ -192,8 +194,9 @@ CFLAGS := $(strip \ -std=gnu99 \ $(OPTIMIZATION_FLAGS) \ $(COMMON_FLAGS) \ - $(COMMON_WARNING_FLAGS) \ - $(CFLAGS)) + $(COMMON_WARNING_FLAGS) -Wno-old-style-declaration \ + $(CFLAGS) \ + $(EXTRA_CFLAGS)) # List of flags to pass to C++ compiler # If any flags are defined in application Makefile, add them at the end. @@ -204,7 +207,8 @@ CXXFLAGS := $(strip \ $(OPTIMIZATION_FLAGS) \ $(COMMON_FLAGS) \ $(COMMON_WARNING_FLAGS) \ - $(CXXFLAGS)) + $(CXXFLAGS) \ + $(EXTRA_CXXFLAGS)) export CFLAGS CPPFLAGS CXXFLAGS From 7dbf01b2107376e6f6c047bd64221b4f16976b19 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Wed, 16 Nov 2016 13:20:49 +0800 Subject: [PATCH 221/285] esp32: softap supports bridge Softap supports bridge, then the stations associated with esp32 softap can send/receive traffic from each other --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 01f5c068e1..41da160a5d 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 01f5c068e1ac3968add98439ee2f1748b9e391fa +Subproject commit 41da160a5dbf9e13b4fb51f31acf372f50c28270 From 37cbb0bdea302783f3bca43356f806402845322f Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Wed, 16 Nov 2016 14:44:06 +0800 Subject: [PATCH 222/285] driver: bugfix/fix uart parity and frame error bug 1. modify definition for uart_parity_t 2. fix bugs in uart interrupt handler for parity err and frame err. --- components/driver/include/driver/uart.h | 4 ++-- components/driver/uart.c | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 687ae71aad..749cd65648 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -68,8 +68,8 @@ typedef enum { typedef enum { UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ - UART_PARITY_EVEN = 0x10, /*!< Enable UART even parity*/ - UART_PARITY_ODD = 0x11 /*!< Enable UART odd parity*/ + UART_PARITY_EVEN = 0x2, /*!< Enable UART even parity*/ + UART_PARITY_ODD = 0x3 /*!< Enable UART odd parity*/ } uart_parity_t; typedef enum { diff --git a/components/driver/uart.c b/components/driver/uart.c index a8d28ff295..02610e7b49 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -603,10 +603,10 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } else if(uart_intr_status & UART_BRK_DET_INT_ST_M) { uart_reg->int_clr.brk_det = 1; uart_event.type = UART_BREAK; - } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { + } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M ) { uart_reg->int_clr.parity_err = 1; uart_event.type = UART_FRAME_ERR; - } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { + } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { uart_reg->int_clr.frm_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { @@ -964,7 +964,8 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b | UART_RXFIFO_TOUT_INT_ENA_M | UART_FRM_ERR_INT_ENA_M | UART_RXFIFO_OVF_INT_ENA_M - | UART_BRK_DET_INT_ENA_M, + | UART_BRK_DET_INT_ENA_M + | UART_PARITY_ERR_INT_ENA_M, .rxfifo_full_thresh = UART_FULL_THRESH_DEFAULT, .rx_timeout_thresh = UART_TOUT_THRESH_DEFAULT, .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT From 8b7d1cdc272acce928f053ec64eefb55a46f44c4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 17:15:55 +1100 Subject: [PATCH 223/285] ringbuf: Fix case where xTaskGetTickCount() overflows but task timeout point doesn't --- components/freertos/ringbuf.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 062c52023f..67323e3276 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -471,7 +471,8 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, ringbuf_t *rb=(ringbuf_t *)ringbuf; size_t needed_size=dataSize+sizeof(buf_entry_hdr_t); BaseType_t done=pdFALSE; - portTickType ticks_end=xTaskGetTickCount() + ticks_to_wait; + TickType_t ticks_end = xTaskGetTickCount() + ticks_to_wait; + TickType_t ticks_remaining = ticks_to_wait; configASSERT(rb); @@ -486,16 +487,25 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, if (ringbufferFreeMem(rb) < needed_size) { //Data does not fit yet. Wait until the free_space_sem is given, then re-evaluate. - BaseType_t r = xSemaphoreTake(rb->free_space_sem, ticks_to_wait); + BaseType_t r = xSemaphoreTake(rb->free_space_sem, ticks_remaining); if (r == pdFALSE) { //Timeout. return pdFALSE; } - //Adjust ticks_to_wait; we may have waited less than that and in the case the free memory still is not enough, + //Adjust ticks_remaining; we may have waited less than that and in the case the free memory still is not enough, //we will need to wait some more. - ticks_to_wait = ticks_end - xTaskGetTickCount(); + if (ticks_to_wait != portMAX_DELAY) { + ticks_remaining = ticks_end - xTaskGetTickCount(); + } + + // ticks_remaining will always be less than or equal to the original ticks_to_wait, + // unless the timeout is reached - in which case it unsigned underflows to a much + // higher value. + // + // (Check is written this non-intuitive way to allow for the case where xTaskGetTickCount() + // has overflowed but the ticks_end value has not overflowed.) } - } while (ringbufferFreeMem(rb) < needed_size && ticks_end >= xTaskGetTickCount()); + } while (ringbufferFreeMem(rb) < needed_size && ticks_remaining > 0 && ticks_remaining <= ticks_to_wait); //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy. portENTER_CRITICAL(&rb->mux); From 69dbc36a1c9338d5829256c8bf69cffaebbb83a8 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Wed, 16 Nov 2016 16:24:41 +0800 Subject: [PATCH 224/285] lwip: add option to memcopy packet from L2 to L3 Menuconfig add an option to copy the packet from layer2 (WIFI driver) to layer3 (LWIP), default not copy --- components/lwip/Kconfig | 10 +++++ components/lwip/include/lwip/lwip/opt.h | 7 ++++ components/lwip/include/lwip/port/lwipopts.h | 1 + components/lwip/port/netif/wlanif.c | 41 +++++++++----------- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 801fa0b51c..90b7c5b5c7 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -1,5 +1,15 @@ menu "LWIP" +config L2_TO_L3_COPY + bool "Enable copy between Layer2 and Layer3 packets" + default 0 + help + If this feature is enabled, then all traffic from layer2(WIFI Driver) + to layer3(LWIP stack) will make a copy, the layer2 buffer will be + freed and the copy will be sent to layer3. Please make sure you fully + understand this feature before you enable this feature. + + config LWIP_MAX_SOCKETS int "Max number of open sockets" range 1 16 diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index 51d340e00b..9a1d10afbf 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -3008,6 +3008,13 @@ #define LWIP_PERF 0 #endif +/** + * ESP_L2_TO_L3_COPY: enable memcpy when receiving packet from L2 + */ +#ifndef ESP_L2_TO_L3_COPY +#define ESP_L2_TO_L3_COPY 1 +#endif + #ifndef ESP_THREAD_SAFE_DEBUG #define ESP_THREAD_SAFE_DEBUG 0 #endif diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index d06e756850..26bdc3a4e9 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -525,6 +525,7 @@ extern unsigned long os_random(void); #define ESP_RANDOM_TCP_PORT 1 #define ESP_IP4_ATON 1 #define ESP_LIGHT_SLEEP 1 +#define ESP_L2_TO_L3_COPY CONFIG_L2_TO_L3_COPY #define TCP_WND_DEFAULT (4*TCP_MSS) #define TCP_SND_BUF_DEFAULT (2*TCP_MSS) diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index ffad69cd46..3fd2a3192a 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -161,40 +161,37 @@ low_level_output(struct netif *netif, struct pbuf *p) * @param netif the lwip network interface structure for this ethernetif */ void -#if ESP_LWIP wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) -#else -wlanif_input(struct netif *netif, void *buffer, uint16 len) -#endif { struct pbuf *p; -#if ESP_LWIP - if(buffer== NULL) + if(!buffer || !netif) goto _exit; - if(netif == NULL) - goto _exit; -#endif -#if ESP_LWIP - p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); - if (p == NULL){ -#if ESP_PERF - g_rx_alloc_pbuf_fail_cnt++; -#endif - return; - } - p->payload = buffer; - p->eb = eb; -#else - p = pbuf_alloc(PBUF_IP, len, PBUF_POOL); +#if (ESP_L2_TO_L3_COPY == 1) + //p = pbuf_alloc(PBUF_IP, len, PBUF_POOL); + p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { + #if ESP_PERF + g_rx_alloc_pbuf_fail_cnt++; + #endif + esp_wifi_internal_free_rx_buffer(eb); return; } memcpy(p->payload, buffer, len); + esp_wifi_internal_free_rx_buffer(eb); +#else + p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); + if (p == NULL){ + #if ESP_PERF + g_rx_alloc_pbuf_fail_cnt++; + #endif + return; + } + p->payload = buffer; + p->eb = eb; #endif - /* full packet send to tcpip_thread to process */ if (netif->input(p, netif) != ERR_OK) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); From a023b005c67c80c7cf5cc4e2666ff34fe99e5baf Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 16 Nov 2016 17:53:50 +0800 Subject: [PATCH 225/285] taskwdt fixes: better handling of empty/emptying wdt task list, lock task struct while feeding --- components/esp32/task_wdt.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 549b7f58b2..de5f9f54f9 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -22,6 +22,8 @@ #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" #include #include "esp_err.h" #include "esp_intr.h" @@ -45,6 +47,7 @@ struct wdt_task_t { }; static wdt_task_t *wdt_task_list=NULL; +static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED; static void IRAM_ATTR task_wdt_isr(void *arg) { wdt_task_t *wdttask; @@ -55,13 +58,23 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { TIMERG0.wdt_wprotect=0; //Ack interrupt TIMERG0.int_clr_timers.wdt=1; + //We are taking a spinlock while doing I/O (ets_printf) here. Normally, that is a pretty + //bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case, + //something bad already happened and reporting this is considered more important + //than the badness caused by a spinlock here. + portENTER_CRITICAL(&taskwdt_spinlock); + if (!wdt_task_list) { + //No task on list. Maybe none registered yet. + portEXIT_CRITICAL(&taskwdt_spinlock); + return; + } //Watchdog got triggered because at least one task did not report in. ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { if (!wdttask->fed_watchdog) { cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; - printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); } } ets_printf("Tasks currently running:\n"); @@ -73,6 +86,7 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { ets_printf("Aborting.\n"); abort(); #endif + portEXIT_CRITICAL(&taskwdt_spinlock); } @@ -80,6 +94,8 @@ void esp_task_wdt_feed() { wdt_task_t *wdttask=wdt_task_list; bool found_task=false, do_feed_wdt=true; TaskHandle_t handle=xTaskGetCurrentTaskHandle(); + portENTER_CRITICAL(&taskwdt_spinlock); + //Walk the linked list of wdt tasks to find this one, as well as see if we need to feed //the real watchdog timer. for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { @@ -114,14 +130,18 @@ void esp_task_wdt_feed() { //Reset fed_watchdog status for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; } + portEXIT_CRITICAL(&taskwdt_spinlock); } void esp_task_wdt_delete() { TaskHandle_t handle=xTaskGetCurrentTaskHandle(); wdt_task_t *wdttask=wdt_task_list; + portENTER_CRITICAL(&taskwdt_spinlock); + //Wdt task list can't be empty if (!wdt_task_list) { ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?"); + portEXIT_CRITICAL(&taskwdt_spinlock); return; } if (handle==wdt_task_list) { @@ -130,15 +150,25 @@ void esp_task_wdt_delete() { free(wdttask); } else { //Find current task in list + if (wdt_task_list->task_handle==handle) { + //Task is the very first one. + wdt_task_t *freeme=wdt_task_list; + wdt_task_list=wdt_task_list->next; + free(freeme); + portEXIT_CRITICAL(&taskwdt_spinlock); + return; + } while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next; if (!wdttask->next) { ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!"); + portEXIT_CRITICAL(&taskwdt_spinlock); return; } wdt_task_t *freeme=wdttask->next; wdttask->next=wdttask->next->next; free(freeme); } + portEXIT_CRITICAL(&taskwdt_spinlock); } From 0b2cea6e9f757191be0bfc83a6fe3ed15981ccf2 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Wed, 16 Nov 2016 21:11:17 +0800 Subject: [PATCH 226/285] lwip: modify menuconfig comments according to review --- components/lwip/Kconfig | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 90b7c5b5c7..7661fe6cb7 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -6,9 +6,14 @@ config L2_TO_L3_COPY help If this feature is enabled, then all traffic from layer2(WIFI Driver) to layer3(LWIP stack) will make a copy, the layer2 buffer will be - freed and the copy will be sent to layer3. Please make sure you fully - understand this feature before you enable this feature. - + freed and the copy will be sent to layer3. Please be notified that the + total layer2 receiving buffer is fixed and ESP32 currently supports 25 + layer2 receiving buffer, when layer2 buffer runs out of memory, then the + incoming packets will be dropped in hardware. The layer3 buffer is + allocated from the heap, so the total layer3 receiving buffer depends + on the available heap size, when heap runs out of memory, no copy will + be sent to layer3 and packet will be dropped in layer2. Please make sure + you fully understand the impact of this feature before enabling it. config LWIP_MAX_SOCKETS int "Max number of open sockets" From 8713155e669fd19db23e9b20364910240c3f15c9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 14 Nov 2016 15:40:54 +0800 Subject: [PATCH 227/285] docs: add description of ESP-IDF application startup flow and memory layout --- docs/general-notes.rst | 127 +++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 1 + 2 files changed, 128 insertions(+) create mode 100644 docs/general-notes.rst diff --git a/docs/general-notes.rst b/docs/general-notes.rst new file mode 100644 index 0000000000..519a03b287 --- /dev/null +++ b/docs/general-notes.rst @@ -0,0 +1,127 @@ +General Notes About ESP-IDF Programming +======================================= + +Application startup flow +------------------------ + +This note explains various steps which happen before ``app_main`` function of an ESP-IDF application is called. + +The high level view of startup process is as follows: + +1. First-stage bootloader in ROM loads second-stage bootloader image to RAM (IRAM & DRAM) from flash offset 0x1000. +2. Second-stage bootloader loads partition table and main app image from flash. Main app incorporates both RAM segments and read-only segments mapped via flash cache. +3. Main app image executes. At this point the second CPU and RTOS scheduler can be started. + +This process is explained in detail in the following sections. + +First stage bootloader +^^^^^^^^^^^^^^^^^^^^^^ + +After SoC reset, PRO CPU will start running immediately, executing reset vector code, while APP CPU will be held in reset. During startup process, PRO CPU does all the initialization. APP CPU reset is de-asserted in the ``call_start_cpu0`` function of application startup code. Reset vector code is located at address 0x40000400 in the mask ROM of the ESP32 chip and can not be modified. + +Startup code called from the reset vector determines the boot mode by checking ``GPIO_STRAP_REG`` register for bootstrap pin states. Depending on the reset reason, the following takes place: + +1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see deep sleep documentation for this: deep-sleep-stub_. + +2. For power-on reset, software SOC reset, and watchdog SOC reset: check the ``GPIO_STRAP_REG`` register if UART or SDIO download mode is requested. If this is the case, configure UART or SDIO, and wait for code to be downloaded. Otherwise, proceed with boot as if it was due to software CPU reset. + +3. For software CPU reset and watchdog CPU reset: configure SPI flash based on EFUSE values, and attempt to load the code from flash. This step is described in more detail in the next paragraphs. If loading code from flash fails, unpack BASIC interpreter into the RAM and start it. Note that RTC watchdog is still enabled when this happens, so unless any input is received by the interpreter, watchdog will reset the SOC in a few hundred milliseconds, repeating the whole process. If the interpreter receives any input from the UART, it disables the watchdog. + +Application binary image is loaded from flash starting at address 0x1000. First 4kB sector of flash is used to store secure boot IV and signature of the application image. Please check secure boot documentation for details about this. + +.. TODO: describe application binary image format, describe optional flash configuration commands. + +Second stage bootloader +^^^^^^^^^^^^^^^^^^^^^^^ + +In ESP-IDF, the binary image which resides at offset 0x1000 in flash is the second stage bootloader. Second stage bootloader source code is available in components/bootloader directory of ESP-IDF. Note that this arrangement is not the only one possible with the ESP32 chip. It is possible to write a fully featured application which would work when flashed to offset 0x1000, but this is out of scope of this document. Second stage bootloader is used in ESP-IDF to add flexibility to flash layout (using partition tables), and allow for various flows associated with flash encryption, secure boot, and over-the-air updates (OTA) to take place. + +When the first stage bootloader is finished checking and loading the second stage bootloader, it jumps to the second stage bootloader entry point found in the binary image header. + +Second stage bootloader reads the partition table found at offset 0x8000. For more information about partition tables, see partition-tables_. It finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition. + +For the selected partition, second stage bootloader copies data and code sections which are mapped into IRAM and DRAM to their load addresses. For sections which have load addresses in DROM and IROM regions, flash MMU is configured to provide the correct mapping. Note that the second stage bootloader configures flash MMU for both PRO and APP CPUs, but it only enables flash MMU for PRO CPU. Reason for this is that second stage bootloader code is loaded into the memory region used by APP CPU cache. The duty of enabling cache for APP CPU is passed on to the application. Once code is loaded and flash MMU is set up, second stage bootloader jumps to the application entry point found in the binary image header. + +Currently it is not possible to add application-defined hooks to the bootloader to customize application partition selection logic. This may be required to load different application image depending on a state of a GPIO, for example. Such customization features will be added to ESP-IDF in the future. For now, bootloader can be customized by copying bootloader component into application directory and making necessary changes there. ESP-IDF build system will compile the component in application directory instead of ESP-IDF components directory in this case. + +Application startup +^^^^^^^^^^^^^^^^^^^ + +ESP-IDF application entry point is ``call_start_cpu0`` function found in ``components/esp32/cpu_start.c``. Two main things this function does are to enable heap allocator and to make APP CPU jump to its entry point, ``call_start_cpu1``. The code on PRO CPU sets the entry point for APP CPU, de-asserts APP CPU reset, and waits for a global flag to be set by the code running on APP CPU, indicating that it has started. Once this is done, PRO CPU jumps to ``start_cpu0`` function, and APP CPU jumps to ``start_cpu1`` function. + +Both ``start_cpu0`` and ``start_cpu1`` are weak functions, meaning that they can be overridden in the application, if some application-specific change to initialization sequence is needed. Default implementation of ``start_cpu0`` enables or initializes components depending on choices made in ``menuconfig``. Please see source code of this function in ``components/esp32/cpu_start.c`` for an up to date list of steps performed. Note that any C++ global constructors present in the application will be called at this stage. Once all essential components are initialized, *main task* is created and FreeRTOS scheduler is started. + +While PRO CPU does initialization in ``start_cpu0`` function, APP CPU spins in ``start_cpu1`` function, waiting for the scheduler to be started on the PRO CPU. Once the scheduler is started on the PRO CPU, code on the APP CPU starts the scheduler as well. + +Main task is the task which runs ``app_main`` function. Main task stack size and priority can be configured in ``menuconfig``. Application can use this task for initial application-specific setup, for example to launch other tasks. Application can also use main task for event loops and other general purpose activities. If ``app_main`` function returns, main task is deleted. + + +Application memory layout +------------------------- + +ESP32 chip has flexible memory mapping features. This section describes how ESP-IDF uses these features by default. + +Application code in ESP-IDF can be placed into one of the following memory regions. + +IRAM (instruction RAM) +^^^^^^^^^^^^^^^^^^^^^^ + +ESP-IDF allocates part of `Internal SRAM0` region (defined in the Technical Reference Manual) for instruction RAM. Except for the first 64 kB block which is used for PRO and APP CPU caches, the rest of this memory range (i.e. from ``0x40080000`` to ``0x400A0000``) is used to store parts of application which need to run from RAM. + +A few components of ESP-IDF and parts of WiFi stack are placed into this region using the linker script. + +If some application code needs to be placed into IRAM, it can be done using ``IRAM_ATTR`` define:: + + #include "esp_attr.h" + + void IRAM_ATTR gpio_isr_handler(void* arg) + { + // ... + } + +Here are the cases when parts of application may or should be placed into IRAM. + +- ISR handlers must always be placed into IRAM. Furthermore, ISR handlers may only call functions placed into IRAM or functions present in ROM. *Note 1:* all FreeRTOS APIs are currently placed into IRAM, so are safe to call from ISR handlers. *Note 1:* all constant data used by ISR handlers and functions called from ISR handlers (including, but not limited to, ``const char`` arrays), must be placed into DRAM using ``DRAM_ATTR``. + +- Some timing critical code may be placed into IRAM to reduce the penalty associated with loading the code from flash. ESP32 reads code and data from flash via a 32 kB cache. In some cases, placing a function into IRAM may reduce delays caused by a cache miss. + +IROM (code executed from Flash) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If a function is not explicitly placed into IRAM or RTC memory, it is placed into flash. The mechanism by which Flash MMU is used to allow code execution from flash is described in the Technical Reference Manual. ESP-IDF places the code which should be executed from flash starting from the beginning of ``0x400D0000 — 0x40400000`` region. Upon startup, second stage bootloader initializes Flash MMU to map the location in flash where code is located into the beginning of this region. Access to this region is transparently cached using two 32kB blocks in ``0x40070000`` — ``0x40080000`` range. + +Note that the code outside ``0x40000000 — 0x40400000`` region may not be reachable with Window ABI ``CALLx`` instructions, so special care is required if ``0x40400000 — 0x40800000`` or ``0x40800000 — 0x40C00000`` regions are used by the application. ESP-IDF doesn't use these regions by default. + +RTC fast memory +^^^^^^^^^^^^^^^ + +The code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in deep-sleep-stub_. + +DRAM (data RAM) +^^^^^^^^^^^^^^^ + +Non-constant static data and zero-initialized data is placed by the linker into 200 kB ``0x3FFB0000 — 0x3FFF0000`` region. Note that this region is reduced by 64kB (by shifting start address to ``0x3FFC0000``) if Bluetooth stack is used. Length of this region is also reduced by 16 kB or 32kB if trace memory is used. All space which is left in this region after placing static data there is used for the runtime heap. + +Constant data may also be placed into DRAM, for example if it is used in an ISR handler (see notes in IRAM section above). To do that, ``DRAM_ATTR`` define can be used:: + + DRAM_ATTR const char[] format_string = "%p %x"; + char buffer[64]; + sprintf(buffer, format_string, ptr, val); + +Needless to say, it is not advised to use ``printf`` and other output functions in ISR handlers. For debugging purposes, use ``ESP_EARLY_LOGx`` macros when logging from ISR handlers. Make sure that both ``TAG`` and format string are placed into ``DRAM`` in that case. + +DROM (data stored in Flash) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, constant data is placed by the linker into a 4 MB region (``0x3F400000 — 0x3F800000``) which is used to access external flash memory via Flash MMU and cache. Exceptions to this are literal constants which are embedded by the compiler into application code. + +RTC slow memory +^^^^^^^^^^^^^^^ + +Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check the detailed description in deep-sleep-stub_. + + +.. _deep-sleep-stub: deep-sleep-stub.rst +.. _partition-tables: partition-tables.rst + + diff --git a/docs/index.rst b/docs/index.rst index 1ca6e28eec..fb5e0c7c9e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -28,6 +28,7 @@ Contents: :caption: What Else? :maxdepth: 1 + General Notes partition-tables build_system openocd From ca202cbb4605f97bedd884b3acd807e6b4102797 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 16 Nov 2016 21:33:04 +0800 Subject: [PATCH 228/285] docs: fix broken links, formatting, add SPI flash and partition APIs --- docs/Doxyfile | 8 ++++- docs/api/nvs_flash.rst | 5 ++- docs/api/spi_flash.rst | 67 +++++++++++++++++++++++++++++++++++ docs/doxygen_xml_to_rst.xslt | 56 +++++++++++++++++++++++++++++ docs/general-notes.rst | 10 +++--- docs/index.rst | 2 +- docs/security/secure-boot.rst | 12 +++---- 7 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 docs/api/spi_flash.rst create mode 100644 docs/doxygen_xml_to_rst.xslt diff --git a/docs/Doxyfile b/docs/Doxyfile index 6ff4c45860..a46f014a5b 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,6 +1,12 @@ PROJECT_NAME = "ESP32 Programming Guide" -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include +INPUT = ../components/esp32/include/esp_wifi.h \ + ../components/driver/include/driver \ + ../components/bt/include \ + ../components/nvs_flash/include \ + ../components/log/include \ + ../components/vfs/include \ + ../components/spi_flash/include WARN_NO_PARAMDOC = YES diff --git a/docs/api/nvs_flash.rst b/docs/api/nvs_flash.rst index 0768fa5597..5d36343552 100644 --- a/docs/api/nvs_flash.rst +++ b/docs/api/nvs_flash.rst @@ -3,7 +3,10 @@ Application Example ------------------- -`Instructions `_ +Two examples are provided in ESP-IDF examples directory: + +- `07_nvs_rw_value `_ demostrates how to read and write integer values +- `08_nvs_rw_blob `_ demostrates how to read and write variable length binary values API Reference ------------- diff --git a/docs/api/spi_flash.rst b/docs/api/spi_flash.rst new file mode 100644 index 0000000000..5903c0902d --- /dev/null +++ b/docs/api/spi_flash.rst @@ -0,0 +1,67 @@ +.. include:: ../../components/spi_flash/README.rst + +Application Example +------------------- + +`Instructions`_ + +.. _Instructions: template.html + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `spi_flash/include/esp_spi_flash.h `_ + * `spi_flash/include/esp_partition.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: ESP_ERR_FLASH_BASE +.. doxygendefine:: ESP_ERR_FLASH_OP_FAIL +.. doxygendefine:: ESP_ERR_FLASH_OP_TIMEOUT +.. doxygendefine:: SPI_FLASH_SEC_SIZE +.. doxygendefine:: ESP_PARTITION_SUBTYPE_OTA + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: spi_flash_mmap_handle_t +.. doxygentypedef:: esp_partition_iterator_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: spi_flash_mmap_memory_t +.. doxygenenum:: esp_partition_type_t +.. doxygenenum:: esp_partition_subtype_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: esp_partition_t + +Functions +^^^^^^^^^ + +.. doxygenfunction:: spi_flash_init +.. doxygenfunction:: spi_flash_get_chip_size +.. doxygenfunction:: spi_flash_erase_sector +.. doxygenfunction:: spi_flash_erase_range +.. doxygenfunction:: spi_flash_write +.. doxygenfunction:: spi_flash_read +.. doxygenfunction:: spi_flash_mmap +.. doxygenfunction:: spi_flash_munmap +.. doxygenfunction:: spi_flash_mmap_dump +.. doxygenfunction:: esp_partition_find +.. doxygenfunction:: esp_partition_find_first +.. doxygenfunction:: esp_partition_get +.. doxygenfunction:: esp_partition_next +.. doxygenfunction:: esp_partition_iterator_release +.. doxygenfunction:: esp_partition_read +.. doxygenfunction:: esp_partition_write +.. doxygenfunction:: esp_partition_erase_range +.. doxygenfunction:: esp_partition_mmap + diff --git a/docs/doxygen_xml_to_rst.xslt b/docs/doxygen_xml_to_rst.xslt new file mode 100644 index 0000000000..c4570a9df6 --- /dev/null +++ b/docs/doxygen_xml_to_rst.xslt @@ -0,0 +1,56 @@ + + + + + + + + + Macros + ^^^^^^ + + .. doxygendefine:: + + + + + + Type Definitions + ^^^^^^^^^^^^^^^^ + + .. doxygentypedef:: + + + + + + Enumerations + ^^^^^^^^^^^^ + + .. doxygenenum:: + + + + + + + Structures + ^^^^^^^^^^ + + .. doxygenstruct:: + + + + + + Functions + ^^^^^^^^^ + + .. doxygenfunction:: + + + + + + + diff --git a/docs/general-notes.rst b/docs/general-notes.rst index 519a03b287..a683467007 100644 --- a/docs/general-notes.rst +++ b/docs/general-notes.rst @@ -21,7 +21,7 @@ After SoC reset, PRO CPU will start running immediately, executing reset vector Startup code called from the reset vector determines the boot mode by checking ``GPIO_STRAP_REG`` register for bootstrap pin states. Depending on the reset reason, the following takes place: -1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see deep sleep documentation for this: deep-sleep-stub_. +1. Reset from deep sleep: if the value in ``RTC_CNTL_STORE6_REG`` is non-zero, and CRC value of RTC memory in ``RTC_CNTL_STORE7_REG`` is valid, use ``RTC_CNTL_STORE6_REG`` as an entry point address and jump immediately to it. If ``RTC_CNTL_STORE6_REG`` is zero, or ``RTC_CNTL_STORE7_REG`` contains invalid CRC, or once the code called via ``RTC_CNTL_STORE6_REG`` returns, proceed with boot as if it was a power-on reset. **Note**: to run customized code at this point, a deep sleep stub mechanism is provided. Please see :doc:`deep sleep ` documentation for this. 2. For power-on reset, software SOC reset, and watchdog SOC reset: check the ``GPIO_STRAP_REG`` register if UART or SDIO download mode is requested. If this is the case, configure UART or SDIO, and wait for code to be downloaded. Otherwise, proceed with boot as if it was due to software CPU reset. @@ -38,7 +38,7 @@ In ESP-IDF, the binary image which resides at offset 0x1000 in flash is the seco When the first stage bootloader is finished checking and loading the second stage bootloader, it jumps to the second stage bootloader entry point found in the binary image header. -Second stage bootloader reads the partition table found at offset 0x8000. For more information about partition tables, see partition-tables_. It finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition. +Second stage bootloader reads the partition table found at offset 0x8000. See :doc:`partition tables ` documentation for more information. The bootloader finds factory and OTA partitions, and decides which one to boot based on data found in *OTA info* partition. For the selected partition, second stage bootloader copies data and code sections which are mapped into IRAM and DRAM to their load addresses. For sections which have load addresses in DROM and IROM regions, flash MMU is configured to provide the correct mapping. Note that the second stage bootloader configures flash MMU for both PRO and APP CPUs, but it only enables flash MMU for PRO CPU. Reason for this is that second stage bootloader code is loaded into the memory region used by APP CPU cache. The duty of enabling cache for APP CPU is passed on to the application. Once code is loaded and flash MMU is set up, second stage bootloader jumps to the application entry point found in the binary image header. @@ -95,7 +95,7 @@ Note that the code outside ``0x40000000 — 0x40400000`` region may not be reach RTC fast memory ^^^^^^^^^^^^^^^ -The code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in deep-sleep-stub_. +The code which has to run after wake-up from deep sleep mode has to be placed into RTC memory. Please check detailed description in :doc:`deep sleep ` documentation. DRAM (data RAM) ^^^^^^^^^^^^^^^ @@ -118,10 +118,8 @@ By default, constant data is placed by the linker into a 4 MB region (``0x3F4000 RTC slow memory ^^^^^^^^^^^^^^^ -Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check the detailed description in deep-sleep-stub_. +Global and static variables used by code which runs from RTC memory (i.e. deep sleep stub code) must be placed into RTC slow memory. Please check detailed description in :doc:`deep sleep ` documentation. -.. _deep-sleep-stub: deep-sleep-stub.rst -.. _partition-tables: partition-tables.rst diff --git a/docs/index.rst b/docs/index.rst index fb5e0c7c9e..f777d10caf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -97,7 +97,7 @@ Contents: api/gpio api/uart api/ledc - + SPI Flash and Partition APIs Logging Non-Volatile Storage Virtual Filesystem diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index bdc1b71699..b352b3964c 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -14,7 +14,7 @@ Background - Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. -- To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. +- To understand the secure boot process, first familiarise yourself with the standard :doc:`ESP-IDF boot process <../general-notes>`. - Both stages of the boot process (initial software bootloader load, and subsequent partition & app loading) are verified by the secure boot process, in a "chain of trust" relationship. @@ -30,10 +30,11 @@ This is a high level overview of the secure boot process. Step by step instructi 2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader follows the following process to enable secure boot: - - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - - The secure digest is flashed at offset 0x0 in the flash. - - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) - - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) + + - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - The secure digest is flashed at offset 0x0 in the flash. + - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) 4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`. @@ -175,5 +176,4 @@ Deterministic ECDSA as specified by `RFC6979`. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. -.. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 From 13d47343992a861de7e04ae8191a630ee5b21016 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 16 Nov 2016 01:35:09 +0800 Subject: [PATCH 229/285] docs: fix Doxygen warnings, fail CI build on Doxygen warnings --- .gitlab-ci.yml | 3 + components/bt/include/bt.h | 7 +- components/driver/include/driver/gpio.h | 6 + components/driver/include/driver/ledc.h | 6 + components/driver/include/driver/uart.h | 155 +++---- components/esp32/include/esp_wifi.h | 433 +++++++++---------- components/nvs_flash/include/nvs.h | 107 ++++- components/spi_flash/include/esp_partition.h | 95 ++-- components/spi_flash/include/esp_spi_flash.h | 6 +- components/vfs/include/esp_vfs.h | 10 +- docs/Doxyfile | 3 +- 11 files changed, 444 insertions(+), 387 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e09b55c44..1352bd6c93 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -118,6 +118,9 @@ build_docs: - build_docs script: - cd docs + - doxygen + # If not building master branch, and there are Doxygen warnings, print them and bail out + - test "${CI_BUILD_REF_NAME}" = "master" || test $(cat doxygen-warning-log.txt | wc -l) -eq 0 || ( echo "Doxygen pass had some warnings:" && cat doxygen-warning-log.txt && false ) - make html artifacts: paths: diff --git a/components/bt/include/bt.h b/components/bt/include/bt.h index f476334b12..d7e0496c55 100644 --- a/components/bt/include/bt.h +++ b/components/bt/include/bt.h @@ -32,13 +32,10 @@ void bt_controller_init(void); /** @brief vhci_host_callback * used for vhci call host function to notify what host need to do - * - * notify_host_send_available: notify host can send packet to controller - * notify_host_recv: notify host that controller has packet send to host */ typedef struct vhci_host_callback { - void (*notify_host_send_available)(void); - int (*notify_host_recv)(uint8_t *data, uint16_t len); + void (*notify_host_send_available)(void); /*!< callback used to notify that the host can send packet to controller */ + int (*notify_host_recv)(uint8_t *data, uint16_t len); /*!< callback used to notify that the controller has a packet to send to the host*/ } vhci_host_callback_t; /** @brief API_vhci_host_check_send_available diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 03eb7588f8..de34ac4e35 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -117,6 +117,9 @@ extern const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT]; #define GPIO_IS_VALID_GPIO(gpio_num) ((gpio_num < GPIO_PIN_COUNT && GPIO_PIN_MUX_REG[gpio_num] != 0)) //to decide whether it is a valid GPIO number #define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((GPIO_IS_VALID_GPIO(gpio_num)) && (gpio_num < 34)) //to decide whether it can be a valid GPIO number of output mode +/** + * @brief Pullup/pulldown information for a single GPIO pad + */ typedef struct { uint32_t reg; /*!< Register to modify to enable or disable pullups or pulldowns */ uint32_t pu; /*!< Bit to set or clear in the above register to enable or disable the pullup, respectively */ @@ -208,6 +211,9 @@ typedef enum { GPIO_PULLDOWN_ENABLE = 0x1, /*!< Enable GPIO pull-down resistor */ } gpio_pulldown_t; +/** + * @brief Configuration parameters of GPIO pad for gpio_config function + */ typedef struct { uint64_t pin_bit_mask; /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */ gpio_mode_t mode; /*!< GPIO mode: set input/output mode */ diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 317a599fbc..e07787b2b1 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -78,6 +78,9 @@ typedef enum { LEDC_TIMER_15_BIT = 15, /*!< LEDC PWM depth 15Bit */ } ledc_timer_bit_t; +/** + * @brief Configuration parameters of LEDC channel for ledc_channel_config function + */ typedef struct { int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16*/ ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode*/ @@ -87,6 +90,9 @@ typedef struct { uint32_t duty; /*!< LEDC channel duty, the duty range is [0, (2**bit_num) - 1], */ } ledc_channel_config_t; +/** + * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function + */ typedef struct { ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode*/ ledc_timer_bit_t bit_num; /*!< LEDC channel duty depth*/ diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 749cd65648..905c288260 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -32,11 +32,11 @@ extern "C" { #include "freertos/ringbuf.h" #include -#define UART_FIFO_LEN (128) /*< Length of the hardware FIFO buffers */ -#define UART_INTR_MASK 0x1ff -#define UART_LINE_INV_MASK (0x3f << 19) -#define UART_BITRATE_MAX 5000000 -#define UART_PIN_NO_CHANGE (-1) +#define UART_FIFO_LEN (128) /*!< Length of the hardware FIFO buffers */ +#define UART_INTR_MASK 0x1ff /*!< mask of all UART interrupts */ +#define UART_LINE_INV_MASK (0x3f << 19) /*!< TBD */ +#define UART_BITRATE_MAX 5000000 /*!< Max bit rate supported by UART */ +#define UART_PIN_NO_CHANGE (-1) /*!< Constant for uart_set_pin function which indicates that UART pin should not be changed */ #define UART_INVERSE_DISABLE (0x0) /*!< Disable UART signal inverse*/ #define UART_INVERSE_RXD (UART_RXD_INV_M) /*!< UART RXD input inverse*/ @@ -44,6 +44,9 @@ extern "C" { #define UART_INVERSE_TXD (UART_TXD_INV_M) /*!< UART TXD output inverse*/ #define UART_INVERSE_RTS (UART_RTS_INV_M) /*!< UART RTS output inverse*/ +/** + * @brief UART word length constants + */ typedef enum { UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/ UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/ @@ -52,6 +55,9 @@ typedef enum { UART_DATA_BITS_MAX = 0X4, } uart_word_length_t; +/** + * @brief UART stop bits number + */ typedef enum { UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/ UART_STOP_BITS_1_5 = 0x2, /*!< stop bit: 1.5bits*/ @@ -59,6 +65,9 @@ typedef enum { UART_STOP_BITS_MAX = 0x4, } uart_stop_bits_t; +/** + * @brief UART peripheral number + */ typedef enum { UART_NUM_0 = 0x0, /*!< UART base address 0x3ff40000*/ UART_NUM_1 = 0x1, /*!< UART base address 0x3ff50000*/ @@ -66,12 +75,18 @@ typedef enum { UART_NUM_MAX, } uart_port_t; +/** + * @brief UART parity constants + */ typedef enum { UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/ UART_PARITY_EVEN = 0x2, /*!< Enable UART even parity*/ UART_PARITY_ODD = 0x3 /*!< Enable UART odd parity*/ } uart_parity_t; +/** + * @brief UART hardware flow control modes + */ typedef enum { UART_HW_FLOWCTRL_DISABLE = 0x0, /*!< disable hardware flow control*/ UART_HW_FLOWCTRL_RTS = 0x1, /*!< enable RX hardware flow control (rts)*/ @@ -80,6 +95,9 @@ typedef enum { UART_HW_FLOWCTRL_MAX = 0x4, } uart_hw_flowcontrol_t; +/** + * @brief UART configuration parameters for uart_param_config function + */ typedef struct { int baud_rate; /*!< UART baudrate*/ uart_word_length_t data_bits; /*!< UART byte size*/ @@ -89,6 +107,9 @@ typedef struct { uint8_t rx_flow_ctrl_thresh ; /*!< UART HW RTS threshold*/ } uart_config_t; +/** + * @brief UART interrupt configuration parameters for uart_intr_config function + */ typedef struct { uint32_t intr_enable_mask; /*!< UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator*/ uint8_t rx_timeout_thresh; /*!< UART timeout interrupt threshold(unit: time of sending one byte)*/ @@ -96,6 +117,9 @@ typedef struct { uint8_t rxfifo_full_thresh; /*!< UART RX full interrupt threshold.*/ } uart_intr_config_t; +/** + * @brief UART event types used in the ringbuffer + */ typedef enum { UART_DATA, /*!< UART data event*/ UART_BREAK, /*!< UART break event*/ @@ -107,6 +131,9 @@ typedef enum { UART_EVENT_MAX, /*!< UART event max index*/ } uart_event_type_t; +/** + * @brief Event structure used in UART event queue + */ typedef struct { uart_event_type_t type; /*!< UART event type */ size_t size; /*!< UART data size for UART_DATA event*/ @@ -116,7 +143,6 @@ typedef struct { * @brief Set UART data bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param data_bit UART data bits * * @return @@ -129,7 +155,6 @@ esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit * @brief Get UART data bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param data_bit Pointer to accept value of UART data bits. * * @return @@ -142,7 +167,6 @@ esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bi * @brief Set UART stop bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param bit_num UART stop bits * * @return @@ -155,7 +179,6 @@ esp_err_t uart_set_stop_bits(uart_port_t uart_num, uart_stop_bits_t bit_num); * @brief Set UART stop bits. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param stop_bit Pointer to accept value of UART stop bits. * * @return @@ -168,7 +191,6 @@ esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit); * @brief Set UART parity. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param parity_mode the enum of uart parity configuration * * @return @@ -181,7 +203,6 @@ esp_err_t uart_set_parity(uart_port_t uart_num, uart_parity_t parity_mode); * @brief Get UART parity mode. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param parity_mode Pointer to accept value of UART parity mode. * * @return @@ -195,7 +216,6 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode); * @brief Set UART baud rate. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param baud_rate UART baud-rate. * * @return @@ -208,7 +228,6 @@ esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baud_rate); * @brief Get UART bit-rate. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param baudrate Pointer to accept value of UART baud rate * * @return @@ -222,10 +241,8 @@ esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate); * @brief Set UART line inverse mode * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * - * @param Inverse_mask Choose the wires that need to be inversed. - * - * (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION) + * @param inverse_mask Choose the wires that need to be inversed. + * Inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR operation. * * @return * - ESP_OK Success @@ -237,12 +254,9 @@ esp_err_t uart_set_line_inverse(uart_port_t uart_num, uint32_t inverse_mask); * @brief Set hardware flow control. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param flow_ctrl Hardware flow control mode - * - * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN) - * - * Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set. + * @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN). + * Only when UART_HW_FLOWCTRL_RTS is set, will the rx_thresh value be set. * * @return * - ESP_OK Success @@ -254,7 +268,6 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow * @brief Get hardware flow control mode * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param flow_ctrl Option for different flow control mode. * * @return @@ -267,10 +280,8 @@ esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flo * @brief Clear UART interrupt status * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param clr_mask Bit mask of the status that to be cleared. - * - * (enable_mask should be chosen from the fields of register UART_INT_CLR_REG) + * enable_mask should be chosen from the fields of register UART_INT_CLR_REG. * * @return * - ESP_OK Success @@ -282,10 +293,8 @@ esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask); * @brief Set UART interrupt enable * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param enable_mask Bit mask of the enable bits. - * - * (enable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * enable_mask should be chosen from the fields of register UART_INT_ENA_REG. * * @return * - ESP_OK Success @@ -297,10 +306,8 @@ esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask); * @brief Clear UART interrupt enable bits * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param disable_mask Bit mask of the disable bits. - * - * (disable_mask should be chosen from the fields of register UART_INT_ENA_REG) + * disable_mask should be chosen from the fields of register UART_INT_ENA_REG. * * @return * - ESP_OK Success @@ -346,9 +353,7 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); * @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT) * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param enable 1: enable; 0: disable - * * @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN * * @return @@ -358,19 +363,17 @@ esp_err_t uart_disable_tx_intr(uart_port_t uart_num); esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); /** -* @brief register UART interrupt handler(ISR). -* @note - * UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. + * @brief register UART interrupt handler(ISR). + * + * @note UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. + * + * @attention The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * * @param fn Interrupt handler function. - * @attention - * The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. * @param arg parameter for handler function * * @return @@ -382,18 +385,13 @@ esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (* /** * @brief Set UART pin number * - * @note - * Internal signal can be output to multiple GPIO pads - * Only one GPIO pad can connect with input signal + * @note Internal signal can be output to multiple GPIO pads. + * Only one GPIO pad can connect with input signal. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. - * * @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. - * * @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. - * * @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin. * * @return @@ -407,7 +405,6 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r * UART rx hardware flow control should not be set. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param level 1: RTS output low(active); 0: RTS output high(block) * * @return @@ -420,7 +417,6 @@ esp_err_t uart_set_rts(uart_port_t uart_num, int level); * @brief UART set DTR level (before inverse) * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param level 1: DTR output low; 0: DTR output high * * @return @@ -433,7 +429,6 @@ esp_err_t uart_set_dtr(uart_port_t uart_num, int level); * @brief UART parameter configure * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param uart_config UART parameter settings * * @return @@ -446,7 +441,6 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf * @brief UART interrupt configure * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param intr_conf UART interrupt settings * * @return @@ -458,22 +452,16 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ /** * @brief Install UART driver. * - * UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. + * UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param rx_buffer_size UART RX ring buffer size - * * @param tx_buffer_size UART TX ring buffer size. - * - * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. - * + * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details - * * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. * * @return @@ -497,7 +485,6 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); * @brief Wait UART TX FIFO empty * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param ticks_to_wait Timeout, count in RTOS ticks * * @return @@ -508,15 +495,13 @@ esp_err_t uart_driver_delete(uart_port_t uart_num); esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait); /** - * @brief Send data to the UART port from a given buffer and length, - * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. - * @note - * This function should only be used when UART TX buffer is not enabled. + * @brief Send data to the UART port from a given buffer and length. + * + * This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full. + * @note This function should only be used when UART TX buffer is not enabled. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param buffer data buffer address - * * @param len data length to send * * @return @@ -528,16 +513,14 @@ int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len); /** * @brief Send data to the UART port from a given buffer and length, * - * If parameter tx_buffer_size is set to zero: - * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. + * If parameter tx_buffer_size is set to zero: + * This function will not return until all the data have been sent out, or at least pushed into TX FIFO. * - * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, - * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param src data buffer address - * * @param size data length to send * * @return @@ -549,20 +532,17 @@ int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size); /** * @brief Send data to the UART port from a given buffer and length, * - * If parameter tx_buffer_size is set to zero: - * This function will not return until all the data and the break signal have been sent out. - * After all data send out, send a break signal. + * If parameter tx_buffer_size is set to zero: + * This function will not return until all the data and the break signal have been sent out. + * After all data send out, send a break signal. * - * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, - * then, UART ISR will move data from ring buffer to TX FIFO gradually. - * After all data send out, send a break signal. + * Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer, + * then, UART ISR will move data from ring buffer to TX FIFO gradually. + * After all data send out, send a break signal. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param src data buffer address - * * @param size data length to send - * * @param brk_len break signal length (unit: time of one data bit at current_baudrate) * * @return @@ -576,11 +556,8 @@ int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t si * @brief UART read bytes from UART buffer * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * * @param buf pointer to the buffer. - * * @param length data length - * * @param ticks_to_wait sTimeout, count in RTOS ticks * * @return diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 88ea0d9a65..65a91929dd 100644 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -88,6 +88,9 @@ extern "C" { #define ESP_ERR_WIFI_PASSWORD (ESP_ERR_WIFI_BASE + 10) /*!< Passord is invalid */ #define ESP_ERR_WIFI_TIMEOUT (ESP_ERR_WIFI_BASE + 11) /*!< Timeout error */ +/** + * @brief WiFi stack configuration parameters passed to esp_wifi_init call. + */ typedef struct { system_event_handler_t event_handler; /**< WiFi event handler */ } wifi_init_config_t; @@ -108,12 +111,12 @@ typedef struct { * will post station connected event to this queue. If the queue is not initialized, WiFi * will not post any events * - * @param wifi_init_config_t *config : provide WiFi init configuration + * @param config provide WiFi init configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NO_MEM : out of memory - * - others : refer to error code esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NO_MEM: out of memory + * - others: refer to error code esp_err.h */ esp_err_t esp_wifi_init(wifi_init_config_t *config); @@ -123,7 +126,7 @@ esp_err_t esp_wifi_init(wifi_init_config_t *config); * * @attention 1. This API should be called if you want to remove WiFi driver from the system * - * @return ESP_OK : succeed + * @return ESP_OK: succeed */ esp_err_t esp_wifi_deinit(void); @@ -133,25 +136,25 @@ esp_err_t esp_wifi_deinit(void); * Set the WiFi operating mode as station, soft-AP or station+soft-AP, * The default mode is soft-AP mode. * - * @param wifi_mode_t mode : WiFi operating modes: + * @param mode WiFi operating mode * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error code in esp_err.h */ esp_err_t esp_wifi_set_mode(wifi_mode_t mode); /** * @brief Get current operating mode of WiFi * - * @param wifi_mode_t *mode : store current WiFi mode + * @param[out] mode store current WiFi mode * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_mode(wifi_mode_t *mode); @@ -161,29 +164,25 @@ esp_err_t esp_wifi_get_mode(wifi_mode_t *mode); * If mode is WIFI_MODE_AP, it create soft-AP control block and start soft-AP * If mode is WIFI_MODE_APSTA, it create soft-AP and station control block and start soft-AP and station * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_NO_MEM : out of memory - * - ESP_ERR_WIFI_CONN : WiFi internal error, station or soft-AP control block wrong - * - ESP_ERR_WIFI_FAIL : other WiFi internal errors + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_NO_MEM: out of memory + * - ESP_ERR_WIFI_CONN: WiFi internal error, station or soft-AP control block wrong + * - ESP_ERR_WIFI_FAIL: other WiFi internal errors */ esp_err_t esp_wifi_start(void); /** * @brief Stop WiFi - If mode is WIFI_MODE_STA, it stop station and free station control block + * If mode is WIFI_MODE_STA, it stop station and free station control block * If mode is WIFI_MODE_AP, it stop soft-AP and free soft-AP control block * If mode is WIFI_MODE_APSTA, it stop station/soft-AP and free station/soft-AP control block * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_stop(void); @@ -193,52 +192,47 @@ esp_err_t esp_wifi_stop(void); * @attention 1. This API only impact WIFI_MODE_STA or WIFI_MODE_APSTA mode * @attention 2. If the ESP32 is connected to an AP, call esp_wifi_disconnect to disconnect. * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_CONN : WiFi internal error, station or soft-AP control block wrong - * - ESP_ERR_WIFI_SSID : SSID of AP which station connects is invalid + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_CONN: WiFi internal error, station or soft-AP control block wrong + * - ESP_ERR_WIFI_SSID: SSID of AP which station connects is invalid */ esp_err_t esp_wifi_connect(void); /** * @brief Disconnect the ESP32 WiFi station from the AP. * - * @param null - * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_FAIL : other WiFi internal errors + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_FAIL: other WiFi internal errors */ esp_err_t esp_wifi_disconnect(void); /** * @brief Currently this API is just an stub API * - * @param null - * + * @return - * - ESP_OK : succeed - * - others : fail + * - ESP_OK: succeed + * - others: fail */ esp_err_t esp_wifi_clear_fast_connect(void); /** * @brief deauthenticate all stations or associated id equals to aid * - * @param uint16_t aid : when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid + * @param aid when aid is 0, deauthenticate all stations, otherwise deauthenticate station whose associated id is aid * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_MODE : WiFi mode is wrong + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_MODE: WiFi mode is wrong */ esp_err_t esp_wifi_deauth_sta(uint16_t aid); @@ -249,88 +243,87 @@ esp_err_t esp_wifi_deauth_sta(uint16_t aid); * will be freed in esp_wifi_get_ap_list, so generally, call esp_wifi_get_ap_list to cause * the memory to be freed once the scan is done * - * @param struct scan_config *config : configuration of scanning - * @param bool block : if block is true, this API will block the caller until the scan is done, otherwise + * @param config configuration of scanning + * @param block if block is true, this API will block the caller until the scan is done, otherwise * it will return immediately * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_TIMEOUT : blocking scan is timeout - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_TIMEOUT: blocking scan is timeout + * - others: refer to error code in esp_err.h */ -esp_err_t esp_wifi_scan_start(wifi_scan_config_t *conf, bool block); +esp_err_t esp_wifi_scan_start(wifi_scan_config_t *config, bool block); /** * @brief Stop the scan in process * - * @param null * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start */ esp_err_t esp_wifi_scan_stop(void); /** * @brief Get number of APs found in last scan * - * @param uint16_t *number : store number of APIs found in last scan + * @param[out] number store number of APIs found in last scan * - * @attention This API can only be called when the scan is completed, otherwise it may get wrong value + * @attention This API can only be called when the scan is completed, otherwise it may get wrong value. * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_scan_get_ap_num(uint16_t *number); /** * @brief Get AP list found in last scan * - * @param uint16_t *number : as input param, it stores max AP number ap_records can hold, as output param, it store - the actual AP number this API returns - * @param wifi_ap_record_t *ap_records: wifi_ap_record_t array to hold the found APs + * @param[inout] number As input param, it stores max AP number ap_records can hold. + * As output param, it receives the actual AP number this API returns. + * @param ap_records wifi_ap_record_t array to hold the found APs * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_NOT_START : WiFi is not started by esp_wifi_start - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_NO_MEM : out of memory + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_NOT_START: WiFi is not started by esp_wifi_start + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_NO_MEM: out of memory */ esp_err_t esp_wifi_scan_get_ap_records(uint16_t *number, wifi_ap_record_t *ap_records); /** - * @brief Get information of AP associated with ESP32 station + * @brief Get information of AP which the ESP32 station is associated with * - * @param wifi_ap_record_t *ap_info: the wifi_ap_record_t to hold station assocated AP + * @param ap_info the wifi_ap_record_t to hold AP information * * @return - * - ESP_OK : succeed - * - others : fail + * - ESP_OK: succeed + * - others: fail */ esp_err_t esp_wifi_sta_get_ap_info(wifi_ap_record_t *ap_info); /** * @brief Set current power save type * - * @param wifi_ps_type_t type : power save type + * @param type power save type * - * @return ESP_ERR_WIFI_NOT_SUPPORT : not support yet + * @return ESP_ERR_WIFI_NOT_SUPPORT: not supported yet */ esp_err_t esp_wifi_set_ps(wifi_ps_type_t type); /** * @brief Get current power save type * - * @param wifi_ps_type_t *type : store current power save type + * @param[out] type: store current power save type * - * @return ESP_ERR_WIFI_NOT_SUPPORT : not support yet + * @return ESP_ERR_WIFI_NOT_SUPPORT: not supported yet */ esp_err_t esp_wifi_get_ps(wifi_ps_type_t *type); @@ -340,47 +333,47 @@ esp_err_t esp_wifi_get_ps(wifi_ps_type_t *type); * * @attention Currently we only support 802.11b or 802.11bg or 802.11bgn mode * - * @param wifi_interface_t ifx : interfaces - * @param uint8_t protocol : WiFi protocol bitmap + * @param ifx interfaces + * @param protocol_bitmap WiFi protocol bitmap * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - others : refer to erro code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_set_protocol(wifi_interface_t ifx, uint8_t protocol_bitmap); /** - * @brief Get the current protocol bitmap of specified ifx + * @brief Get the current protocol bitmap of the specified interface * - * @param wifi_interface_t ifx : interfaces - * @param uint8_t protocol : store current WiFi protocol bitmap of interface ifx + * @param ifx interface + * @param[out] protocol_bitmap store current WiFi protocol bitmap of interface ifx * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_get_protocol(wifi_interface_t ifx, uint8_t *protocol_bitmap); /** * @brief Set the bandwidth of ESP32 specified interface * - * @attention 1. API return false if try to configure a interface that is not enable + * @attention 1. API return false if try to configure an interface that is not enabled * @attention 2. WIFI_BW_HT40 is supported only when the interface support 11N * - * @param wifi_interface_t ifx : interface to be configured - * @param wifi_bandwidth_t bw : bandwidth + * @param ifx interface to be configured + * @param bw bandwidth * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw); @@ -389,45 +382,45 @@ esp_err_t esp_wifi_set_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t bw); * * @attention 1. API return false if try to get a interface that is not enable * - * @param wifi_interface_t ifx : interface to be configured - * @param wifi_bandwidth_t *bw : store bandwidth of interface ifx + * @param ifx interface to be configured + * @param[out] bw store bandwidth of interface ifx * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_bandwidth(wifi_interface_t ifx, wifi_bandwidth_t *bw); /** - * @brief Set primary/second channel of ESP32 + * @brief Set primary/secondary channel of ESP32 * * @attention 1. This is a special API for sniffer * - * @param uint8_t primary : for HT20, primary is the channel number, for HT40, primary is the primary channel - * @param wifi_second_chan_t second : for HT20, second is ignored, for HT40, second is the second channel + * @param primary for HT20, primary is the channel number, for HT40, primary is the primary channel + * @param second for HT20, second is ignored, for HT40, second is the second channel * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_set_channel(uint8_t primary, wifi_second_chan_t second); /** - * @brief Get the primary/second channel of ESP32 + * @brief Get the primary/secondary channel of ESP32 * * @attention 1. API return false if try to get a interface that is not enable * - * @param uint8_t *primary : store current primary channel - * @param wifi_second_chan_t *second : store current second channel + * @param primary store current primary channel + * @param[out] second store current second channel * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second); @@ -435,25 +428,25 @@ esp_err_t esp_wifi_get_channel(uint8_t *primary, wifi_second_chan_t *second); * @brief Set country code * The default value is WIFI_COUNTRY_CN * - * @param wifi_country_t country : country type + * @param country country type * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - others: refer to error code in esp_err.h */ esp_err_t esp_wifi_set_country(wifi_country_t country); /** * @brief Get country code * - * @param wifi_country_t country : store current country + * @param country store current country * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_country(wifi_country_t *country); @@ -462,43 +455,44 @@ esp_err_t esp_wifi_get_country(wifi_country_t *country); * * @attention 1. This API can only be called when the interface is disabled * @attention 2. ESP32 soft-AP and station have different MAC addresses, do not set them to be the same. - * - The bit0 of the first byte of ESP32 MAC address can not be 1. For example, the MAC address + * @attention 3. The bit 0 of the first byte of ESP32 MAC address can not be 1. For example, the MAC address * can set to be "1a:XX:XX:XX:XX:XX", but can not be "15:XX:XX:XX:XX:XX". * - * @param wifi_interface_t ifx : interface - * @param uint8 mac[6]: the MAC address. + * @param ifx interface + * @param mac the MAC address * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_MAC : invalid mac address - * - ESP_ERR_WIFI_MODE : WiFi mode is wrong - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_MAC: invalid mac address + * - ESP_ERR_WIFI_MODE: WiFi mode is wrong + * - others: refer to error codes in esp_err.h */ esp_err_t esp_wifi_set_mac(wifi_interface_t ifx, uint8_t mac[6]); /** * @brief Get mac of specified interface * - * @param uint8_t mac[6] : store mac of this interface ifx + * @param ifx interface + * @param[out] mac store mac of the interface ifx * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface */ esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]); /** * @brief The RX callback function in the promiscuous mode. * - * Each time a packet is received, the callback function will be called. + * Each time a packet is received, the callback function will be called. * - * @param void *buf : the data received - * @param uint16_t len : data length + * @param buf the data received + * @param len data length * * @return none */ @@ -507,36 +501,36 @@ typedef void (* wifi_promiscuous_cb_t)(void *buf, uint16_t len); /** * @brief Register the RX callback function in the promiscuous mode. * - * Each time a packet is received, the registered callback function will be called. + * Each time a packet is received, the registered callback function will be called. * - * @param wifi_promiscuous_cb_t cb : callback + * @param cb callback * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); /** * @brief Enable the promiscuous mode. * - * @param bool promiscuous : false - disable / true - enable + * @param en false - disable, true - enable * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_promiscuous(bool en); /** * @brief Get the promiscuous mode. * - * @param bool *enable : store the current status of promiscuous mode + * @param[out] en store the current status of promiscuous mode * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_promiscuous(bool *en); @@ -548,32 +542,32 @@ esp_err_t esp_wifi_get_promiscuous(bool *en); * @attention 3. ESP32 is limited to only one channel, so when in the soft-AP+station mode, the soft-AP will adjust its channel automatically to be the same as * the channel of the ESP32 station. * - * @param wifi_interface_t ifx : interface - * @param wifi_config_t *conf : station or soft-AP configuration + * @param ifx interface + * @param conf station or soft-AP configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface - * - ESP_ERR_WIFI_MODE : invalid mode - * - ESP_ERR_WIFI_PASSWORD : invalid password - * - ESP_ERR_WIFI_NVS : WiFi internal NVS error - * - others : refer to the erro code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface + * - ESP_ERR_WIFI_MODE: invalid mode + * - ESP_ERR_WIFI_PASSWORD: invalid password + * - ESP_ERR_WIFI_NVS: WiFi internal NVS error + * - others: refer to the erro code in esp_err.h */ esp_err_t esp_wifi_set_config(wifi_interface_t ifx, wifi_config_t *conf); /** * @brief Get configuration of specified interface * - * @param wifi_interface_t ifx : interface - * @param wifi_config_t *conf : station or soft-AP configuration + * @param ifx interface + * @param[out] conf station or soft-AP configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_IF : invalid interface + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_IF: invalid interface */ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); @@ -582,14 +576,14 @@ esp_err_t esp_wifi_get_config(wifi_interface_t ifx, wifi_config_t *conf); * * @attention SSC only API * - * @param wifi_sta_list_t *sta: station list + * @param[out] sta station list * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_MODE : WiFi mode is wrong - * - ESP_ERR_WIFI_CONN : WiFi internal error, the station/soft-AP control block is invalid + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_MODE: WiFi mode is wrong + * - ESP_ERR_WIFI_CONN: WiFi internal error, the station/soft-AP control block is invalid */ esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); @@ -599,12 +593,12 @@ esp_err_t esp_wifi_ap_get_sta_list(wifi_sta_list_t *sta); * * @attention 1. The default value is WIFI_STORAGE_FLASH * - * @param wifi_storage_t storage : storage type + * @param storage : storage type * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_set_storage(wifi_storage_t storage); @@ -612,72 +606,63 @@ esp_err_t esp_wifi_set_storage(wifi_storage_t storage); * @brief Set auto connect * The default value is true * - * @param bool en : true - enable auto connect / false - disable auto connect + * @param en : true - enable auto connect / false - disable auto connect * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_MODE : WiFi internal error, the station/soft-AP control block is invalid - * - others : refer to error code in esp_err.h + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_MODE: WiFi internal error, the station/soft-AP control block is invalid + * - others: refer to error code in esp_err.h */ esp_err_t esp_wifi_set_auto_connect(bool en); /** * @brief Get the auto connect flag * - * @param bool *en : store current auto connect configuration + * @param[out] en store current auto connect configuration * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument */ esp_err_t esp_wifi_get_auto_connect(bool *en); /** * @brief Set vendor specific element * - * @param bool enable : enable or not - * @param wifi_vendor_ie_type_t type : 0 - WIFI_VND_IE_TYPE_BEACON - * 1 - WIFI_VND_IE_TYPE_PROBE_REQ - * 2 - WIFI_VND_IE_TYPE_PROBE_RESP - * 3 - WIFI_VND_IE_TYPE_ASSOC_REQ - * 4 - WIFI_VND_IE_TYPE_ASSOC_RESP - * @param wifi_vendor_ie_id_t idx : 0 - WIFI_VND_IE_ID_0 - 1 - WIFI_VND_IE_ID_1 - * @param uint8_t *vnd_ie : pointer to a vendor specific element + * @param enable enable or not + * @param type information element type + * @param idx information element index + * @param vnd_ie pointer to a vendor specific element * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init - * - ESP_ERR_WIFI_ARG : invalid argument - * - ESP_ERR_WIFI_NO_MEM : out of memory + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init + * - ESP_ERR_WIFI_ARG: invalid argument + * - ESP_ERR_WIFI_NO_MEM: out of memory */ esp_err_t esp_wifi_set_vendor_ie(bool enable, wifi_vendor_ie_type_t type, wifi_vendor_ie_id_t idx, uint8_t *vnd_ie); /** * @brief Define function pointer for vendor specific element callback - * @param void *ctx : reserved - * @param wifi_vendor_ie_type_t type : 0 - WIFI_VND_IE_TYPE_BEACON - * 1 - WIFI_VND_IE_TYPE_PROBE_REQ - * 2 - WIFI_VND_IE_TYPE_PROBE_RESP - * 3 - WIFI_VND_IE_TYPE_ASSOC_REQ - * 4 - WIFI_VND_IE_TYPE_ASSOC_RESP - * @param const uint8_t sa[6] : source address - * @param const uint8_t *vnd_ie : pointer to a vendor specific element - * @param int rssi : received signal strength indication + * @param ctx reserved + * @param type information element type + * @param sa source address + * @param vnd_ie pointer to a vendor specific element + * @param rssi received signal strength indication */ typedef void (*esp_vendor_ie_cb_t) (void *ctx, wifi_vendor_ie_type_t type, const uint8_t sa[6], const uint8_t *vnd_ie, int rssi); /** * @brief Set vendor specific element callback * - * @param esp_vendor_ie_cb_t cb : callback function - * @param void *ctx : reserved + * @param cb callback function + * @param ctx reserved * * @return - * - ESP_OK : succeed - * - ESP_ERR_WIFI_NOT_INIT : WiFi is not initialized by eps_wifi_init + * - ESP_OK: succeed + * - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by eps_wifi_init */ esp_err_t esp_wifi_set_vendor_ie_cb(esp_vendor_ie_cb_t cb, void *ctx); diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index 8418959793..5f7a93a7b0 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -77,8 +77,9 @@ typedef enum { */ esp_err_t nvs_open(const char* name, nvs_open_mode open_mode, nvs_handle *out_handle); +/**@{*/ /** - * @brief nvs_set_X - set value for given key + * @brief set value for given key * * This family of functions set value for the key, given its name. Note that * actual storage will not be updated until nvs_commit function is called. @@ -89,7 +90,6 @@ esp_err_t nvs_open(const char* name, nvs_open_mode open_mode, nvs_handle *out_ha * implementation, but is guaranteed to be at least * 16 characters. Shouldn't be empty. * @param[in] value The value to set. - * @param[in] length For nvs_set_blob: length of binary value to set, in bytes. * * @return * - ESP_OK if value was set successfully @@ -112,10 +112,39 @@ esp_err_t nvs_set_u32 (nvs_handle handle, const char* key, uint32_t value); esp_err_t nvs_set_i64 (nvs_handle handle, const char* key, int64_t value); esp_err_t nvs_set_u64 (nvs_handle handle, const char* key, uint64_t value); esp_err_t nvs_set_str (nvs_handle handle, const char* key, const char* value); -esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, size_t length); +/**@}*/ /** - * @brief nvs_get_X - get value for given key + * @brief set variable length binary value for given key + * + * This family of functions set value for the key, given its name. Note that + * actual storage will not be updated until nvs_commit function is called. + * + * @param[in] handle Handle obtained from nvs_open function. + * Handles that were opened read only cannot be used. + * @param[in] key Key name. Maximal length is determined by the underlying + * implementation, but is guaranteed to be at least + * 16 characters. Shouldn't be empty. + * @param[in] value The value to set. + * @param[in] length length of binary value to set, in bytes. + * + * @return + * - ESP_OK if value was set successfully + * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL + * - ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only + * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints + * - ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the + * underlying storage to save the value + * - ESP_ERR_NVS_REMOVE_FAILED if the value wasn't updated because flash + * write operation has failed. The value was written however, and + * update will be finished after re-initialization of nvs, provided that + * flash operation doesn't fail again. + */ +esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, size_t length); + +/**@{*/ +/** + * @brief get value for given key * * These functions retrieve value for the key, given its name. If key does not * exist, or the requested variable type doesn't match the type which was used @@ -125,7 +154,55 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * * All functions expect out_value to be a pointer to an already allocated variable * of the given type. - * Additionally, nvs_get_str and nvs_get_blob support WinAPI-style length queries. + * + * \code{c} + * // Example of using nvs_get_i32: + * int32_t max_buffer_size = 4096; // default value + * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); + * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); + * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still + * // have its default value. + * + * \endcode + * + * @param[in] handle Handle obtained from nvs_open function. + * @param[in] key Key name. Maximal length is determined by the underlying + * implementation, but is guaranteed to be at least + * 16 characters. Shouldn't be empty. + * @param out_value Pointer to the output value. + * May be NULL for nvs_get_str and nvs_get_blob, in this + * case required length will be returned in length argument. + * + * @return + * - ESP_OK if the value was retrieved successfully + * - ESP_ERR_NVS_NOT_FOUND if the requested key doesn't exist + * - ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL + * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints + * - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data + */ +esp_err_t nvs_get_i8 (nvs_handle handle, const char* key, int8_t* out_value); +esp_err_t nvs_get_u8 (nvs_handle handle, const char* key, uint8_t* out_value); +esp_err_t nvs_get_i16 (nvs_handle handle, const char* key, int16_t* out_value); +esp_err_t nvs_get_u16 (nvs_handle handle, const char* key, uint16_t* out_value); +esp_err_t nvs_get_i32 (nvs_handle handle, const char* key, int32_t* out_value); +esp_err_t nvs_get_u32 (nvs_handle handle, const char* key, uint32_t* out_value); +esp_err_t nvs_get_i64 (nvs_handle handle, const char* key, int64_t* out_value); +esp_err_t nvs_get_u64 (nvs_handle handle, const char* key, uint64_t* out_value); +/**@}*/ + +/** + * @brief get value for given key + * + * These functions retrieve value for the key, given its name. If key does not + * exist, or the requested variable type doesn't match the type which was used + * when setting a value, an error is returned. + * + * In case of any error, out_value is not modified. + * + * All functions expect out_value to be a pointer to an already allocated variable + * of the given type. + * + * nvs_get_str and nvs_get_blob functions support WinAPI-style length queries. * To get the size necessary to store the value, call nvs_get_str or nvs_get_blob * with zero out_value and non-zero pointer to length. Variable pointed to * by length argument will be set to the required length. For nvs_get_str, @@ -136,13 +213,6 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * nvs_get/set_blob used for arbitrary data structures. * * \code{c} - * // Example of using nvs_get_i32: - * int32_t max_buffer_size = 4096; // default value - * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size); - * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND); - * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still - * // have its default value. - * * // Example (without error checking) of using nvs_get_str to get a string into dynamic array: * size_t required_size; * nvs_get_str(my_handle, "server_name", NULL, &required_size); @@ -163,8 +233,7 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * @param out_value Pointer to the output value. * May be NULL for nvs_get_str and nvs_get_blob, in this * case required length will be returned in length argument. - * @param[inout] length For nvs_get_str and nvs_get_blob, non-zero pointer - * to the variable holding the length of out_value. + * @param[inout] length A non-zero pointer to the variable holding the length of out_value. * In case out_value a zero, will be set to the length * required to hold the value. In case out_value is not * zero, will be set to the actual length of the value @@ -177,16 +246,10 @@ esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, si * - ESP_ERR_NVS_INVALID_NAME if key name doesn't satisfy constraints * - ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data */ -esp_err_t nvs_get_i8 (nvs_handle handle, const char* key, int8_t* out_value); -esp_err_t nvs_get_u8 (nvs_handle handle, const char* key, uint8_t* out_value); -esp_err_t nvs_get_i16 (nvs_handle handle, const char* key, int16_t* out_value); -esp_err_t nvs_get_u16 (nvs_handle handle, const char* key, uint16_t* out_value); -esp_err_t nvs_get_i32 (nvs_handle handle, const char* key, int32_t* out_value); -esp_err_t nvs_get_u32 (nvs_handle handle, const char* key, uint32_t* out_value); -esp_err_t nvs_get_i64 (nvs_handle handle, const char* key, int64_t* out_value); -esp_err_t nvs_get_u64 (nvs_handle handle, const char* key, uint64_t* out_value); +/**@{*/ esp_err_t nvs_get_str (nvs_handle handle, const char* key, char* out_value, size_t* length); esp_err_t nvs_get_blob(nvs_handle handle, const char* key, void* out_value, size_t* length); +/**@}*/ /** * @brief Erase key-value pair with given key name. diff --git a/components/spi_flash/include/esp_partition.h b/components/spi_flash/include/esp_partition.h index ae0185dcd7..13b803e10f 100644 --- a/components/spi_flash/include/esp_partition.h +++ b/components/spi_flash/include/esp_partition.h @@ -25,57 +25,78 @@ extern "C" { #endif +/** + * @file esp_partition.h + * @brief Partition APIs + */ + + +/** + * @brief Partition type + * @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py + */ typedef enum { - ESP_PARTITION_TYPE_APP = 0x00, - ESP_PARTITION_TYPE_DATA = 0x01, - ESP_PARTITION_TYPE_FILESYSTEM = 0x02, + ESP_PARTITION_TYPE_APP = 0x00, //!< Application partition type + ESP_PARTITION_TYPE_DATA = 0x01, //!< Data partition type } esp_partition_type_t; +/** + * @brief Partition subtype + * @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py + */ typedef enum { - ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, - ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, - ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, - ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, - ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, - ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, - ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, - ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, - ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, - ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, - ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, - ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, - ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10, - ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11, - ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12, - ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13, - ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14, - ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15, - ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, - ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, + ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, //!< Factory application partition + ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, //!< Base for OTA partition subtypes + ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, //!< OTA partition 0 + ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, //!< OTA partition 1 + ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, //!< OTA partition 2 + ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, //!< OTA partition 3 + ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, //!< OTA partition 4 + ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, //!< OTA partition 5 + ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, //!< OTA partition 6 + ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, //!< OTA partition 7 + ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, //!< OTA partition 8 + ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, //!< OTA partition 9 + ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10,//!< OTA partition 10 + ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11,//!< OTA partition 11 + ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12,//!< OTA partition 12 + ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13,//!< OTA partition 13 + ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14,//!< OTA partition 14 + ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15,//!< OTA partition 15 + ESP_PARTITION_SUBTYPE_APP_OTA_MAX = 15, //!< Max subtype of OTA partition + ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition - ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, - ESP_PARTITION_SUBTYPE_DATA_RF = 0x01, - ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, + ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition + ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition + ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition - ESP_PARTITION_SUBTYPE_FILESYSTEM_ESPHTTPD = 0x00, - ESP_PARTITION_SUBTYPE_FILESYSTEM_FAT = 0x01, - ESP_PARTITION_SUBTYPE_FILESYSTEM_SPIFFS = 0x02, + ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition + ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition + ESP_PARTITION_SUBTYPE_DATA_SPIFFS = 0x82, //!< SPIFFS partition - ESP_PARTITION_SUBTYPE_ANY = 0xff, + ESP_PARTITION_SUBTYPE_ANY = 0xff, //!< Used to search for partitions with any subtype } esp_partition_subtype_t; +/** + * @brief Convenience macro to get esp_partition_subtype_t value for the i-th OTA partition + */ #define ESP_PARTITION_SUBTYPE_OTA(i) ((esp_partition_subtype_t)(ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((i) & 0xf))) - +/** + * @brief Opaque partition iterator type + */ typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; +/** + * @brief partition information structure + */ typedef struct { - esp_partition_type_t type; - esp_partition_subtype_t subtype; - uint32_t address; - uint32_t size; - char label[17]; - bool encrypted; + esp_partition_type_t type; /*!< partition type (app/data) */ + esp_partition_subtype_t subtype; /*!< partition subtype */ + uint32_t address; /*!< starting address of the partition in flash */ + uint32_t size; /*!< size of the partition, in bytes */ + char label[17]; /*!< partition label, zero-terminated ASCII string */ + bool encrypted; /*!< flag is set to true if partition is encrypted */ } esp_partition_t; /** diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 840bbc4971..5d124da6b2 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -62,13 +62,13 @@ esp_err_t spi_flash_erase_sector(size_t sector); /** * @brief Erase a range of flash sectors * - * @param uint32_t start_address : Address where erase operation has to start. + * @param start_address Address where erase operation has to start. * Must be 4kB-aligned - * @param uint32_t size : Size of erased range, in bytes. Must be divisible by 4kB. + * @param size Size of erased range, in bytes. Must be divisible by 4kB. * * @return esp_err_t */ -esp_err_t spi_flash_erase_range(size_t start_addr, size_t size); +esp_err_t spi_flash_erase_range(size_t start_address, size_t size); /** diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index 2d9e52c5af..7dd273fb00 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -57,15 +57,15 @@ extern "C" { * flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer * to esp_vfs_register function. * If the implementation doesn't use this extra argument, populate the - * members without _p suffix and set flags memeber to ESP_VFS_FLAG_DEFAULT. + * members without _p suffix and set flags member to ESP_VFS_FLAG_DEFAULT. * * If the FS driver doesn't provide some of the functions, set corresponding * members to NULL. */ typedef struct { - int fd_offset; - int flags; + int fd_offset; /*!< file descriptor offset, determined by the FS driver */ + int flags; /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */ union { size_t (*write_p)(void* p, int fd, const void * data, size_t size); size_t (*write)(int fd, const void * data, size_t size); @@ -135,7 +135,7 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct * These functions are to be used in newlib syscall table. They will be called by * newlib when it needs to use any of the syscalls. */ - +/**@{*/ ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size); off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode); ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size); @@ -146,7 +146,7 @@ int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st); int esp_vfs_link(struct _reent *r, const char* n1, const char* n2); int esp_vfs_unlink(struct _reent *r, const char *path); int esp_vfs_rename(struct _reent *r, const char *src, const char *dst); - +/**@}*/ #ifdef __cplusplus diff --git a/docs/Doxyfile b/docs/Doxyfile index a46f014a5b..0e4549a10f 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -20,9 +20,8 @@ XML_OUTPUT = xml GENERATE_HTML = NO HAVE_DOT = NO GENERATE_LATEX = NO -GENERATE_MAN = NO +GENERATE_MAN = YES GENERATE_RTF = NO -QUIET = YES WARN_LOGFILE = "doxygen-warning-log.txt" From 4eeb2bc41e43c850d468ed9132c0202f0ddbf638 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 10:35:47 +1100 Subject: [PATCH 230/285] build system: Fix embedding files which are themselves generated by the build system Used by secure boot, which generates the secure boot signing key inside build/. --- make/component_wrapper.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 55a135158a..6f903f9fce 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -194,7 +194,7 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect define GenerateEmbedTarget $(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) $(summary) EMBED $$@ - $$(if $$(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir + $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir $$(if $$(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ rm $$(notdir $$<) From 24b4c17ead152119736716347b29e9266462ab75 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 11:12:48 +1100 Subject: [PATCH 231/285] build system: Refactor SubmoduleCheck to work project-wide Required at project level because some components use header files in other components' submodules, and one component with a submodule (esptool.py) doesn't have or need a component.mk. --- components/bt/component.mk | 3 +- components/esp32/component.mk | 4 +-- components/esptool_py/Makefile.projbuild | 4 ++- docs/build_system.rst | 8 +++++ make/common.mk | 25 ---------------- make/component_wrapper.mk | 5 ++-- make/project.mk | 37 +++++++++++++++++++++--- 7 files changed, 49 insertions(+), 37 deletions(-) diff --git a/components/bt/component.mk b/components/bt/component.mk index 91620ddc14..d6bde0c5b6 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -15,5 +15,4 @@ COMPONENT_ADD_LDFLAGS := -lbt -L $(COMPONENT_PATH)/lib \ ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) $(COMPONENT_LIBRARY): $(ALL_LIB_FILES) -# automatically trigger a git submodule update if BT library is missing -$(eval $(call SubmoduleCheck,$(ALL_LIB_FILES),$(COMPONENT_PATH)/lib)) +COMPONENT_SUBMODULES += lib diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 040c686e5e..8059d157c5 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -17,9 +17,7 @@ COMPONENT_ADD_LDFLAGS := -lesp32 \ ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS)) -# automatically trigger a git submodule update -# if any libraries are missing -$(eval $(call SubmoduleCheck,$(ALL_LIB_FILES),$(COMPONENT_PATH)/lib)) +COMPONENT_SUBMODULES += lib # this is a hack to make sure the app is re-linked if the binary # libraries change or are updated. If they change, the main esp32 diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index aa3bb2bfa9..acbada7244 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -54,4 +54,6 @@ app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) @echo "Flashing app to serial port $(ESPPORT), offset $(CONFIG_APP_OFFSET)..." $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) -$(eval $(call SubmoduleCheck,$(ESPTOOLPY_SRC),$(COMPONENT_PATH)/esptool)) +# Submodules normally added in component.mk, but can be added +# at the project level as long as qualified path +COMPONENT_SUBMODULES += $(COMPONENT_PATH)/esptool diff --git a/docs/build_system.rst b/docs/build_system.rst index 8168cb76ef..aa14cdda56 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -186,6 +186,14 @@ The following variables can be set inside ``component.mk`` to control build sett generates an include file which you then want to include in another component. Most components do not need to set this variable. +The following variable only works for components that are part of esp-idf itself: + +- ``COMPONENT_SUBMODULES``: Optional list of git submodule paths + (relative to COMPONENT_PATH) used by the component. These will be + checked (and initialised if necessary) by the build process. This + variable is ignored if the component is outside the IDF_PATH + directory. + Optional Component-Specific Variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/make/common.mk b/make/common.mk index 6e4fa75ab5..4d40f7abc3 100644 --- a/make/common.mk +++ b/make/common.mk @@ -26,31 +26,6 @@ details := @true MAKEFLAGS += --silent endif -# Pseudo-target to check a git submodule has been properly initialised -# -# $(eval $(call SubmoduleCheck,FILENAMES,SUBMODULE_PATH)) to create a target that -# automatically runs 'git submodule update --init SUBMODULE_PATH' if any of -# the files in FILENAMES are missing, and fails if this is not possible. -# -# Will also print a WARNING if the submodule at SUBMODULE_PATH appears -# to require an update. -define SubmoduleCheck -$(1): - @echo "WARNING: Missing submodule $(2) for $$@..." - [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) - [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule --init' in esp-idf root directory."; exit 1) - @echo "Attempting 'git submodule update --init' in esp-idf root directory..." - cd ${IDF_PATH} && git submodule update --init $(2) - -# Parse 'git submodule status' output for out-of-date submodule. -# Status output prefixes status line with '+' if the submodule commit doesn't match -ifneq ("$(shell cd ${IDF_PATH} && git submodule status $(2) | grep '^+')","") -$$(info WARNING: git submodule $2 may be out of date. Run 'git submodule update' to update.) -endif -endef - - - # General make utilities # convenience variable for printing an 80 asterisk wide separator line diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 6f903f9fce..005c996712 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -103,8 +103,8 @@ endef # component_project_vars.mk target for the component. This is used to # take component.mk variables COMPONENT_ADD_INCLUDEDIRS, -# COMPONENT_ADD_LDFLAGS and COMPONENT_DEPENDS and inject those into -# the project make pass. +# COMPONENT_ADD_LDFLAGS, COMPONENT_DEPENDS and COMPONENT_SUBMODULES +# and inject those into the project make pass. # # The target here has no dependencies, as the parent target in # project.mk evaluates dependencies before calling down to here. See @@ -119,6 +119,7 @@ component_project_vars.mk:: @echo '# Automatically generated build file. Do not edit.' > $@ @echo 'COMPONENT_INCLUDES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_INCLUDEDIRS)))' >> $@ @echo 'COMPONENT_LDFLAGS += $(call MakeVariablePath,$(COMPONENT_ADD_LDFLAGS))' >> $@ + @echo 'COMPONENT_SUBMODULES += $(call MakeVariablePath,$(addprefix $(COMPONENT_PATH)/,$(COMPONENT_SUBMODULES)))' >> $@ @echo '$(COMPONENT_NAME)-build: $(addsuffix -build,$(COMPONENT_DEPENDS))' >> $@ diff --git a/make/project.mk b/make/project.mk index a081992804..870db55f97 100644 --- a/make/project.mk +++ b/make/project.mk @@ -10,7 +10,7 @@ # where this file is located. # -.PHONY: build-components menuconfig defconfig all build clean all_binaries +.PHONY: build-components menuconfig defconfig all build clean all_binaries check-submodules all: all_binaries # see below for recipe of 'all' target # @@ -94,13 +94,16 @@ COMPONENT_PATHS += $(abspath $(SRCDIRS)) # A component is buildable if it has a component.mk makefile in it COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(cp)/component.mk),$(cp))) -# Initialise a project-wide list of include dirs (COMPONENT_INCLUDES), -# and LDFLAGS args (COMPONENT_LDFLAGS) supplied by each component. +# Initialise project-wide variables which can be added to by +# each component. # # These variables are built up via the component_project_vars.mk # generated makefiles (one per component). +# +# See docs/build-system.rst for more details. COMPONENT_INCLUDES := COMPONENT_LDFLAGS := +COMPONENT_SUBMODULES := # COMPONENT_PROJECT_VARS is the list of component_project_vars.mk generated makefiles # for each component. @@ -289,7 +292,7 @@ endef define GenerateComponentTargets .PHONY: $(2)-build $(2)-clean -$(2)-build: +$(2)-build: check-submodules $(call ComponentMake,$(1),$(2)) build $(2)-clean: @@ -332,4 +335,30 @@ app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) config-clean: app-clean clean: config-clean +# phony target to check if any git submodule listed in COMPONENT_SUBMODULES are missing +# or out of date, and exit if so. Components can add paths to this variable. +# +# This only works for components inside IDF_PATH +check-submodules: +# Generate a target to check this submodule +# $(1) - submodule directory, relative to IDF_PATH +define GenerateSubmoduleCheckTarget +check-submodules: $(IDF_PATH)/$(1)/.git +$(IDF_PATH)/$(1)/.git: + @echo "WARNING: Missing submodule $(1)..." + [ -d ${IDF_PATH}/.git ] || ( echo "ERROR: esp-idf must be cloned from git to work."; exit 1) + [ -x $(which git) ] || ( echo "ERROR: Need to run 'git submodule init $(1)' in esp-idf root directory."; exit 1) + @echo "Attempting 'git submodule update --init $(1)' in esp-idf root directory..." + cd ${IDF_PATH} && git submodule update --init $(1) + +# Parse 'git submodule status' output for out-of-date submodule. +# Status output prefixes status line with '+' if the submodule commit doesn't match +ifneq ("$(shell cd ${IDF_PATH} && git submodule status $(1) | grep '^+')","") +$$(info WARNING: git submodule $(1) may be out of date. Run 'git submodule update' to update.) +endif +endef + +# filter/subst in expression ensures all submodule paths begin with $(IDF_PATH), and then strips that prefix +# so the argument is suitable for use with 'git submodule' commands +$(foreach submodule,$(subst $(IDF_PATH)/,,$(filter $(IDF_PATH)/%,$(COMPONENT_SUBMODULES))),$(eval $(call GenerateSubmoduleCheckTarget,$(submodule)))) From 84635a160e0b6765e031c562c6a38f0d078c69f7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 11:18:40 +1100 Subject: [PATCH 232/285] micro-ecc: Add submodule dependency Closes github #100 --- components/micro-ecc/component.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/micro-ecc/component.mk b/components/micro-ecc/component.mk index df73f7a3b2..8c569df597 100644 --- a/components/micro-ecc/component.mk +++ b/components/micro-ecc/component.mk @@ -4,3 +4,5 @@ COMPONENT_SRCDIRS := micro-ecc COMPONENT_OBJS := micro-ecc/uECC.o COMPONENT_ADD_INCLUDEDIRS := micro-ecc + +COMPONENT_SUBMODULES := micro-ecc From 872a481cf1c526263c0bf90afc0ebf7c25aa6a47 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 11:45:39 +1100 Subject: [PATCH 233/285] build system: When embedding binary files, fix re-generating on partial builds When embedding a generated file (ie secure boot public key data), the file was being re-generated each time. --- make/component_wrapper.mk | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 005c996712..2c073af761 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -189,18 +189,29 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect # txt files are null-terminated before being embedded (otherwise # identical behaviour.) # -# Files are temporarily copied to the build directory before objcopy, -# because objcopy generates the symbol name from the full command line -# path to the input file. define GenerateEmbedTarget -$(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) + +# copy the input file into the build dir (using a subdirectory +# in case the file already exists elsewhere in the build dir) +embed_bin/$$(notdir $(1)): $(call resolvepath,$(1),$(COMPONENT_PATH)) | embed_bin + cp $$< $$@ + +embed_txt/$$(notdir $(1)): $(call resolvepath,$(1),$(COMPONENT_PATH)) | embed_txt + cp $$< $$@ + echo -ne '\0' >> $$@ # null-terminate text files + +# messing about with the embed_X subdirectory then using 'cd' for objcopy is because the +# full path passed to OBJCOPY makes it into the name of the symbols in the .o file +$(1).$(2).o: embed_$(2)/$$(notdir $(1)) | $$(dir $(1)) $(summary) EMBED $$@ - $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir - $$(if $$(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output - $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ - rm $$(notdir $$<) + cd embed_$(2); $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) ../$$@ + +CLEAN_FILES += embed_$(2)/$$(notdir $(1)) endef +embed_txt embed_bin: + mkdir -p $@ + # generate targets to embed binary & text files $(foreach binfile,$(COMPONENT_EMBED_FILES), $(eval $(call GenerateEmbedTarget,$(binfile),bin))) From 78d084942670c1703af3da58d0fa91f07fd19953 Mon Sep 17 00:00:00 2001 From: Benjamin Vernoux Date: Fri, 28 Oct 2016 22:25:14 +0200 Subject: [PATCH 234/285] Simplified "Installing OpenOCD" with git clone --recursive --- docs/openocd.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/openocd.rst b/docs/openocd.rst index 2dcb55f0c5..642e3067e1 100644 --- a/docs/openocd.rst +++ b/docs/openocd.rst @@ -40,10 +40,8 @@ Installing OpenOCD The sources for the ESP32-enabled variant of OpenOCD are available from `Espressifs Github `_. To download the source, use the following commands:: - git clone https://github.com/espressif/openocd-esp32.git + git clone --recursive https://github.com/espressif/openocd-esp32.git cd openocd-esp32 - git submodule init - git submodule update For compilation of OpenOCD, please refer to the README, README.OSX and README.Windows file in the openocd-esp32 directory. You can skip the ``make install`` step if you want. From ceea97495f5070e50ba3b36de3b9f1a0afcf5226 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 10:22:20 +0800 Subject: [PATCH 235/285] lwip:refractor to the description about this menuconfig option --- components/lwip/Kconfig | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 7661fe6cb7..bf7bff15b4 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -4,16 +4,17 @@ config L2_TO_L3_COPY bool "Enable copy between Layer2 and Layer3 packets" default 0 help - If this feature is enabled, then all traffic from layer2(WIFI Driver) - to layer3(LWIP stack) will make a copy, the layer2 buffer will be - freed and the copy will be sent to layer3. Please be notified that the - total layer2 receiving buffer is fixed and ESP32 currently supports 25 - layer2 receiving buffer, when layer2 buffer runs out of memory, then the - incoming packets will be dropped in hardware. The layer3 buffer is - allocated from the heap, so the total layer3 receiving buffer depends - on the available heap size, when heap runs out of memory, no copy will - be sent to layer3 and packet will be dropped in layer2. Please make sure - you fully understand the impact of this feature before enabling it. + If this feature is enabled, all traffic from layer2(WIFI Driver) will be + copied to a new buffer before sending it to layer3(LWIP stack), freeing + the layer2 buffer. + Please be notified that the total layer2 receiving buffer is fixed and + ESP32 currently supports 25 layer2 receiving buffer, when layer2 buffer + runs out of memory, then the incoming packets will be dropped in hardware. + The layer3 buffer is allocated from the heap, so the total layer3 receiving + buffer depends on the available heap size, when heap runs out of memory, + no copy will be sent to layer3 and packet will be dropped in layer2. + Please make sure you fully understand the impact of this feature before + enabling it. config LWIP_MAX_SOCKETS int "Max number of open sockets" From 5e428f21b6e07825ce041acc9a4b84bdeddb1dc9 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 10:53:00 +0800 Subject: [PATCH 236/285] lwip: default ESP_L2_TO_L3_COPY to 0 --- components/lwip/include/lwip/lwip/opt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/lwip/include/lwip/lwip/opt.h b/components/lwip/include/lwip/lwip/opt.h index 9a1d10afbf..c42f3cd735 100755 --- a/components/lwip/include/lwip/lwip/opt.h +++ b/components/lwip/include/lwip/lwip/opt.h @@ -3012,7 +3012,7 @@ * ESP_L2_TO_L3_COPY: enable memcpy when receiving packet from L2 */ #ifndef ESP_L2_TO_L3_COPY -#define ESP_L2_TO_L3_COPY 1 +#define ESP_L2_TO_L3_COPY 0 #endif #ifndef ESP_THREAD_SAFE_DEBUG From f12be7ebd8c5b0006a94c1ddaa492555ffc73d78 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 11:22:20 +0800 Subject: [PATCH 237/285] nvs_flash: adjust the nvs_flash start sector number Modify the nvs flash start sector from 6 to 9 --- components/nvs_flash/src/nvs_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index c1a910260e..f6c6c588aa 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -63,7 +63,7 @@ extern "C" void nvs_dump() extern "C" esp_err_t nvs_flash_init(void) { - return nvs_flash_init_custom(6, 3); + return nvs_flash_init_custom(9, 3); } extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) From 12dd886ee194004ce8f9e74c1b3329b3b40129be Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 18:03:55 +1100 Subject: [PATCH 238/285] build system: Use correct objcopy arguments for object format Avoid ambiguous argument error on some platforms Ref internal discussion !198 squash! build system: Use correct objcopy --input-target argument not --input --- make/component_wrapper.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 2c073af761..3018c18b55 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -180,7 +180,7 @@ $(foreach srcdir,$(COMPONENT_SRCDIRS), $(eval $(call GenerateCompileTargets,$(sr ## Support for embedding binary files into the ELF as symbols -OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architecture xtensa --rename-section .data=.rodata.embedded +OBJCOPY_EMBED_ARGS := --input-target binary --output-target elf32-xtensa-le --binary-architecture xtensa --rename-section .data=.rodata.embedded # Generate pattern for embedding text or binary files into the app # $(1) is name of file (as relative path inside component) From 936b02216b49af02488d058e80c2b58dc771fd51 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 11:44:39 +0800 Subject: [PATCH 239/285] docs/nvs_flash: update the nvs flash offset comment according review --- components/nvs_flash/include/nvs_flash.h | 2 +- docs/partition-tables.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index 1cade0e956..d6e1990250 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -21,7 +21,7 @@ extern "C" { /** Initialise NVS flash storage with default flash sector layout Temporarily, this region is hardcoded as a 12KB (0x3000 byte) - region starting at 24KB (0x6000 byte) offset in flash. + region starting at 36KB (0x9000 byte) offset in flash. @return ESP_OK if flash was successfully initialised. */ diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index 5f5911bd52..a1a46866ee 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -23,7 +23,7 @@ Known Issues The below design document outlines the goals for the partition table system. At the moment, only some features are used: - data partition types "rf" & "wifi" are unused and can be entirely omitted to save space. -- NVS (non-volatile-storage) uses a hardcoded 12KB (0x3000 byte) region at offset 0x6000. +- NVS (non-volatile-storage) uses a hardcoded 12KB (0x3000 byte) region at offset 0x9000. Once a full user API is in place for partition access, these limitations will be resolved and you'll be able to use the partition mechanism fully for storing data in flash. From d19d7107d34541bf6edc9d9e80daeff394904cf4 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 17 Nov 2016 11:56:22 +0800 Subject: [PATCH 240/285] esp32: tw8706 softap join ap return wrong code In softap/null mode, esp_wifi_connect will return ESP_ERR_WIFI_MODE --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index 41da160a5d..e188536a63 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 41da160a5dbf9e13b4fb51f31acf372f50c28270 +Subproject commit e188536a6315cc3ce4f1006ac3a4450faea6abc6 From d116adf260358180807045ad047dc95f302a2f52 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 17 Nov 2016 12:09:08 +0800 Subject: [PATCH 241/285] Add documentation --- components/esp32/include/esp_int_wdt.h | 3 -- components/esp32/include/esp_task_wdt.h | 9 ---- docs/Doxyfile | 4 +- docs/api/wdts.rst | 72 +++++++++++++++++++++++++ docs/index.rst | 5 +- 5 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 docs/api/wdts.rst diff --git a/components/esp32/include/esp_int_wdt.h b/components/esp32/include/esp_int_wdt.h index 4387400396..b32d0219fb 100644 --- a/components/esp32/include/esp_int_wdt.h +++ b/components/esp32/include/esp_int_wdt.h @@ -41,9 +41,6 @@ This uses the TIMERG1 WDT. * @brief Initialize the interrupt watchdog. This is called in the init code if * the interrupt watchdog is enabled in menuconfig. * - * @param null - * - * @return null */ void esp_int_wdt_init(); diff --git a/components/esp32/include/esp_task_wdt.h b/components/esp32/include/esp_task_wdt.h index bbc4995674..eb77377009 100644 --- a/components/esp32/include/esp_task_wdt.h +++ b/components/esp32/include/esp_task_wdt.h @@ -42,9 +42,6 @@ This uses the TIMERG0 WDT. * @brief Initialize the task watchdog. This is called in the init code, if the * task watchdog is enabled in menuconfig. * - * @param null - * - * @return null */ void esp_task_wdt_init(); @@ -52,9 +49,6 @@ void esp_task_wdt_init(); * @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling * task to keep feeding the watchdog until task_wdt_delete() is called. * - * @param null - * - * @return null */ void esp_task_wdt_feed(); @@ -63,9 +57,6 @@ void esp_task_wdt_feed(); /** * @brief Delete the watchdog for the current task. * - * @param null - * - * @return null */ void esp_task_wdt_delete(); diff --git a/docs/Doxyfile b/docs/Doxyfile index 6ff4c45860..d5970ee5e9 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,6 +1,8 @@ PROJECT_NAME = "ESP32 Programming Guide" -INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include ../components/nvs_flash/include ../components/log/include ../components/vfs/include +INPUT = ../components/esp32/include/esp_wifi.h ../components/driver/include/driver ../components/bt/include \ + ../components/nvs_flash/include ../components/log/include ../components/vfs/include \ + ../components/esp32/include/esp_int_wdt.h ../components/esp32/include/esp_task_wdt.h WARN_NO_PARAMDOC = YES diff --git a/docs/api/wdts.rst b/docs/api/wdts.rst new file mode 100644 index 0000000000..1b476f2f79 --- /dev/null +++ b/docs/api/wdts.rst @@ -0,0 +1,72 @@ +Watchdogs +========= + +Overview +-------- + +Esp-idf has support for two types of watchdogs: a task watchdog as well as an interrupt watchdog. Both can be +enabled using ``make menuconfig`` and selecting the appropriate options. + +Interrupt watchdog +^^^^^^^^^^^^^^^^^^ + +The interrupt watchdog makes sure the FreeRTOS task switching interrupt isn't blocked for a long time. This +is bad because no other tasks, including potentially important ones like the WiFi task and the idle task, +can't get any CPU runtime. A blocked task switching interrupt can happen because a program runs into an +infinite loop with interrupts disabled or hangs in an interrupt. + +The default action of the interrupt watchdog is to invoke the panic handler. causing a register dump and an opportunity +for the programmer to find out, using either OpenOCD or gdbstub, what bit of code is stuck with interrupts +disabled. Depending on the configuration of the panic handler, it can also blindly reset the CPU, which may be +preferred in a production environment. + +The interrupt watchdog is built around the hardware watchdog in timer group 1. If this watchdog for some reason +cannot execute the NMI handler that invokes the panic handler (e.g. because IRAM is overwritten by garbage), +it will hard-reset the SOC. + +Task watchdog +^^^^^^^^^^^^^ + +Any tasks can elect to be watched by the task watchdog. If such a task does not feed the watchdog within the time +specified by the task watchdog timeout (which is configurable using ``make menuconfig``), the watchdog will +print out a warning with information about which processes are running on the ESP32 CPUs and which processes +failed to feed the watchdog. + +By default, the task watchdog watches the idle tasks. The usual cause of idle tasks not feeding the watchdog +is a higher-priority process looping without yielding to the lower-priority processes, and can be an indicator +of badly-written code that spinloops on a peripheral or a task that is stuck in an infinite loop. + +Other task can elect to be watched by the task watchdog by calling ``esp_task_wdt_feed()``. Calling this routine +for the first time will register the task to the task watchdog; calling it subsequent times will feed +the watchdog. If a task does not want to be watched anymore (e.g. because it is finished and will call +``vTaskDelete()`` on itself), it needs to call ``esp_task_wdt_delete()``. + +The task watchdog is built around the hardware watchdog in timer group 0. If this watchdog for some reason +cannot execute the interrupt handler that prints the task data (e.g. because IRAM is overwritten by garbage +or interrupts are disabled entirely) it will hard-reset the SOC. + +JTAG and watchdogs +^^^^^^^^^^^^^^^^^^ + +While debugging using OpenOCD, if the CPUs are halted the watchdogs will keep running, eventually resetting the +CPU. This makes it very hard to debug code; that is why the OpenOCD config will disable both watchdogs on startup. +This does mean that you will not get any warnings or panics from either the task or interrupt watchdog when the ESP32 +is connected to OpenOCD via JTAG. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `esp32/include/esp_int_wdt.h `_ + * `esp32/include/esp_task_wdt.h `_ + + +Functions +--------- + +.. doxygenfunction:: esp_int_wdt_init +.. doxygenfunction:: esp_task_wdt_init +.. doxygenfunction:: esp_task_wdt_feed +.. doxygenfunction:: esp_task_wdt_delete diff --git a/docs/index.rst b/docs/index.rst index 1ca6e28eec..c19fc52c9a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -42,9 +42,9 @@ Contents: 1.2. Application startup flow - TBA 1.3. Flash encryption and secure boot: how they work and APIs - TBA 1.4. Lower Power Coprocessor - TBA - 1.5. Watchdogs + 1.5. Watchdogs 1.6. ... - 2. Memeory - TBA + 2. Memory - TBA 2.1. Memory layout of the application (IRAM/IROM, limitations of each) - TBA 2.2. Flash layout and partitions - TBA 2.3. Flash access APIs - TBA @@ -92,6 +92,7 @@ Contents: Wi-Fi Bluetooth + Watchdogs api/gpio api/uart From 02f5bb9442e7dac64086b1573030b9ba9b814466 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 17 Nov 2016 12:33:31 +0800 Subject: [PATCH 242/285] Add docs build artifacts to gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 5ec57a167f..2870b4a805 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,8 @@ GPATH examples/*/sdkconfig examples/*/sdkconfig.old examples/*/build + +#Doc build artifacts +docs/_build/ +docs/doxygen-warning-log.txt +docs/xml/ From 9e6e6b8ff649a682f4d693cd23e7c5cf97cb784d Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 17 Nov 2016 13:45:53 +0800 Subject: [PATCH 243/285] save logs as test_report artifacts --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e09b55c44..9aa23f27bf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -159,6 +159,7 @@ test_report: when: always paths: - $REPORT_PATH + - $LOG_PATH expire_in: 12 mos script: # clone test bench From b0ca2feabcc1e3aefccd289eec5a6cf4807e85b2 Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 17 Nov 2016 14:02:46 +0800 Subject: [PATCH 244/285] support build multiple bin for SSC --- .gitlab-ci.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e09b55c44..ad1750faf7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -63,10 +63,7 @@ build_ssc: <<: *build_template artifacts: paths: - - ./SSC/build/*.bin - - ./SSC/build/*.elf - - ./SSC/build/*.map - - ./SSC/build/bootloader/*.bin + - ./SSC/ssc_bin expire_in: 6 mos script: @@ -229,7 +226,7 @@ deploy_docs: variables: # LOCAL_ENV_CONFIG_PATH: define in template and jobs can overwrite if required LOCAL_ENV_CONFIG_PATH: /home/gitlab-runner/LocalConfig/ESP32_IDF - BIN_PATH: "$CI_PROJECT_DIR/SSC/build/" + BIN_PATH: "$CI_PROJECT_DIR/SSC/ssc_bin/SSC" APP_NAME: "ssc" LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" # append test level folder to TEST_CASE_FILE_PATH in before_script of test job From d73448f57f2c8ab44de4dad373cbc1a2e28fd9af Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 17 Nov 2016 18:05:47 +0800 Subject: [PATCH 245/285] Make sure task wdt also does the right thing when triggered when flash is disabled --- components/esp32/include/esp_attr.h | 4 ++++ components/esp32/task_wdt.c | 15 ++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index 78aa3bd190..7ef2920d98 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -26,6 +26,10 @@ // Forces data into DRAM instead of flash #define DRAM_ATTR __attribute__((section(".dram1"))) +// Forces a string into DRAM instrad of flash +// Use as ets_printf(DRAM_STR("Hello world!\n")); +#define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) + // Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst" #define RTC_IRAM_ATTR __attribute__((section(".rtc.text"))) diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index de5f9f54f9..6f39591259 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -49,6 +49,7 @@ struct wdt_task_t { static wdt_task_t *wdt_task_list=NULL; static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED; + static void IRAM_ATTR task_wdt_isr(void *arg) { wdt_task_t *wdttask; const char *cpu; @@ -69,21 +70,21 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { return; } //Watchdog got triggered because at least one task did not report in. - ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); + ets_printf(DRAM_STR("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n")); for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { if (!wdttask->fed_watchdog) { - cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; - if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; - ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1"); + if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1"); + ets_printf(DRAM_STR(" - %s (%s)\n"), pcTaskGetTaskName(wdttask->task_handle), cpu); } } - ets_printf("Tasks currently running:\n"); + ets_printf(DRAM_STR("Tasks currently running:\n")); for (int x=0; x Date: Fri, 18 Nov 2016 10:07:34 +0800 Subject: [PATCH 246/285] components/openssl_demo: remove the apache license header and add cc license head --- .../09_openssl_client/main/openssl_client.c | 19 +++++++------------ .../09_openssl_client/main/openssl_client.h | 9 +++++++++ .../10_openssl_server/main/openssl_server.c | 19 +++++++------------ .../10_openssl_server/main/openssl_server.h | 9 +++++++++ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/examples/09_openssl_client/main/openssl_client.c b/examples/09_openssl_client/main/openssl_client.c index c804b6c4fd..c6b0e449ac 100644 --- a/examples/09_openssl_client/main/openssl_client.c +++ b/examples/09_openssl_client/main/openssl_client.c @@ -1,16 +1,11 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +/* OpenSSL client Example -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ #include "openssl_client.h" diff --git a/examples/09_openssl_client/main/openssl_client.h b/examples/09_openssl_client/main/openssl_client.h index 5bc69a0aca..f5ab887ad4 100644 --- a/examples/09_openssl_client/main/openssl_client.h +++ b/examples/09_openssl_client/main/openssl_client.h @@ -1,3 +1,12 @@ +/* OpenSSL client Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + #ifndef _OPENSSL_DEMO_H_ #define _OPENSSL_DEMO_H_ diff --git a/examples/10_openssl_server/main/openssl_server.c b/examples/10_openssl_server/main/openssl_server.c index 7f4b7d6b6e..53b6050d57 100644 --- a/examples/10_openssl_server/main/openssl_server.c +++ b/examples/10_openssl_server/main/openssl_server.c @@ -1,16 +1,11 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +/* OpenSSL server Example -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ #include "openssl_server.h" diff --git a/examples/10_openssl_server/main/openssl_server.h b/examples/10_openssl_server/main/openssl_server.h index e87f5e482e..5f49de35f2 100644 --- a/examples/10_openssl_server/main/openssl_server.h +++ b/examples/10_openssl_server/main/openssl_server.h @@ -1,3 +1,12 @@ +/* OpenSSL server Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + #ifndef _OPENSSL_DEMO_H_ #define _OPENSSL_DEMO_H_ From 6b687b43f48252649af40f26ccca1e1599b65296 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Wed, 16 Nov 2016 20:37:51 +0800 Subject: [PATCH 247/285] mbedtls hardware RSA: Fix "mbedtls_mpi_exp_mod" hardware calculations --- components/mbedtls/port/esp_bignum.c | 158 ++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 4 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 0a835c9e8d..401d3fc24b 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -53,12 +53,11 @@ void mbedtls_mpi_printf(const char *name, const mbedtls_mpi *X) static char buf[1024]; size_t n; memset(buf, 0, sizeof(buf)); - printf("%s = 0x", name); mbedtls_mpi_write_string(X, 16, buf, sizeof(buf)-1, &n); if(n) { - puts(buf); + ESP_LOGI(TAG, "%s = 0x%s", name, buf); } else { - puts("TOOLONG"); + ESP_LOGI(TAG, "TOOLONG"); } } @@ -278,6 +277,7 @@ int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi /* * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) */ + #if 0 int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) { int ret; @@ -336,6 +336,155 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi return ret; } +#else + +/** + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b + */ +static mbedtls_mpi_uint modular_inverse(const mbedtls_mpi *M) +{ + int i; + uint64_t t = 1; + uint64_t two_2_i_minus_1 = 2; /* 2^(i-1) */ + uint64_t two_2_i = 4; /* 2^i */ + uint64_t N = M->p[0]; + + for (i = 2; i <= 32; i++) { + if ((mbedtls_mpi_uint) N * t % two_2_i >= two_2_i_minus_1) { + t += two_2_i_minus_1; + } + + two_2_i_minus_1 <<= 1; + two_2_i <<= 1; + } + + return (mbedtls_mpi_uint)(UINT32_MAX - t + 1); +} + +static int bignum_param_init(const mbedtls_mpi *M, mbedtls_mpi *_RR, mbedtls_mpi *r, mbedtls_mpi_uint *Mi, size_t num_words) +{ + int ret = 0; + size_t num_bits; + mbedtls_mpi RR; + + /* Calculate number of bits */ + num_bits = num_words * 32; + ESP_LOGI(TAG, "num_bits = %d\n", num_bits); + + /* + * R = b^n where b = 2^32, n=num_words, + * R = 2^N (where N=num_bits) + * RR(R^2) = 2^(2*N) (where N=num_bits) + * + * r = RR(R^2) mod M + * + * Get the RR(RR == r) value from up level if RR and RR->p is not NULL + */ + ESP_LOGI(TAG, "r = RR(R^2) mod M\n"); + if (_RR == NULL || _RR->p == NULL) { + ESP_LOGI(TAG, "RR(R^2) = 2^(2*N) (where N=num_bits)\n"); + mbedtls_mpi_init(&RR); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1)); + mbedtls_mpi_printf("RR", &RR); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(r, &RR, M)); + + if (_RR != NULL) + memcpy(_RR, r, sizeof( mbedtls_mpi ) ); + } else { + memcpy(r, _RR, sizeof( mbedtls_mpi ) ); + } + mbedtls_mpi_printf("r", r); + + *Mi = modular_inverse(M); + +cleanup: + mbedtls_mpi_free(&RR); + + return ret; +} + +static void bignum_param_deinit(mbedtls_mpi *_RR, mbedtls_mpi *r) +{ + if (_RR == NULL || _RR->p == NULL) + mbedtls_mpi_free(r); +} + +/* + * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) + */ +int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) +{ + int ret = 0; + size_t z_words = hardware_words_needed(Z); + size_t x_words = hardware_words_needed(X); + size_t y_words = hardware_words_needed(Y); + size_t m_words = hardware_words_needed(M); + size_t num_words; + + mbedtls_mpi r; + mbedtls_mpi_uint Mi = 0; + + /* "all numbers must be the same length", so choose longest number + as cardinal length of operation... + */ + num_words = z_words; + if (x_words > num_words) { + num_words = x_words; + } + if (y_words > num_words) { + num_words = y_words; + } + if (m_words > num_words) { + num_words = m_words; + } + ESP_LOGI(TAG, "num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); + + if (num_words * 32 > 4096) + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + + mbedtls_mpi_init(&r); + ret = bignum_param_init(M, _RR, &r, &Mi, num_words); + if (ret != 0) { + return ret; + } + + mbedtls_mpi_printf("X",X); + mbedtls_mpi_printf("Y",Y); + + esp_mpi_acquire_hardware(); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MODEXP_MODE_REG, (num_words / 16) - 1); + + /* Load M, X, Rinv, M-prime (M-prime is mod 2^32) */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &r, num_words); + REG_WRITE(RSA_M_DASH_REG, Mi); + + execute_op(RSA_START_MODEXP_REG); + + ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); + + esp_mpi_release_hardware(); + + mbedtls_mpi_printf("Z",Z); + ESP_LOGI(TAG, "print (Z == (X ** Y) %% M)\n"); + + bignum_param_deinit(_RR, &r); + + return ret; +} + + +#endif + #endif /* MBEDTLS_MPI_EXP_MOD_ALT */ @@ -385,7 +534,7 @@ static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t /* Block of debugging data, output suitable to paste into Python TODO remove */ - mbedtls_mpi_printf("R", &RR); + mbedtls_mpi_printf("RR", &RR); mbedtls_mpi_printf("M", M); mbedtls_mpi_printf("Rinv", &Rinv); mbedtls_mpi_printf("Mprime", &Mprime); @@ -463,6 +612,7 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi multiplication doesn't have the same restriction, so result is simply the number of bits in X plus number of bits in in Y.) */ + //ESP_LOGE(TAG, "INFO: %d bit result (%d bits * %d bits)\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); if (words_mult * 32 > 2048) { /* Calculate new length of Z */ words_z = words_x + words_y; From f87be70d51e60ea62ed5d16cf93563e000b96702 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 13:44:37 +1100 Subject: [PATCH 248/285] mbedtls hardware RSA: Combine methods for calculating M' & r inverse Remove redundant gcd calculation, use consistent terminology. Also remove leftover debugging code --- components/mbedtls/port/esp_bignum.c | 586 +++++++++------------------ 1 file changed, 191 insertions(+), 395 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 401d3fc24b..076234b55b 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -39,46 +39,10 @@ static const char *TAG = "bignum"; #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) -/* Constants from mbedTLS bignum.c */ -#define ciL (sizeof(mbedtls_mpi_uint)) /* chars in limb */ -#define biL (ciL << 3) /* bits in limb */ - static _lock_t mpi_lock; -/* Temporary debugging function to print an MPI number to - stdout. Happens to be in a format compatible with Python. -*/ -void mbedtls_mpi_printf(const char *name, const mbedtls_mpi *X) -{ - static char buf[1024]; - size_t n; - memset(buf, 0, sizeof(buf)); - mbedtls_mpi_write_string(X, 16, buf, sizeof(buf)-1, &n); - if(n) { - ESP_LOGI(TAG, "%s = 0x%s", name, buf); - } else { - ESP_LOGI(TAG, "TOOLONG"); - } -} - -/* Temporary debug function to dump a memory block's contents to stdout - TODO remove - */ -static void __attribute__((unused)) dump_memory_block(const char *label, uint32_t addr) -{ - printf("Dumping %s @ %08x\n", label, addr); - for(int i = 0; i < (4096 / 8); i += 4) { - if(i % 32 == 0) { - printf("\n %04x:", i); - } - printf("%08x ", REG_READ(addr + i)); - } - printf("Done\n"); -} - /* At the moment these hardware locking functions aren't exposed publically - for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, - please raise a feature request. + for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, please raise a feature request. */ static void esp_mpi_acquire_hardware( void ) { @@ -116,6 +80,14 @@ static inline size_t hardware_words_needed(const mbedtls_mpi *mpi) return res; } +/* Convert number of bits to number of words, rounded up to nearest + 512 bit (16 word) block count. +*/ +static inline size_t bits_to_hardware_words(size_t num_bits) +{ + return ((num_bits + 511) / 512) * 16; +} + /* Copy mbedTLS MPI bignum 'mpi' to hardware memory block at 'mem_base'. If num_words is higher than the number of words in the bignum then @@ -156,194 +128,14 @@ static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_wo return ret; } -/* Given a & b, determine u & v such that - - gcd(a,b) = d = au - bv - - This is suitable for calculating values for montgomery multiplication: - - gcd(R, M) = R * Rinv - M * Mprime = 1 - - Conditions which must be true: - - argument 'a' (R) is a power of 2. - - argument 'b' (M) is odd. - - Underlying algorithm comes from: - http://www.hackersdelight.org/hdcodetxt/mont64.c.txt - http://www.ucl.ac.uk/~ucahcjm/combopt/ext_gcd_python_programs.pdf - */ -static void extended_binary_gcd(const mbedtls_mpi *a, const mbedtls_mpi *b, - mbedtls_mpi *u, mbedtls_mpi *v) -{ - mbedtls_mpi a_, ta; - - /* These checks degrade performance, TODO remove them... */ - assert(b->p[0] & 1); - assert(mbedtls_mpi_bitlen(a) == mbedtls_mpi_lsb(a)+1); - assert(mbedtls_mpi_cmp_mpi(a, b) > 0); - - mbedtls_mpi_lset(u, 1); - mbedtls_mpi_lset(v, 0); - - /* 'a' needs to be half its real value for this algorithm - TODO see if we can halve the number in the caller to avoid - allocating a bignum here. - */ - mbedtls_mpi_init(&a_); - mbedtls_mpi_copy(&a_, a); - mbedtls_mpi_shift_r(&a_, 1); - - mbedtls_mpi_init(&ta); - mbedtls_mpi_copy(&ta, &a_); - - //mbedtls_mpi_printf("a", &a_); - //mbedtls_mpi_printf("b", b); - - /* Loop invariant: - 2*ta = u*2*a - v*b. - - Loop until ta == 0 - */ - while (mbedtls_mpi_cmp_int(&ta, 0) != 0) { - //mbedtls_mpi_printf("ta", &ta); - //mbedtls_mpi_printf("u", u); - //mbedtls_mpi_printf("v", v); - //printf("2*ta == u*2*a - v*b\n"); - - mbedtls_mpi_shift_r(&ta, 1); - if (mbedtls_mpi_get_bit(u, 0) == 0) { - // Remove common factor of 2 in u & v - mbedtls_mpi_shift_r(u, 1); - mbedtls_mpi_shift_r(v, 1); - } - else { - /* u = (u + b) >> 1 */ - mbedtls_mpi_add_mpi(u, u, b); - mbedtls_mpi_shift_r(u, 1); - /* v = (v - a) >> 1 */ - mbedtls_mpi_shift_r(v, 1); - mbedtls_mpi_add_mpi(v, v, &a_); - } - } - mbedtls_mpi_free(&ta); - mbedtls_mpi_free(&a_); -} - -/* Execute RSA operation. op_reg specifies which 'START' register - to write to. -*/ -static inline void execute_op(uint32_t op_reg) -{ - /* Clear interrupt status, start operation */ - REG_WRITE(RSA_INTERRUPT_REG, 1); - REG_WRITE(op_reg, 1); - - /* TODO: use interrupt instead of busywaiting */ - while(REG_READ(RSA_INTERRUPT_REG) != 1) - { } - - /* clear the interrupt */ - REG_WRITE(RSA_INTERRUPT_REG, 1); -} - -/* Sub-stages of modulo multiplication/exponentiation operations */ -static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words); -inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); - -/* Z = (X * Y) mod M - - Not an mbedTLS function - */ -int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M) -{ - int ret; - size_t num_words = hardware_words_needed(M); - - /* Calculate and load the first stage montgomery multiplication */ - MBEDTLS_MPI_CHK( modular_op_prepare(X, M, num_words) ); - - execute_op(RSA_MULT_START_REG); - - MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); - - esp_mpi_release_hardware(); - - cleanup: - return ret; -} - -#if defined(MBEDTLS_MPI_EXP_MOD_ALT) - -/* - * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) - */ - #if 0 -int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) -{ - int ret; - size_t z_words = hardware_words_needed(Z); - size_t x_words = hardware_words_needed(X); - size_t y_words = hardware_words_needed(Y); - size_t m_words = hardware_words_needed(M); - size_t num_words; - - mbedtls_mpi_printf("X",X); - mbedtls_mpi_printf("Y",Y); - mbedtls_mpi_printf("M",M); - - /* "all numbers must be the same length", so choose longest number - as cardinal length of operation... - */ - num_words = z_words; - if (x_words > num_words) { - num_words = x_words; - } - if (y_words > num_words) { - num_words = y_words; - } - if (m_words > num_words) { - num_words = m_words; - } - printf("num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); - - /* TODO: _RR parameter currently ignored */ - - ret = modular_op_prepare(X, M, num_words); - if (ret != 0) { - return ret; - } - - mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); - - //dump_memory_block("X_BLOCK", RSA_MEM_X_BLOCK_BASE); - //dump_memory_block("Y_BLOCK", RSA_MEM_Y_BLOCK_BASE); - //dump_memory_block("M_BLOCK", RSA_MEM_M_BLOCK_BASE); - - REG_WRITE(RSA_MODEXP_MODE_REG, (num_words / 16) - 1); - - execute_op(RSA_START_MODEXP_REG); - - //dump_memory_block("Z_BLOCK", RSA_MEM_Z_BLOCK_BASE); - - /* TODO: only need to read m_words not num_words, provided result is correct... */ - ret = mem_block_to_mpi(Z, RSA_MEM_Z_BLOCK_BASE, num_words); - - esp_mpi_release_hardware(); - - mbedtls_mpi_printf("Z",Z); - printf("print (Z == (X ** Y) %% M)\n"); - - return ret; -} - -#else /** - * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, - * where B^-1(B-1) mod N=1. Actually, only the least significant part of - * N' is needed, hence the definition N0'=N' mod b. We reproduce below the - * simple algorithm from an article by Dusse and Kaliski to efficiently - * find N0' from N0 and b + * + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b */ static mbedtls_mpi_uint modular_inverse(const mbedtls_mpi *M) { @@ -365,59 +157,104 @@ static mbedtls_mpi_uint modular_inverse(const mbedtls_mpi *M) return (mbedtls_mpi_uint)(UINT32_MAX - t + 1); } -static int bignum_param_init(const mbedtls_mpi *M, mbedtls_mpi *_RR, mbedtls_mpi *r, mbedtls_mpi_uint *Mi, size_t num_words) +/* Calculate Rinv = RR^2 mod M, where: + * + * R = b^n where b = 2^32, n=num_words, + * R = 2^N (where N=num_bits) + * RR = R^2 = 2^(2*N) (where N=num_bits=num_words*32) + * + * This calculation is computationally expensive (mbedtls_mpi_mod_mpi) + * so caller should cache the result where possible. + * + * DO NOT call this function while holding esp_mpi_acquire_hardware(). + * + */ +static int calculate_rinv(mbedtls_mpi *Rinv, const mbedtls_mpi *M, int num_words) { - int ret = 0; - size_t num_bits; + int ret; + size_t num_bits = num_words * 32; mbedtls_mpi RR; + mbedtls_mpi_init(&RR); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(Rinv, &RR, M)); - /* Calculate number of bits */ - num_bits = num_words * 32; - ESP_LOGI(TAG, "num_bits = %d\n", num_bits); - - /* - * R = b^n where b = 2^32, n=num_words, - * R = 2^N (where N=num_bits) - * RR(R^2) = 2^(2*N) (where N=num_bits) - * - * r = RR(R^2) mod M - * - * Get the RR(RR == r) value from up level if RR and RR->p is not NULL - */ - ESP_LOGI(TAG, "r = RR(R^2) mod M\n"); - if (_RR == NULL || _RR->p == NULL) { - ESP_LOGI(TAG, "RR(R^2) = 2^(2*N) (where N=num_bits)\n"); - mbedtls_mpi_init(&RR); - MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&RR, num_bits * 2, 1)); - mbedtls_mpi_printf("RR", &RR); - - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(r, &RR, M)); - - if (_RR != NULL) - memcpy(_RR, r, sizeof( mbedtls_mpi ) ); - } else { - memcpy(r, _RR, sizeof( mbedtls_mpi ) ); - } - mbedtls_mpi_printf("r", r); - - *Mi = modular_inverse(M); - -cleanup: + cleanup: mbedtls_mpi_free(&RR); - return ret; } -static void bignum_param_deinit(mbedtls_mpi *_RR, mbedtls_mpi *r) + +/* Execute RSA operation. op_reg specifies which 'START' register + to write to. +*/ +static inline void execute_op(uint32_t op_reg) { - if (_RR == NULL || _RR->p == NULL) - mbedtls_mpi_free(r); + /* Clear interrupt status, start operation */ + REG_WRITE(RSA_INTERRUPT_REG, 1); + REG_WRITE(op_reg, 1); + + /* TODO: use interrupt instead of busywaiting */ + while(REG_READ(RSA_INTERRUPT_REG) != 1) + { } + + /* clear the interrupt */ + REG_WRITE(RSA_INTERRUPT_REG, 1); } +/* Sub-stages of modulo multiplication/exponentiation operations */ +inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); + +/* Z = (X * Y) mod M + + Not an mbedTLS function +*/ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M) +{ + int ret; + size_t num_words = hardware_words_needed(M); + mbedtls_mpi Rinv; + mbedtls_mpi_uint Mprime; + + /* Calculate and load the first stage montgomery multiplication */ + mbedtls_mpi_init(&Rinv); + MBEDTLS_MPI_CHK(calculate_rinv(&Rinv, M, num_words)); + Mprime = modular_inverse(M); + + esp_mpi_acquire_hardware(); + + /* Load M, X, Rinv, Mprime (Mprime is mod 2^32) */ + mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, num_words); + REG_WRITE(RSA_M_DASH_REG, (uint32_t)Mprime); + + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + + /* Execute first stage montgomery multiplication */ + execute_op(RSA_MULT_START_REG); + + /* execute second stage */ + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); + + esp_mpi_release_hardware(); + + cleanup: + mbedtls_mpi_free(&Rinv); + return ret; +} + +#if defined(MBEDTLS_MPI_EXP_MOD_ALT) + /* * Sliding-window exponentiation: Z = X^Y mod M (HAC 14.85) + * + * _Rinv is optional pre-calculated version of Rinv (via calculate_rinv()). + * + * (See RSA Accelerator section in Technical Reference for more about Mprime, Rinv) + * */ -int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _RR ) +int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi* Y, const mbedtls_mpi* M, mbedtls_mpi* _Rinv ) { int ret = 0; size_t z_words = hardware_words_needed(Z); @@ -426,8 +263,9 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi size_t m_words = hardware_words_needed(M); size_t num_words; - mbedtls_mpi r; - mbedtls_mpi_uint Mi = 0; + mbedtls_mpi Rinv_new; /* used if _Rinv == NULL */ + mbedtls_mpi *Rinv; /* points to _Rinv (if not NULL) othwerwise &RR_new */ + mbedtls_mpi_uint Mprime; /* "all numbers must be the same length", so choose longest number as cardinal length of operation... @@ -442,19 +280,24 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi if (m_words > num_words) { num_words = m_words; } - ESP_LOGI(TAG, "num_words = %d # %d, %d, %d\n", num_words, x_words, y_words, m_words); - if (num_words * 32 > 4096) + if (num_words * 32 > 4096) { return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; - - mbedtls_mpi_init(&r); - ret = bignum_param_init(M, _RR, &r, &Mi, num_words); - if (ret != 0) { - return ret; } - mbedtls_mpi_printf("X",X); - mbedtls_mpi_printf("Y",Y); + /* Determine RR pointer, either _RR for cached value + or local RR_new */ + if (_Rinv == NULL) { + mbedtls_mpi_init(&Rinv_new); + Rinv = &Rinv_new; + } else { + Rinv = _Rinv; + } + if (Rinv->p == NULL) { + MBEDTLS_MPI_CHK(calculate_rinv(Rinv, M, num_words)); + } + + Mprime = modular_inverse(M); esp_mpi_acquire_hardware(); @@ -465,8 +308,8 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); mpi_to_mem_block(RSA_MEM_Y_BLOCK_BASE, Y, num_words); mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); - mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &r, num_words); - REG_WRITE(RSA_M_DASH_REG, Mi); + mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, Rinv, num_words); + REG_WRITE(RSA_M_DASH_REG, Mprime); execute_op(RSA_START_MODEXP_REG); @@ -474,91 +317,16 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi esp_mpi_release_hardware(); - mbedtls_mpi_printf("Z",Z); - ESP_LOGI(TAG, "print (Z == (X ** Y) %% M)\n"); - - bignum_param_deinit(_RR, &r); - - return ret; -} - - -#endif - -#endif /* MBEDTLS_MPI_EXP_MOD_ALT */ - - -/* The common parts of modulo multiplication and modular sliding - * window exponentiation: - * - * @param X first multiplication factor and/or base of exponent. - * @param M modulo value for result - * @param num_words size of modulo operation, in words (limbs). - * Should already be rounded up to a multiple of 16 words (512 bits) & range checked. - * - * Steps: - * Calculate Rinv & Mprime based on M & num_words - * Load all coefficients to memory - * Set mode register - * - * @note This function calls esp_mpi_acquire_hardware. If successful, - * returns 0 and it becomes the callers responsibility to call - * esp_mpi_release_hardware(). If failure is returned, the caller does - * not need to call esp_mpi_release_hardware(). - */ -static int modular_op_prepare(const mbedtls_mpi *X, const mbedtls_mpi *M, size_t num_words) -{ - int ret = 0; - mbedtls_mpi RR, Rinv, Mprime; - size_t num_bits; - - /* Calculate number of bits */ - num_bits = num_words * 32; - - if(num_bits > 4096) { - return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + cleanup: + if (_Rinv == NULL) { + mbedtls_mpi_free(&Rinv_new); } - /* Rinv & Mprime are calculated via extended binary gcd - algorithm, see references on extended_binary_gcd() above. - */ - mbedtls_mpi_init(&Rinv); - mbedtls_mpi_init(&RR); - mbedtls_mpi_init(&Mprime); - - mbedtls_mpi_set_bit(&RR, num_bits, 1); /* R = b^n where b = 2^32, n=num_words, - ie R = 2^N (where N=num_bits) */ - /* calculate Rinv & Mprime */ - extended_binary_gcd(&RR, M, &Rinv, &Mprime); - - /* Block of debugging data, output suitable to paste into Python - TODO remove - */ - mbedtls_mpi_printf("RR", &RR); - mbedtls_mpi_printf("M", M); - mbedtls_mpi_printf("Rinv", &Rinv); - mbedtls_mpi_printf("Mprime", &Mprime); - printf("print (R * Rinv - M * Mprime == 1)\n"); - printf("print (Rinv == (R * R) %% M)\n"); - - esp_mpi_acquire_hardware(); - - /* Load M, X, Rinv, M-prime (M-prime is mod 2^32) */ - mpi_to_mem_block(RSA_MEM_M_BLOCK_BASE, M, num_words); - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); - mpi_to_mem_block(RSA_MEM_RB_BLOCK_BASE, &Rinv, num_words); - REG_WRITE(RSA_M_DASH_REG, Mprime.p[0]); - - /* "mode" register loaded with number of 512-bit blocks, minus 1 */ - REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); - - mbedtls_mpi_free(&Rinv); - mbedtls_mpi_free(&RR); - mbedtls_mpi_free(&Mprime); - return ret; } +#endif /* MBEDTLS_MPI_EXP_MOD_ALT */ + /* Second & final step of a modular multiply - load second multiplication * factor Y, run the multiply, read back the result into Z. * @@ -594,17 +362,43 @@ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y ) { int ret; - size_t words_x, words_y, words_mult, words_z; + size_t bits_x, bits_y, words_x, words_y, words_mult, words_z; /* Count words needed for X & Y in hardware */ - words_x = hardware_words_needed(X); - words_y = hardware_words_needed(Y); + bits_x = mbedtls_mpi_bitlen(X); + bits_y = mbedtls_mpi_bitlen(Y); + /* Convert bit counts to words, rounded up to 512-bit + (16 word) blocks */ + words_x = bits_to_hardware_words(bits_x); + words_y = bits_to_hardware_words(bits_y); + + /* Short-circuit eval if either argument is 0 or 1. + + This is needed as the mpi modular division + argument will sometimes call in here when one + argument is too large for the hardware unit, but the other + argument is zero or one. + + This leaks some timing information, although overall there is a + lot less timing variation than a software MPI approach. + */ + if (bits_x == 0 || bits_y == 0) { + mbedtls_mpi_lset(Z, 0); + return 0; + } + if (bits_x == 1) { + return mbedtls_mpi_copy(Z, Y); + } + if (bits_y == 1) { + return mbedtls_mpi_copy(Z, X); + } words_mult = (words_x > words_y ? words_x : words_y); /* Result Z has to have room for double the larger factor */ words_z = words_mult * 2; + /* If either factor is over 2048 bits, we can't use the standard hardware multiplier (it assumes result is double longest factor, and result is max 4096 bits.) @@ -612,12 +406,11 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi multiplication doesn't have the same restriction, so result is simply the number of bits in X plus number of bits in in Y.) */ - //ESP_LOGE(TAG, "INFO: %d bit result (%d bits * %d bits)\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); if (words_mult * 32 > 2048) { /* Calculate new length of Z */ - words_z = words_x + words_y; + words_z = bits_to_hardware_words(bits_x + bits_y); if (words_z * 32 > 4096) { - ESP_LOGE(TAG, "ERROR: %d bit result (%d bits * %d bits) too large for hardware unit\n", words_z * 32, mbedtls_mpi_bitlen(X), mbedtls_mpi_bitlen(Y)); + ESP_LOGE(TAG, "ERROR: %d bit result %d bits * %d bits too large for hardware unit\n", words_z * 32, bits_x, bits_y); return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; } else { @@ -640,7 +433,7 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi /* "mode" register loaded with number of 512-bit blocks in result, plus 7 (for range 9-12). (this is ((N~ / 32) - 1) + 8)) - */ + */ REG_WRITE(RSA_MULT_MODE_REG, (words_z / 16) + 7); execute_op(RSA_MULT_START_REG); @@ -656,54 +449,57 @@ int mbedtls_mpi_mul_mpi( mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi } /* Special-case of mbedtls_mpi_mult_mpi(), where we use hardware montgomery mod - multiplication to solve the case where A or B are >2048 bits so - can't use the standard multiplication method. + multiplication to calculate an mbedtls_mpi_mult_mpi result where either + A or B are >2048 bits so can't use the standard multiplication method. - This case is simpler than esp_mpi_mul_mpi_mod() as we control the arguments: + Result (A bits + B bits) must still be less than 4096 bits. + + This case is simpler than the general case modulo multiply of + esp_mpi_mul_mpi_mod() because we can control the other arguments: * Modulus is chosen with M=(2^num_bits - 1) (ie M=R-1), so output - isn't actually modulo anything. - * Therefore of of M' and Rinv are predictable as follows: - M' = 1 - Rinv = 1 + isn't actually modulo anything. + * Mprime and Rinv are therefore predictable as follows: + Mprime = 1 + Rinv = 1 - (See RSA Accelerator section in Technical Reference * - extended_binary_gcd() function above for more about M', Rinv) + (See RSA Accelerator section in Technical Reference for more about Mprime, Rinv) */ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) - { - int ret = 0; +{ + int ret = 0; - /* Load coefficients to hardware */ - esp_mpi_acquire_hardware(); + /* Load coefficients to hardware */ + esp_mpi_acquire_hardware(); - /* M = 2^num_words - 1, so block is entirely FF */ - for(int i = 0; i < num_words; i++) { - REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX); - } - /* Mprime = 1 */ - REG_WRITE(RSA_M_DASH_REG, 1); + /* M = 2^num_words - 1, so block is entirely FF */ + for(int i = 0; i < num_words; i++) { + REG_WRITE(RSA_MEM_M_BLOCK_BASE + i * 4, UINT32_MAX); + } + /* Mprime = 1 */ + REG_WRITE(RSA_M_DASH_REG, 1); - /* "mode" register loaded with number of 512-bit blocks, minus 1 */ - REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); + /* "mode" register loaded with number of 512-bit blocks, minus 1 */ + REG_WRITE(RSA_MULT_MODE_REG, (num_words / 16) - 1); - /* Load X */ - mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); + /* Load X */ + mpi_to_mem_block(RSA_MEM_X_BLOCK_BASE, X, num_words); - /* Rinv = 1 */ - REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1); - for(int i = 1; i < num_words; i++) { - REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0); - } + /* Rinv = 1 */ + REG_WRITE(RSA_MEM_RB_BLOCK_BASE, 1); + for(int i = 1; i < num_words; i++) { + REG_WRITE(RSA_MEM_RB_BLOCK_BASE + i * 4, 0); + } - execute_op(RSA_MULT_START_REG); + execute_op(RSA_MULT_START_REG); - MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); + /* finish the modular multiplication */ + MBEDTLS_MPI_CHK( modular_multiply_finish(Z, X, Y, num_words) ); - esp_mpi_release_hardware(); + esp_mpi_release_hardware(); cleanup: - return ret; + return ret; } #endif /* MBEDTLS_MPI_MUL_MPI_ALT */ From 68d370542a19c3d1b406a2ba6da3b8f088e541ae Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 14:26:02 +1100 Subject: [PATCH 249/285] mbedtls hardware RSA: Put into menuconfig, squash warnings All combinations of enabling/disabling hardware acceleration no longer show unused warnings. --- components/mbedtls/Kconfig | 21 ++++++++++++++++++- components/mbedtls/library/bignum.c | 8 ++++++- components/mbedtls/port/esp_bignum.c | 8 ++++--- .../mbedtls/port/include/mbedtls/esp_config.h | 4 ++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 60facc7d27..bb2aa8f6a3 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -22,7 +22,7 @@ config MBEDTLS_SSL_MAX_CONTENT_LEN config MBEDTLS_DEBUG bool "Enable mbedTLS debugging" - default "no" + default n help Enable mbedTLS debugging functions. @@ -34,4 +34,23 @@ config MBEDTLS_DEBUG functionality. See the "https_request_main" example for a sample function which connects the two together. +config MBEDTLS_HARDWARE_AES + bool "Enable hardware AES acceleration" + default y + help + Enable hardware accelerated AES encryption & decryption. + +config MBEDTLS_HARDWARE_MPI + bool "Enable hardware MPI (bignum) acceleration" + default y + help + Enable hardware accelerated multiple precision integer operations. + + Hardware accelerated multiplication, modulo multiplication, + and modular exponentiation for up to 4096 bit results. + + These operations are used by RSA. + + + endmenu diff --git a/components/mbedtls/library/bignum.c b/components/mbedtls/library/bignum.c index e739bc1d38..04ff9e07b2 100644 --- a/components/mbedtls/library/bignum.c +++ b/components/mbedtls/library/bignum.c @@ -1092,6 +1092,8 @@ int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint return( mbedtls_mpi_sub_mpi( X, A, &_B ) ); } +#if !defined(MBEDTLS_MPI_MUL_MPI_ALT) || !defined(MBEDTLS_MPI_EXP_MOD_ALT) + /* * Helper for mbedtls_mpi multiplication */ @@ -1103,6 +1105,7 @@ static */ __attribute__ ((noinline)) #endif + void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mpi_uint b ) { mbedtls_mpi_uint c = 0, t = 0; @@ -1164,6 +1167,8 @@ void mpi_mul_hlp( size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mp while( c != 0 ); } +#endif + #if !defined(MBEDTLS_MPI_MUL_MPI_ALT) /* * Baseline multiplication: X = A * B (HAC 14.12) @@ -1526,6 +1531,8 @@ int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, mbedtls_mpi_ return( 0 ); } +#if !defined(MBEDTLS_MPI_EXP_MOD_ALT) + /* * Fast Montgomery initialization (thanks to Tom St Denis) */ @@ -1600,7 +1607,6 @@ static int mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N, mbedtls_mpi_uint m return( mpi_montmul( A, &U, N, mm, T ) ); } -#if !defined(MBEDTLS_MPI_EXP_MOD_ALT) /* * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) */ diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 076234b55b..11a1c63973 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -35,8 +35,6 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -static const char *TAG = "bignum"; - #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) static _lock_t mpi_lock; @@ -330,6 +328,8 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi /* Second & final step of a modular multiply - load second multiplication * factor Y, run the multiply, read back the result into Z. * + * Called from both mbedtls_mpi_exp_mod and mbedtls_mpi_mod_mpi. + * * @param Z result value * @param X first multiplication factor (used to set sign of result). * @param Y second multiplication factor. @@ -338,7 +338,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi* Z, const mbedtls_mpi* X, const mbedtls_mpi * * Caller must have already called esp_mpi_acquire_hardware(). */ -inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) +static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words) { int ret; /* Load Y to X input memory block, rerun */ @@ -356,6 +356,8 @@ inline static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, #if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ +static const char *TAG = "bignum"; + static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); /* Z = X * Y */ diff --git a/components/mbedtls/port/include/mbedtls/esp_config.h b/components/mbedtls/port/include/mbedtls/esp_config.h index 2b47d84ea4..db87c6ef31 100644 --- a/components/mbedtls/port/include/mbedtls/esp_config.h +++ b/components/mbedtls/port/include/mbedtls/esp_config.h @@ -239,7 +239,9 @@ /* The following units have ESP32 hardware support, uncommenting each _ALT macro will use the hardware-accelerated implementation. */ +#ifdef CONFIG_MBEDTLS_HARDWARE_AES #define MBEDTLS_AES_ALT +#endif /* Currently hardware SHA does not work with TLS handshake, due to concurrency issue. Internal TW#7111. */ @@ -251,8 +253,10 @@ Uncommenting these macros will use the hardware-accelerated implementations. */ +#ifdef CONFIG_MBEDTLS_HARDWARE_MPI #define MBEDTLS_MPI_EXP_MOD_ALT #define MBEDTLS_MPI_MUL_MPI_ALT +#endif /** * \def MBEDTLS_MD2_PROCESS_ALT From 36f29017b62336ecf58d52b91f88b3c11ab36fe8 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 15:53:00 +1100 Subject: [PATCH 250/285] mbedtls hardware bignum: Support "RSA" interrupt for end of operation Allows CPU to do other things which bignum operation is in progress. --- components/mbedtls/Kconfig | 16 ++++++++++ components/mbedtls/port/esp_bignum.c | 48 +++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index bb2aa8f6a3..d6e2a2dcb7 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -51,6 +51,22 @@ config MBEDTLS_HARDWARE_MPI These operations are used by RSA. +config MBEDTLS_MPI_USE_INTERRUPT + bool "Use interrupt for MPI operations" + depends on MBEDTLS_HARDWARE_MPI + default y + help + Use an interrupt to coordinate MPI operations. + This allows other code to run on the CPU while an MPI operation is pending. + Otherwise the CPU busy-waits. + +config MBEDTLS_MPI_INTERRUPT_NUM + int "MPI Interrupt number" + depends on MBEDTLS_MPI_USE_INTERRUPT + default 18 + help + CPU interrupt number for MPI interrupt to connect to. Must be otherwise unused. + Eventually this assignment will be handled automatically at runtime. endmenu diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 11a1c63973..2133e7cae6 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -31,12 +31,43 @@ #include "soc/hwcrypto_reg.h" #include "esp_system.h" #include "esp_log.h" +#include "esp_intr.h" +#include "esp_attr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/semphr.h" #if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) +static const char *TAG = "bignum"; + +#if defined(CONFIG_MBEDTLS_MPI_USE_INTERRUPT) +static SemaphoreHandle_t op_complete_sem; + +static IRAM_ATTR void rsa_complete_isr(void *arg) +{ + BaseType_t higher_woken; + REG_WRITE(RSA_INTERRUPT_REG, 1); + xSemaphoreGiveFromISR(op_complete_sem, &higher_woken); + if (higher_woken) { + portYIELD_FROM_ISR(); + } +} + +static void rsa_isr_initialise() +{ + if (op_complete_sem == NULL) { + op_complete_sem = xSemaphoreCreateBinary(); + intr_matrix_set(xPortGetCoreID(), ETS_RSA_INTR_SOURCE, CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + xt_set_interrupt_handler(CONFIG_MBEDTLS_MPI_INTERRUPT_NUM, &rsa_complete_isr, NULL); + xthal_set_intclear(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + xt_ints_on(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + } +} + +#endif /* CONFIG_MBEDTLS_MPI_USE_INTERRUPT */ + static _lock_t mpi_lock; /* At the moment these hardware locking functions aren't exposed publically @@ -47,6 +78,9 @@ static void esp_mpi_acquire_hardware( void ) /* newlib locks lazy initialize on ESP-IDF */ _lock_acquire(&mpi_lock); ets_bigint_enable(); +#ifdef CONFIG_MBEDTLS_MPI_USE_INTERRUPT + rsa_isr_initialise(); +#endif } static void esp_mpi_release_hardware( void ) @@ -187,13 +221,21 @@ static int calculate_rinv(mbedtls_mpi *Rinv, const mbedtls_mpi *M, int num_words */ static inline void execute_op(uint32_t op_reg) { - /* Clear interrupt status, start operation */ + /* Clear interrupt status */ REG_WRITE(RSA_INTERRUPT_REG, 1); + REG_WRITE(op_reg, 1); - /* TODO: use interrupt instead of busywaiting */ +#ifdef CONFIG_MBEDTLS_MPI_USE_INTERRUPT + if (!xSemaphoreTake(op_complete_sem, 2000 / portTICK_PERIOD_MS)) { + ESP_LOGE(TAG, "Timed out waiting for RSA operation (op_reg 0x%x int_reg 0x%x)", + op_reg, REG_READ(RSA_INTERRUPT_REG)); + abort(); /* indicates a fundamental problem with driver */ + } +#else while(REG_READ(RSA_INTERRUPT_REG) != 1) { } +#endif /* clear the interrupt */ REG_WRITE(RSA_INTERRUPT_REG, 1); @@ -356,8 +398,6 @@ static int modular_multiply_finish(mbedtls_mpi *Z, const mbedtls_mpi *X, const m #if defined(MBEDTLS_MPI_MUL_MPI_ALT) /* MBEDTLS_MPI_MUL_MPI_ALT */ -static const char *TAG = "bignum"; - static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, size_t num_words); /* Z = X * Y */ From 1cc0b3000b699e9ea451a18c9f255da1e590a62d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 18 Nov 2016 16:38:22 +1100 Subject: [PATCH 251/285] mbedtls hardware bignum: Expose ESP-only bignum API in wrapper mbedtls/bignum.h --- components/mbedtls/port/esp_bignum.c | 14 +--- components/mbedtls/port/include/aes_alt.h | 3 +- .../mbedtls/port/include/mbedtls/bignum.h | 78 +++++++++++++++++++ components/mbedtls/port/include/sha1_alt.h | 17 +++- components/mbedtls/port/include/sha256_alt.h | 18 +++-- components/mbedtls/port/include/sha512_alt.h | 19 +++-- 6 files changed, 121 insertions(+), 28 deletions(-) create mode 100644 components/mbedtls/port/include/mbedtls/bignum.h diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 2133e7cae6..68fd8bec98 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -26,7 +26,6 @@ #include #include #include "mbedtls/bignum.h" -#include "mbedtls/bn_mul.h" #include "rom/bigint.h" #include "soc/hwcrypto_reg.h" #include "esp_system.h" @@ -38,9 +37,7 @@ #include "freertos/task.h" #include "freertos/semphr.h" -#if defined(MBEDTLS_MPI_MUL_MPI_ALT) || defined(MBEDTLS_MPI_EXP_MOD_ALT) - -static const char *TAG = "bignum"; +static const __attribute__((unused)) char *TAG = "bignum"; #if defined(CONFIG_MBEDTLS_MPI_USE_INTERRUPT) static SemaphoreHandle_t op_complete_sem; @@ -70,10 +67,7 @@ static void rsa_isr_initialise() static _lock_t mpi_lock; -/* At the moment these hardware locking functions aren't exposed publically - for MPI. If you want to use the ROM bigint functions and co-exist with mbedTLS, please raise a feature request. -*/ -static void esp_mpi_acquire_hardware( void ) +void esp_mpi_acquire_hardware( void ) { /* newlib locks lazy initialize on ESP-IDF */ _lock_acquire(&mpi_lock); @@ -83,7 +77,7 @@ static void esp_mpi_acquire_hardware( void ) #endif } -static void esp_mpi_release_hardware( void ) +void esp_mpi_release_hardware( void ) { ets_bigint_disable(); _lock_release(&mpi_lock); @@ -546,5 +540,3 @@ static int mpi_mult_mpi_failover_mod_mult(mbedtls_mpi *Z, const mbedtls_mpi *X, #endif /* MBEDTLS_MPI_MUL_MPI_ALT */ -#endif /* MBEDTLS_MPI_MUL_MPI_ALT || MBEDTLS_MPI_EXP_MOD_ALT */ - diff --git a/components/mbedtls/port/include/aes_alt.h b/components/mbedtls/port/include/aes_alt.h index 7161b282c2..d4da6ca878 100644 --- a/components/mbedtls/port/include/aes_alt.h +++ b/components/mbedtls/port/include/aes_alt.h @@ -20,7 +20,6 @@ * * */ - #ifndef AES_ALT_H #define AES_ALT_H @@ -56,4 +55,4 @@ typedef esp_aes_context mbedtls_aes_context; } #endif -#endif /* aes.h */ +#endif diff --git a/components/mbedtls/port/include/mbedtls/bignum.h b/components/mbedtls/port/include/mbedtls/bignum.h new file mode 100644 index 0000000000..23cd56348a --- /dev/null +++ b/components/mbedtls/port/include/mbedtls/bignum.h @@ -0,0 +1,78 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __ESP_MBEDTLS_BIGNUM_H__ +#define __ESP_MBEDTLS_BIGNUM_H__ + +#include_next "mbedtls/bignum.h" + +/** + * This is a wrapper for the main mbedtls/bignum.h. This wrapper + * provides a few additional ESP32-only functions. + * + * This is because we don't set MBEDTLS_BIGNUM_ALT in the same way we + * do for AES, SHA, etc. Because we still use most of the bignum.h + * implementation and just replace a few hardware accelerated + * functions (see MBEDTLS_MPI_EXP_MOD_ALT & MBEDTLS_MPI_MUL_MPI_ALT in + * esp_config.h). + * + * @note Unlike the other hardware accelerator support functions in esp32/hwcrypto, there is no + * generic "hwcrypto/bignum.h" header for using these functions without mbedTLS. The reason for this + * is that all of the function implementations depend strongly upon the mbedTLS MPI implementation. + */ + +/** + * @brief Lock access to RSA Accelerator (MPI/bignum operations) + * + * RSA Accelerator hardware unit can only be used by one + * consumer at a time. + * + * @note This function is non-recursive (do not call it twice from the + * same task.) + * + * @note You do not need to call this if you are using the mbedTLS bignum.h + * API or esp_mpi_xxx functions. This function is only needed if you + * want to call ROM RSA functions or access the registers directly. + * + */ +void esp_mpi_acquire_hardware(void); + +/** + * @brief Unlock access to RSA Accelerator (MPI/bignum operations) + * + * Has to be called once for each call to esp_mpi_acquire_hardware(). + * + * @note You do not need to call this if you are using the mbedTLS bignum.h + * API or esp_mpi_xxx functions. This function is only needed if you + * want to call ROM RSA functions or access the registers directly. + */ +void esp_mpi_release_hardware(void); + +/* @brief MPI modular mupltiplication function + * + * Calculates Z = (X * Y) mod M using MPI hardware acceleration. + * + * This is not part of the standard mbedTLS bignum API. + * + * @note All of X, Y & Z should be less than 4096 bit long or an error is returned. + * + * @param Z Result bignum, should be pre-initialised with mbedtls_mpi_init(). + * @param X First multiplication argument. + * @param Y Second multiplication argument. + * @param M Modulus value for result. + * + * @return 0 on success, mbedTLS MPI error codes on failure. + */ +int esp_mpi_mul_mpi_mod(mbedtls_mpi *Z, const mbedtls_mpi *X, const mbedtls_mpi *Y, const mbedtls_mpi *M); + +#endif diff --git a/components/mbedtls/port/include/sha1_alt.h b/components/mbedtls/port/include/sha1_alt.h index 60297b9fbf..f5e69b3f95 100644 --- a/components/mbedtls/port/include/sha1_alt.h +++ b/components/mbedtls/port/include/sha1_alt.h @@ -1,7 +1,16 @@ -/* - * copyright (c) 2010 - 2012 Espressif System - * - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef _SHA1_ALT_H_ #define _SHA1_ALT_H_ diff --git a/components/mbedtls/port/include/sha256_alt.h b/components/mbedtls/port/include/sha256_alt.h index 6d9986b3a1..143d8c75e1 100644 --- a/components/mbedtls/port/include/sha256_alt.h +++ b/components/mbedtls/port/include/sha256_alt.h @@ -1,8 +1,16 @@ -/* - * copyright (c) 2010 - 2012 Espressif System - * - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef _SHA256_ALT_H_ #define _SHA256_ALT_H_ @@ -30,4 +38,4 @@ typedef esp_sha_context mbedtls_sha256_context; } #endif -#endif /* sha256.h */ +#endif diff --git a/components/mbedtls/port/include/sha512_alt.h b/components/mbedtls/port/include/sha512_alt.h index 241f2be3b3..8044b42754 100644 --- a/components/mbedtls/port/include/sha512_alt.h +++ b/components/mbedtls/port/include/sha512_alt.h @@ -1,9 +1,16 @@ -/* - * copyright (c) 2010 - 2012 Espressif System - * - * esf Link List Descriptor - */ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef _SHA512_ALT_H_ #define _SHA512_ALT_H_ @@ -30,4 +37,4 @@ typedef esp_sha_context mbedtls_sha512_context; } #endif -#endif /* sha512.h */ +#endif From 51021b06f8c7e49e937f8c295df95807bf505eb3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:23:29 +0800 Subject: [PATCH 252/285] nvs: initialize using layout from partition table --- components/nvs_flash/include/nvs_flash.h | 30 ++++++++++-------------- components/nvs_flash/src/nvs_api.cpp | 12 +++++++++- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index d6e1990250..eca7f99edd 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -18,25 +18,21 @@ extern "C" { #endif -/** Initialise NVS flash storage with default flash sector layout - - Temporarily, this region is hardcoded as a 12KB (0x3000 byte) - region starting at 36KB (0x9000 byte) offset in flash. - - @return ESP_OK if flash was successfully initialised. -*/ +/** + * @brief Initialize NVS flash storage with layout given in the partition table. + * + * @return ESP_OK if storage was successfully initialized. + */ esp_err_t nvs_flash_init(void); -/** Initialise NVS flash storage with custom flash sector layout - - @param baseSector Flash sector (units of 4096 bytes) offset to start NVS. - @param sectorCount Length (in flash sectors) of NVS region. - - @return ESP_OK if flash was successfully initialised. - - @note Use this parameter if you're not using the options in menuconfig for - configuring flash layout & partition table. -*/ +/** + * @brief Initialize NVS flash storage with custom flash sector layout + * @note Make sure specified sectors don't fall within ranges occupied + * by other partitions. + * @param baseSector Flash sector (units of 4096 bytes) offset to start NVS + * @param sectorCount Length (in flash sectors) of NVS region + * @return ESP_OK if flash was successfully initialized + */ esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount); diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index f6c6c588aa..b2cb5e7ad4 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -16,6 +16,7 @@ #include "nvs_storage.hpp" #include "intrusive_list.h" #include "nvs_platform.hpp" +#include "esp_partition.h" #include "sdkconfig.h" #ifdef ESP_PLATFORM @@ -61,10 +62,19 @@ extern "C" void nvs_dump() s_nvs_storage.debugDump(); } +#ifdef ESP_PLATFORM extern "C" esp_err_t nvs_flash_init(void) { - return nvs_flash_init_custom(9, 3); + const esp_partition_t* partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); + if (partition == NULL) { + return ESP_ERR_NOT_FOUND; + } + + return nvs_flash_init_custom(partition->address / SPI_FLASH_SEC_SIZE, + partition->size / SPI_FLASH_SEC_SIZE); } +#endif extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) { From 6e97936bace204d802cee8d34f6cbfed3a5dee0c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:24:56 +0800 Subject: [PATCH 253/285] nvs: allow nvs_flash_init to be called more than once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also don’t assert in nvs_* functions if nvs_flash_init wasn’t called, and make nvs_flash_init_custom an internal API for unit tests. --- components/nvs_flash/include/nvs_flash.h | 10 ----- components/nvs_flash/src/nvs_api.cpp | 9 +++-- components/nvs_flash/src/nvs_platform.hpp | 21 +++++----- components/nvs_flash/src/nvs_storage.cpp | 5 +++ components/nvs_flash/src/nvs_storage.hpp | 2 + components/nvs_flash/src/nvs_test_api.h | 47 +++++++++++++++++++++++ components/nvs_flash/test/test_nvs.cpp | 2 +- 7 files changed, 73 insertions(+), 23 deletions(-) create mode 100644 components/nvs_flash/src/nvs_test_api.h diff --git a/components/nvs_flash/include/nvs_flash.h b/components/nvs_flash/include/nvs_flash.h index eca7f99edd..0162a8f8ac 100644 --- a/components/nvs_flash/include/nvs_flash.h +++ b/components/nvs_flash/include/nvs_flash.h @@ -25,16 +25,6 @@ extern "C" { */ esp_err_t nvs_flash_init(void); -/** - * @brief Initialize NVS flash storage with custom flash sector layout - * @note Make sure specified sectors don't fall within ranges occupied - * by other partitions. - * @param baseSector Flash sector (units of 4096 bytes) offset to start NVS - * @param sectorCount Length (in flash sectors) of NVS region - * @return ESP_OK if flash was successfully initialized - */ -esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount); - #ifdef __cplusplus } diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index b2cb5e7ad4..751542ee12 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -65,6 +65,11 @@ extern "C" void nvs_dump() #ifdef ESP_PLATFORM extern "C" esp_err_t nvs_flash_init(void) { + Lock::init(); + Lock lock; + if (s_nvs_storage.isValid()) { + return ESP_OK; + } const esp_partition_t* partition = esp_partition_find_first( ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); if (partition == NULL) { @@ -78,9 +83,7 @@ extern "C" esp_err_t nvs_flash_init(void) extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) { - Lock::init(); - Lock lock; - ESP_LOGD(TAG, "init start=%d count=%d", baseSector, sectorCount); + ESP_LOGD(TAG, "nvs_flash_init_custom start=%d count=%d", baseSector, sectorCount); s_nvs_handles.clear(); return s_nvs_storage.init(baseSector, sectorCount); } diff --git a/components/nvs_flash/src/nvs_platform.hpp b/components/nvs_flash/src/nvs_platform.hpp index 374dbca6cc..0973c4875c 100644 --- a/components/nvs_flash/src/nvs_platform.hpp +++ b/components/nvs_flash/src/nvs_platform.hpp @@ -16,9 +16,6 @@ #ifdef ESP_PLATFORM -#define NVS_DEBUGV(...) ets_printf(__VA_ARGS__) - -#include "rom/ets_sys.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -30,19 +27,23 @@ class Lock public: Lock() { - assert(mSemaphore); - xSemaphoreTake(mSemaphore, portMAX_DELAY); + if (mSemaphore) { + xSemaphoreTake(mSemaphore, portMAX_DELAY); + } } ~Lock() { - assert(mSemaphore); - xSemaphoreGive(mSemaphore); + if (mSemaphore) { + xSemaphoreGive(mSemaphore); + } } static esp_err_t init() { - assert(mSemaphore == nullptr); + if (mSemaphore) { + return ESP_OK; + } mSemaphore = xSemaphoreCreateMutex(); if (!mSemaphore) { return ESP_ERR_NO_MEM; @@ -52,7 +53,9 @@ public: static void uninit() { - vSemaphoreDelete(mSemaphore); + if (mSemaphore) { + vSemaphoreDelete(mSemaphore); + } mSemaphore = nullptr; } diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index cacfbd4022..f8da28fa24 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -69,6 +69,11 @@ esp_err_t Storage::init(uint32_t baseSector, uint32_t sectorCount) return ESP_OK; } +bool Storage::isValid() const +{ + return mState == StorageState::ACTIVE; +} + esp_err_t Storage::findItem(uint8_t nsIndex, ItemType datatype, const char* key, Page* &page, Item& item) { for (auto it = std::begin(mPageManager); it != std::end(mPageManager); ++it) { diff --git a/components/nvs_flash/src/nvs_storage.hpp b/components/nvs_flash/src/nvs_storage.hpp index f8cee9f2a9..ecf2651ae5 100644 --- a/components/nvs_flash/src/nvs_storage.hpp +++ b/components/nvs_flash/src/nvs_storage.hpp @@ -47,6 +47,8 @@ public: esp_err_t init(uint32_t baseSector, uint32_t sectorCount); + bool isValid() const; + esp_err_t createOrOpenNamespace(const char* nsName, bool canCreate, uint8_t& nsIndex); esp_err_t writeItem(uint8_t nsIndex, ItemType datatype, const char* key, const void* data, size_t dataSize); diff --git a/components/nvs_flash/src/nvs_test_api.h b/components/nvs_flash/src/nvs_test_api.h new file mode 100644 index 0000000000..97940092d5 --- /dev/null +++ b/components/nvs_flash/src/nvs_test_api.h @@ -0,0 +1,47 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "nvs_flash.h" + +/** + * @brief Initialize NVS flash storage with custom flash sector layout + * + * @note This API is intended to be used in unit tests. + * + * @param baseSector Flash sector (units of 4096 bytes) offset to start NVS + * @param sectorCount Length (in flash sectors) of NVS region. + NVS partition must be at least 3 sectors long. + * @return ESP_OK if flash was successfully initialized + */ +esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount); + + +/** + * @brief Dump contents of NVS storage to stdout + * + * This function may be used for debugging purposes to inspect the state + * of NVS pages. For each page, list of entries is also dumped. + */ +void nvs_dump(void); + + +#ifdef __cplusplus +} +#endif diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test/test_nvs.cpp index 81bf7fd216..282d4de48e 100644 --- a/components/nvs_flash/test/test_nvs.cpp +++ b/components/nvs_flash/test/test_nvs.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include "catch.hpp" #include "nvs.hpp" -#include "nvs_flash.h" +#include "nvs_test_api.h" #include "spi_flash_emulation.h" #include #include From a0feea8daa21d301a19283304ef635008eeff75c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:35:10 +0800 Subject: [PATCH 254/285] partition_table: update layouts, remove unused subtypes, sync with header file --- components/partition_table/gen_esp32part.py | 10 +++++++--- components/partition_table/partitions_singleapp.csv | 4 ++-- components/partition_table/partitions_two_ota.csv | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index cb6a5f24a1..5ead13adc7 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -116,7 +116,8 @@ class PartitionDefinition(object): "app" : APP_TYPE, "data" : DATA_TYPE, } - + + # Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h SUBTYPES = { APP_TYPE : { "factory" : 0x00, @@ -124,8 +125,11 @@ class PartitionDefinition(object): }, DATA_TYPE : { "ota" : 0x00, - "rf" : 0x01, - "wifi" : 0x02, + "phy" : 0x01, + "nvs" : 0x02, + "esphttpd" : 0x80, + "fat" : 0x81, + "spiffs" : 0x82, }, } diff --git a/components/partition_table/partitions_singleapp.csv b/components/partition_table/partitions_singleapp.csv index 940b8a76c4..8517b1196f 100644 --- a/components/partition_table/partitions_singleapp.csv +++ b/components/partition_table/partitions_singleapp.csv @@ -1,4 +1,4 @@ # Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x6000 +phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M -rfdata, data, rf, , 256K -wifidata, data, wifi, , 256K diff --git a/components/partition_table/partitions_two_ota.csv b/components/partition_table/partitions_two_ota.csv index 8e064e14b2..58c1127d8c 100644 --- a/components/partition_table/partitions_two_ota.csv +++ b/components/partition_table/partitions_two_ota.csv @@ -1,7 +1,7 @@ # Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x4000 +otadata, data, ota, 0xd000, 0x2000 +phy_init, data, phy, 0xf000, 0x1000 factory, 0, 0, 0x10000, 1M ota_0, 0, ota_0, , 1M ota_1, 0, ota_1, , 1M -rfdata, data, rf, , 256K -wifidata, data, wifi, , 256K -otadata, data, ota, , 256K From 4db29f74a0cf2ac950628bd4cf56d08451d333a6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Nov 2016 18:36:18 +0800 Subject: [PATCH 255/285] add PHY init support --- components/esp32/Kconfig | 54 +++++ components/esp32/Makefile.projbuild | 42 ++++ components/esp32/cpu_start.c | 15 ++ components/esp32/include/esp_phy_init.h | 170 +++++++++++++++ components/esp32/lib | 2 +- components/esp32/phy.h | 51 +++++ components/esp32/phy_init.c | 264 ++++++++++++++++++++++++ components/esp32/phy_init_data.h | 139 +++++++++++++ 8 files changed, 736 insertions(+), 1 deletion(-) create mode 100644 components/esp32/Makefile.projbuild create mode 100644 components/esp32/include/esp_phy_init.h create mode 100644 components/esp32/phy.h create mode 100644 components/esp32/phy_init.c create mode 100644 components/esp32/phy_init_data.h diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 1f04cf4bb7..6bf6499112 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -364,4 +364,58 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL depends on DOCUMENTATION_FOR_RTC_CNTL endchoice +config ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + bool "Store PHY calibration data in NVS" + default y + help + Choose whether to use non-volatile storage library (NVS) + to store PHY calibration data obtained at run time. + If enabled, this will use approximately 2kB of NVS storage + for PHY calibration data. + If this option is not enabled, calibration data will not be stored, + unless application provides its own implementations of + esp_phy_store_cal_data and esp_phy_load_cal_data functions. + See esp_phy_init.h for details. + + If unsure, choose 'y'. + + +config ESP32_PHY_AUTO_INIT + bool "Initialize PHY in startup code" + default y + help + If enabled, PHY will be initialized in startup code, before + app_main function runs. + If this is undesired, disable this option and call esp_phy_init + from the application before enabling WiFi or BT. + + If this option is enabled along with ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS, + startup code will also initialize NVS prior to initializing PHY. + + If unsure, choose 'y'. + +config ESP32_PHY_INIT_DATA_IN_PARTITION + bool "Use a partition to store PHY init data" + default n + help + If enabled, PHY init data will be loaded from a partition. + When using a custom partition table, make sure that PHY data + partition is included (type: 'data', subtype: 'phy'). + With default partition tables, this is done automatically. + If PHY init data is stored in a partition, it has to be flashed there, + otherwise runtime error will occur. + + If this option is not enabled, PHY init data will be embedded + into the application binary. + + If unsure, choose 'n'. + +config ESP32_PHY_MAX_TX_POWER + int "Max TX power (dBm)" + range 0 20 + default 20 + help + Set maximum transmit power. Actual transmit power for high + data rates may be lower than this setting. + endmenu diff --git a/components/esp32/Makefile.projbuild b/components/esp32/Makefile.projbuild new file mode 100644 index 0000000000..1b54f1844d --- /dev/null +++ b/components/esp32/Makefile.projbuild @@ -0,0 +1,42 @@ +ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + +PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o +PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin + +PARTITION_TABLE_COMPONENT_PATH := $(COMPONENT_PATH)/../partition_table +ESP32_COMPONENT_PATH := $(COMPONENT_PATH) + +GEN_ESP32PART := $(PYTHON) $(PARTITION_TABLE_COMPONENT_PATH)/gen_esp32part.py -q + +# Path to partition CSV file is relative to project path for custom +# partition CSV files, but relative to component dir otherwise. +PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(PARTITION_TABLE_COMPONENT_PATH))) +PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(subst $(quote),,$(CONFIG_PARTITION_TABLE_FILENAME)))) +PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) + +# Parse partition table and get offset of PHY init data partition +PHY_INIT_GET_ADDR_CMD := $(GEN_ESP32PART) $(PARTITION_TABLE_CSV_PATH) | $(GEN_ESP32PART) - | sed -n -e "s/[^,]*,data,phy,\\([^,]*\\),.*/\\1/p" +PHY_INIT_DATA_ADDR = $(shell $(PHY_INIT_GET_ADDR_CMD)) + +# Command to flash PHY init data partition +PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) + +$(PHY_INIT_DATA_OBJ): $(ESP32_COMPONENT_PATH)/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h + $(summary) CC $(notdir $@) + printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP32_COMPONENT_PATH) -I $(ESP32_COMPONENT_PATH)/include -c -o $@ -xc - + +$(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ) + $(summary) BIN $(notdir $@) + $(OBJCOPY) -O binary $< $@ + +phy_init_data: $(PHY_INIT_DATA_BIN) + +phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin + @echo "Flashing PHY init data..." + $(PHY_INIT_DATA_FLASH_CMD) + +phy_init_data-clean: + rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ) + +endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index a96fdee950..df839069f9 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -50,6 +50,7 @@ #include "esp_brownout.h" #include "esp_int_wdt.h" #include "esp_task_wdt.h" +#include "esp_phy_init.h" #include "trax.h" void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))); @@ -187,6 +188,20 @@ void start_cpu0_default(void) esp_ipc_init(); spi_flash_init(); +#if CONFIG_ESP32_PHY_AUTO_INIT +#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + nvs_flash_init(); +#endif + esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; + if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { + calibration_mode = PHY_RF_CAL_NONE; + } + if (esp_phy_init(calibration_mode) != ESP_OK) { + ESP_LOGD(TAG, "phy init has failed"); + abort(); + } +#endif + xTaskCreatePinnedToCore(&main_task, "main", ESP_TASK_MAIN_STACK, NULL, ESP_TASK_MAIN_PRIO, NULL, 0); diff --git a/components/esp32/include/esp_phy_init.h b/components/esp32/include/esp_phy_init.h new file mode 100644 index 0000000000..f911276dff --- /dev/null +++ b/components/esp32/include/esp_phy_init.h @@ -0,0 +1,170 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t param_ver_id; /*!< init_data structure version */ + uint8_t crystal_select; /*!< 0: 40MHz, 1: 26 MHz, 2: 24 MHz, 3: auto */ + uint8_t wifi_rx_gain_swp_step_1; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_2; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_3; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_4; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_5; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_6; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_7; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_8; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_9; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_10; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_11; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_12; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_13; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_14; /*!< do not change */ + uint8_t wifi_rx_gain_swp_step_15; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_1; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_2; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_3; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_4; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_5; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_6; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_7; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_8; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_9; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_10; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_11; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_12; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_13; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_14; /*!< do not change */ + uint8_t bt_rx_gain_swp_step_15; /*!< do not change */ + uint8_t gain_cmp_1; /*!< do not change */ + uint8_t gain_cmp_6; /*!< do not change */ + uint8_t gain_cmp_11; /*!< do not change */ + uint8_t gain_cmp_ext2_1; /*!< do not change */ + uint8_t gain_cmp_ext2_6; /*!< do not change */ + uint8_t gain_cmp_ext2_11; /*!< do not change */ + uint8_t gain_cmp_ext3_1; /*!< do not change */ + uint8_t gain_cmp_ext3_6; /*!< do not change */ + uint8_t gain_cmp_ext3_11; /*!< do not change */ + uint8_t gain_cmp_bt_ofs_1; /*!< do not change */ + uint8_t gain_cmp_bt_ofs_6; /*!< do not change */ + uint8_t gain_cmp_bt_ofs_11; /*!< do not change */ + uint8_t target_power_qdb_0; /*!< 78 means target power is 78/4=19.5dbm */ + uint8_t target_power_qdb_1; /*!< 76 means target power is 76/4=19dbm */ + uint8_t target_power_qdb_2; /*!< 74 means target power is 74/4=18.5dbm */ + uint8_t target_power_qdb_3; /*!< 68 means target power is 68/4=17dbm */ + uint8_t target_power_qdb_4; /*!< 64 means target power is 64/4=16dbm */ + uint8_t target_power_qdb_5; /*!< 52 means target power is 52/4=13dbm */ + uint8_t target_power_index_mcs0; /*!< target power index is 0, means target power is target_power_qdb_0 19.5dbm; (1m,2m,5.5m,11m,6m,9m) */ + uint8_t target_power_index_mcs1; /*!< target power index is 0, means target power is target_power_qdb_0 19.5dbm; (12m) */ + uint8_t target_power_index_mcs2; /*!< target power index is 1, means target power is target_power_qdb_1 19dbm; (18m) */ + uint8_t target_power_index_mcs3; /*!< target power index is 1, means target power is target_power_qdb_1 19dbm; (24m) */ + uint8_t target_power_index_mcs4; /*!< target power index is 2, means target power is target_power_qdb_2 18.5dbm; (36m) */ + uint8_t target_power_index_mcs5; /*!< target power index is 3, means target power is target_power_qdb_3 17dbm; (48m) */ + uint8_t target_power_index_mcs6; /*!< target power index is 4, means target power is target_power_qdb_4 16dbm; (54m) */ + uint8_t target_power_index_mcs7; /*!< target power index is 5, means target power is target_power_qdb_5 13dbm */ + uint8_t pwr_ind_11b_en; /*!< 0: 11b power is same as mcs0 and 6m, 1: 11b power different with OFDM */ + uint8_t pwr_ind_11b_0; /*!< 1m, 2m power index [0~5] */ + uint8_t pwr_ind_11b_1; /*!< 5.5m, 11m power index [0~5] */ + uint8_t chan_backoff_en; /*!< 0: channel backoff disable, 1:channel backoff enable */ + uint8_t chan1_power_backoff_qdb; /*!< 4 means backoff is 1db */ + uint8_t chan2_power_backoff_qdb; /*!< see chan1_power_backoff_qdb */ + uint8_t chan3_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan4_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan5_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan6_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan7_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan8_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan9_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan10_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan11_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan12_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan13_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan14_power_backoff_qdb; /*!< chan1_power_backoff_qdb */ + uint8_t chan1_rate_backoff_index; /*!< if bit i is set, backoff data rate is target_power_qdb_i */ + uint8_t chan2_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan3_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan4_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan5_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan6_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan7_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan8_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan9_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan10_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan11_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan12_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan13_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t chan14_rate_backoff_index; /*!< see chan1_rate_backoff_index */ + uint8_t spur_freq_cfg_msb_1; /*!< first spur: */ + uint8_t spur_freq_cfg_1; /*!< spur_freq_cfg = (spur_freq_cfg_msb_1 <<8) | spur_freq_cfg_1 */ + uint8_t spur_freq_cfg_div_1; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_1 */ + uint8_t spur_freq_en_h_1; /*!< the seventh bit for total enable */ + uint8_t spur_freq_en_l_1; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority */ + uint8_t spur_freq_cfg_msb_2; /*!< second spur: */ + uint8_t spur_freq_cfg_2; /*!< spur_freq_cfg = (spur_freq_cfg_msb_2 <<8) | spur_freq_cfg_2 */ + uint8_t spur_freq_cfg_div_2; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_2 */ + uint8_t spur_freq_en_h_2; /*!< the seventh bit for total enable */ + uint8_t spur_freq_en_l_2; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority */ + uint8_t spur_freq_cfg_msb_3; /*!< third spur: */ + uint8_t spur_freq_cfg_3; /*!< spur_freq_cfg = (spur_freq_cfg_msb_3 <<8) | spur_freq_cfg_3 */ + uint8_t spur_freq_cfg_div_3; /*!< spur_freq=spur_freq_cfg/spur_freq_cfg_div_3 */ + uint8_t spur_freq_en_h_3; /*!< the seventh bit for total enable */ + uint8_t spur_freq_en_l_3; /*!< each bit for 1 channel, and use [spur_freq_en_h, spur_freq_en_l] to select the spur's channel priority, */ + uint8_t reserved[23]; /*!< reserved for future expansion */ +} esp_phy_init_data_t; + +typedef struct { + uint8_t opaque[1904]; /*!< opaque calibration data */ +} esp_phy_calibration_data_t; + +typedef enum { + PHY_RF_CAL_PARTIAL = 0x00000000, /*!< Do part of RF calibration. This should be used after power-on reset. */ + PHY_RF_CAL_NONE = 0x00000001, /*!< Don't do any RF calibration. This mode is only suggested to be used after deep sleep reset. */ + PHY_RF_CAL_FULL = 0x00000002 /*!< Do full RF calibration. Produces best results, but also consumes a lot of time and current. Suggested to be used once. */ +} esp_phy_calibration_mode_t; + +/** + * + * @param mode + * @return + */ +esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode); + +#ifndef CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + +/** + * + * @param cal_data + * @return + */ +esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data); + +/** + * + * @param out_cal_data + * @return + */ +esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data); + +#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + +#ifdef __cplusplus +} +#endif + diff --git a/components/esp32/lib b/components/esp32/lib index e188536a63..db867fe912 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit e188536a6315cc3ce4f1006ac3a4450faea6abc6 +Subproject commit db867fe9128cc1fc273d76af5a412f6743519149 diff --git a/components/esp32/phy.h b/components/esp32/phy.h new file mode 100644 index 0000000000..ad6b9003ec --- /dev/null +++ b/components/esp32/phy.h @@ -0,0 +1,51 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include "esp_phy_init.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file phy.h + * @brief Declarations for functions provided by libphy.a + */ + +/** + * @brief Initialize function pointer table in PHY library. + * @note This function should be called before register_chipv7_phy. + */ +void phy_get_romfunc_addr(void); + +/** + * @brief Initialize PHY module and do RF calibration + * @param[in] init_data Initialization parameters to be used by the PHY + * @param[inout] cal_data As input, calibration data previously obtained. As output, will contain new calibration data. + * @param[in] cal_mode RF calibration mode + * @return reserved for future use + */ +int register_chipv7_phy(const esp_phy_init_data_t* init_data, esp_phy_calibration_data_t *cal_data, esp_phy_calibration_mode_t cal_mode); + +/** + * @brief Get the format version of calibration data used by PHY library. + * @return Format version number + */ +uint32_t phy_get_rf_cal_version(); + +#ifdef __cplusplus +} +#endif + diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c new file mode 100644 index 0000000000..11d571f04e --- /dev/null +++ b/components/esp32/phy_init.c @@ -0,0 +1,264 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "rom/ets_sys.h" +#include "soc/dport_reg.h" + +#include "esp_err.h" +#include "esp_phy_init.h" +#include "esp_system.h" +#include "phy.h" +#include "esp_log.h" +#include "sdkconfig.h" +#include "phy_init_data.h" + +static const char* TAG = "phy_init"; + +static const esp_phy_init_data_t* phy_get_init_data(); +static void phy_release_init_data(const esp_phy_init_data_t*); + +esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode) +{ + ESP_LOGD(TAG, "esp_phy_init, mode=%d", mode); + esp_err_t err; + const esp_phy_init_data_t* init_data = phy_get_init_data(); + if (init_data == NULL) { + ESP_LOGE(TAG, "failed to obtain PHY init data"); + return ESP_FAIL; + } + esp_phy_calibration_data_t* cal_data = + (esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1); + if (cal_data == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for RF calibration data"); + return ESP_ERR_NO_MEM; + } + // Initialize PHY function pointer table + phy_get_romfunc_addr(); + // Enable WiFi peripheral clock + SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf); + // If full calibration is requested, don't need to load previous calibration data + if (mode != PHY_RF_CAL_FULL) { + err = esp_phy_load_cal_data(cal_data); + if (err != ESP_OK) { + ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration"); + mode = PHY_RF_CAL_FULL; + } + } + ESP_LOGV(TAG, "calling register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", init_data, cal_data, mode); + register_chipv7_phy(init_data, cal_data, mode); + if (mode != PHY_RF_CAL_NONE) { + err = esp_phy_store_cal_data(cal_data); + } else { + err = ESP_OK; + } + phy_release_init_data(init_data); + free(cal_data); // PHY maintains a copy of calibration data, so we can free this + return err; +} + +// PHY init data handling functions + +#if CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION +#define NO_DEFAULT_INIT_DATA +#include "esp_partition.h" + +static const esp_phy_init_data_t* phy_get_init_data() +{ + const esp_partition_t* partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_PHY, NULL); + if (partition == NULL) { + ESP_LOGE(TAG, "PHY data partition not found"); + return NULL; + } + ESP_LOGD(TAG, "loading PHY init data from partition at offset 0x%x", partition->address); + size_t init_data_store_length = sizeof(phy_init_magic_pre) + + sizeof(esp_phy_init_data_t) + sizeof(phy_init_magic_post); + uint8_t* init_data_store = (uint8_t*) malloc(init_data_store_length); + if (init_data_store == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for PHY init data"); + return NULL; + } + esp_err_t err = esp_partition_read(partition, 0, init_data_store, init_data_store_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to read PHY data partition (%d)", err); + return NULL; + } + if (memcmp(init_data_store, PHY_INIT_MAGIC, sizeof(phy_init_magic_pre)) != 0 || + memcmp(init_data_store + init_data_store_length - sizeof(phy_init_magic_post), + PHY_INIT_MAGIC, sizeof(phy_init_magic_post)) != 0) { + ESP_LOGE(TAG, "failed to validate PHY data partition"); + return NULL; + } + ESP_LOGE(TAG, "PHY data partition validated"); + return (const esp_phy_init_data_t*) (init_data_store + sizeof(phy_init_magic_pre)); +} + +static void phy_release_init_data(const esp_phy_init_data_t* init_data) +{ + free((uint8_t*) init_data - sizeof(phy_init_magic_pre)); +} + +#else // CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + +// phy_init_data.h will declare static 'phy_init_data' variable initialized with default init data + +static const esp_phy_init_data_t* phy_get_init_data() +{ + ESP_LOGD(TAG, "loading PHY init data from application binary"); + return &phy_init_data; +} + +static void phy_release_init_data(const esp_phy_init_data_t* init_data) +{ + // no-op +} +#endif // CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION + + +// PHY calibration data handling functions + +#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS +#include "nvs.h" + +static const char* PHY_NAMESPACE = "phy"; +static const char* PHY_CAL_VERSION_KEY = "cal_version"; +static const char* PHY_CAL_MAC_KEY = "cal_mac"; +static const char* PHY_CAL_DATA_KEY = "cal_data"; + +static esp_err_t load_cal_data_from_nvs(nvs_handle handle, + esp_phy_calibration_data_t* out_cal_data); + +static esp_err_t store_cal_data_to_nvs(nvs_handle handle, + const esp_phy_calibration_data_t* cal_data); + +esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data) +{ + nvs_handle handle; + esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err); + return err; + } + else { + err = load_cal_data_from_nvs(handle, out_cal_data); + nvs_close(handle); + return err; + } +} + +esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) +{ + nvs_handle handle; + esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READWRITE, &handle); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to open NVS namespace (%d)", __func__, err); + return err; + } + else { + err = store_cal_data_to_nvs(handle, cal_data); + nvs_close(handle); + return err; + } +} + +static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_data_t* out_cal_data) +{ + esp_err_t err; + uint32_t cal_data_version; + err = nvs_get_u32(handle, PHY_CAL_VERSION_KEY, &cal_data_version); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to get cal_version (%d)", __func__, err); + return err; + } + uint32_t cal_format_version = phy_get_rf_cal_version(); + if (cal_data_version != cal_format_version) { + ESP_LOGD(TAG, "%s: expected calibration data format %d, found %d", + __func__, cal_format_version, cal_data_version); + return ESP_FAIL; + } + uint8_t cal_data_mac[6]; + size_t length = sizeof(cal_data_mac); + err = nvs_get_blob(handle, PHY_CAL_MAC_KEY, cal_data_mac, &length); + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to get cal_mac (%d)", __func__, err); + return err; + } + if (length != sizeof(cal_data_mac)) { + ESP_LOGD(TAG, "%s: invalid length of cal_mac (%d)", __func__, length); + return ESP_ERR_INVALID_SIZE; + } + uint8_t sta_mac[6]; + system_efuse_read_mac(sta_mac); + if (memcmp(sta_mac, cal_data_mac, sizeof(sta_mac)) != 0) { + ESP_LOGE(TAG, "%s: calibration data MAC check failed: expected " \ + MACSTR ", found " MACSTR, + __func__, MAC2STR(sta_mac), MAC2STR(cal_data_mac)); + return ESP_FAIL; + } + length = sizeof(*out_cal_data); + err = nvs_get_blob(handle, PHY_CAL_DATA_KEY, out_cal_data, &length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: failed to get cal_data(%d)", __func__, err); + return err; + } + if (length != sizeof(*out_cal_data)) { + ESP_LOGD(TAG, "%s: invalid length of cal_data (%d)", __func__, length); + return ESP_ERR_INVALID_SIZE; + } + return ESP_OK; +} + +static esp_err_t store_cal_data_to_nvs(nvs_handle handle, + const esp_phy_calibration_data_t* cal_data) +{ + esp_err_t err; + uint32_t cal_format_version = phy_get_rf_cal_version(); + err = nvs_set_u32(handle, PHY_CAL_VERSION_KEY, cal_format_version); + if (err != ESP_OK) { + return err; + } + uint8_t sta_mac[6]; + system_efuse_read_mac(sta_mac); + err = nvs_set_blob(handle, PHY_CAL_MAC_KEY, sta_mac, sizeof(sta_mac)); + if (err != ESP_OK) { + return err; + } + err = nvs_set_blob(handle, PHY_CAL_DATA_KEY, cal_data, sizeof(*cal_data)); + return err; +} + +#else // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + +// Default implementation: don't store or load calibration data. +// These functions are defined as weak and can be overridden in the application. + +esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) +{ + // pretend that calibration data is stored + return ESP_OK; +} + +esp_err_t esp_phy_load_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) +{ + // nowhere to load data from + return ESP_ERR_NOT_SUPPORTED; +} + +#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS + diff --git a/components/esp32/phy_init_data.h b/components/esp32/phy_init_data.h new file mode 100644 index 0000000000..206598f97c --- /dev/null +++ b/components/esp32/phy_init_data.h @@ -0,0 +1,139 @@ +// Copyright 2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include "esp_phy_init.h" +#include "sdkconfig.h" + +// constrain a value between 'low' and 'high', inclusive +#define LIMIT(val, low, high) ((val < low) ? low : (val > high) ? high : val) + +#define PHY_INIT_MAGIC "PHYINIT" + +static const char phy_init_magic_pre[] = PHY_INIT_MAGIC; + +/** + * @brief Structure containing default recommended PHY initialization parameters. + */ +static const esp_phy_init_data_t phy_init_data= { + .param_ver_id = 0, + .crystal_select = 3, + .wifi_rx_gain_swp_step_1 = 0x05, + .wifi_rx_gain_swp_step_2 = 0x04, + .wifi_rx_gain_swp_step_3 = 0x06, + .wifi_rx_gain_swp_step_4 = 0x05, + .wifi_rx_gain_swp_step_5 = 0x01, + .wifi_rx_gain_swp_step_6 = 0x06, + .wifi_rx_gain_swp_step_7 = 0x05, + .wifi_rx_gain_swp_step_8 = 0x04, + .wifi_rx_gain_swp_step_9 = 0x06, + .wifi_rx_gain_swp_step_10 = 0x04, + .wifi_rx_gain_swp_step_11 = 0x05, + .wifi_rx_gain_swp_step_12 = 0x00, + .wifi_rx_gain_swp_step_13 = 0x00, + .wifi_rx_gain_swp_step_14 = 0x00, + .wifi_rx_gain_swp_step_15 = 0x00, + .bt_rx_gain_swp_step_1 = 0x05, + .bt_rx_gain_swp_step_2 = 0x04, + .bt_rx_gain_swp_step_3 = 0x06, + .bt_rx_gain_swp_step_4 = 0x05, + .bt_rx_gain_swp_step_5 = 0x01, + .bt_rx_gain_swp_step_6 = 0x06, + .bt_rx_gain_swp_step_7 = 0x05, + .bt_rx_gain_swp_step_8 = 0x00, + .bt_rx_gain_swp_step_9 = 0x00, + .bt_rx_gain_swp_step_10 = 0x00, + .bt_rx_gain_swp_step_11 = 0x00, + .bt_rx_gain_swp_step_12 = 0x00, + .bt_rx_gain_swp_step_13 = 0x00, + .bt_rx_gain_swp_step_14 = 0x00, + .bt_rx_gain_swp_step_15 = 0x00, + .gain_cmp_1 = 0x0a, + .gain_cmp_6 = 0x0a, + .gain_cmp_11 = 0x0c, + .gain_cmp_ext2_1 = 0xf0, + .gain_cmp_ext2_6 = 0xf0, + .gain_cmp_ext2_11 = 0xf0, + .gain_cmp_ext3_1 = 0xe0, + .gain_cmp_ext3_6 = 0xe0, + .gain_cmp_ext3_11 = 0xe0, + .gain_cmp_bt_ofs_1 = 0x18, + .gain_cmp_bt_ofs_6 = 0x18, + .gain_cmp_bt_ofs_11 = 0x18, + .target_power_qdb_0 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 78), + .target_power_qdb_1 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 76), + .target_power_qdb_2 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 74), + .target_power_qdb_3 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 68), + .target_power_qdb_4 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 64), + .target_power_qdb_5 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 52), + .target_power_index_mcs0 = 0, + .target_power_index_mcs1 = 0, + .target_power_index_mcs2 = 1, + .target_power_index_mcs3 = 1, + .target_power_index_mcs4 = 2, + .target_power_index_mcs5 = 3, + .target_power_index_mcs6 = 4, + .target_power_index_mcs7 = 5, + .pwr_ind_11b_en = 0, + .pwr_ind_11b_0 = 0, + .pwr_ind_11b_1 = 0, + .chan_backoff_en = 0, + .chan1_power_backoff_qdb = 0, + .chan2_power_backoff_qdb = 0, + .chan3_power_backoff_qdb = 0, + .chan4_power_backoff_qdb = 0, + .chan5_power_backoff_qdb = 0, + .chan6_power_backoff_qdb = 0, + .chan7_power_backoff_qdb = 0, + .chan8_power_backoff_qdb = 0, + .chan9_power_backoff_qdb = 0, + .chan10_power_backoff_qdb = 0, + .chan11_power_backoff_qdb = 0, + .chan12_power_backoff_qdb = 0, + .chan13_power_backoff_qdb = 0, + .chan14_power_backoff_qdb = 0, + .chan1_rate_backoff_index = 0, + .chan2_rate_backoff_index = 0, + .chan3_rate_backoff_index = 0, + .chan4_rate_backoff_index = 0, + .chan5_rate_backoff_index = 0, + .chan6_rate_backoff_index = 0, + .chan7_rate_backoff_index = 0, + .chan8_rate_backoff_index = 0, + .chan9_rate_backoff_index = 0, + .chan10_rate_backoff_index = 0, + .chan11_rate_backoff_index = 0, + .chan12_rate_backoff_index = 0, + .chan13_rate_backoff_index = 0, + .chan14_rate_backoff_index = 0, + .spur_freq_cfg_msb_1 = 0, + .spur_freq_cfg_1 = 0, + .spur_freq_cfg_div_1 = 0, + .spur_freq_en_h_1 = 0, + .spur_freq_en_l_1 = 0, + .spur_freq_cfg_msb_2 = 0, + .spur_freq_cfg_2 = 0, + .spur_freq_cfg_div_2 = 0, + .spur_freq_en_h_2 = 0, + .spur_freq_en_l_2 = 0, + .spur_freq_cfg_msb_3 = 0, + .spur_freq_cfg_3 = 0, + .spur_freq_cfg_div_3 = 0, + .spur_freq_en_h_3 = 0, + .spur_freq_en_l_3 = 0, + .reserved = {0} +}; + +static const char phy_init_magic_post[] = PHY_INIT_MAGIC; + From 6d4ab76db2a9f24371864c9f4238ea635b598eed Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 16 Nov 2016 12:22:32 +1100 Subject: [PATCH 256/285] phy init data: Read PHY init data partition offset from menuconfig --- components/esp32/Makefile.projbuild | 30 +++----- components/partition_table/Kconfig.projbuild | 72 +++++++++++-------- .../partition_table/partitions_singleapp.csv | 1 + .../partition_table/partitions_two_ota.csv | 1 + 4 files changed, 55 insertions(+), 49 deletions(-) diff --git a/components/esp32/Makefile.projbuild b/components/esp32/Makefile.projbuild index 1b54f1844d..01f8d03c5a 100644 --- a/components/esp32/Makefile.projbuild +++ b/components/esp32/Makefile.projbuild @@ -3,34 +3,21 @@ ifdef CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION PHY_INIT_DATA_OBJ = $(BUILD_DIR_BASE)/phy_init_data.o PHY_INIT_DATA_BIN = $(BUILD_DIR_BASE)/phy_init_data.bin -PARTITION_TABLE_COMPONENT_PATH := $(COMPONENT_PATH)/../partition_table -ESP32_COMPONENT_PATH := $(COMPONENT_PATH) - -GEN_ESP32PART := $(PYTHON) $(PARTITION_TABLE_COMPONENT_PATH)/gen_esp32part.py -q - -# Path to partition CSV file is relative to project path for custom -# partition CSV files, but relative to component dir otherwise. -PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(PARTITION_TABLE_COMPONENT_PATH))) -PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(subst $(quote),,$(CONFIG_PARTITION_TABLE_FILENAME)))) -PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) - -# Parse partition table and get offset of PHY init data partition -PHY_INIT_GET_ADDR_CMD := $(GEN_ESP32PART) $(PARTITION_TABLE_CSV_PATH) | $(GEN_ESP32PART) - | sed -n -e "s/[^,]*,data,phy,\\([^,]*\\),.*/\\1/p" -PHY_INIT_DATA_ADDR = $(shell $(PHY_INIT_GET_ADDR_CMD)) - # Command to flash PHY init data partition -PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) -ESPTOOL_ALL_FLASH_ARGS += $(PHY_INIT_DATA_ADDR) $(PHY_INIT_DATA_BIN) +PHY_INIT_DATA_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(CONFIG_PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_PHY_DATA_OFFSET) $(PHY_INIT_DATA_BIN) + +ESP32_COMPONENT_PATH := $(COMPONENT_PATH) $(PHY_INIT_DATA_OBJ): $(ESP32_COMPONENT_PATH)/phy_init_data.h $(BUILD_DIR_BASE)/include/sdkconfig.h $(summary) CC $(notdir $@) printf "#include \"phy_init_data.h\"\n" | $(CC) -I $(BUILD_DIR_BASE)/include -I $(ESP32_COMPONENT_PATH) -I $(ESP32_COMPONENT_PATH)/include -c -o $@ -xc - - + $(PHY_INIT_DATA_BIN): $(PHY_INIT_DATA_OBJ) $(summary) BIN $(notdir $@) - $(OBJCOPY) -O binary $< $@ + $(OBJCOPY) -O binary $< $@ -phy_init_data: $(PHY_INIT_DATA_BIN) +phy_init_data: $(PHY_INIT_DATA_BIN) phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin @echo "Flashing PHY init data..." @@ -39,4 +26,7 @@ phy_init_data-flash: $(BUILD_DIR_BASE)/phy_init_data.bin phy_init_data-clean: rm -f $(PHY_INIT_DATA_BIN) $(PHY_INIT_DATA_OBJ) +all: phy_init_data +flash: phy_init_data + endif # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION diff --git a/components/partition_table/Kconfig.projbuild b/components/partition_table/Kconfig.projbuild index fa2685d7a1..1f019a6e3f 100644 --- a/components/partition_table/Kconfig.projbuild +++ b/components/partition_table/Kconfig.projbuild @@ -1,49 +1,63 @@ menu "Partition Table" choice - prompt "Partition Table" - default PARTITION_TABLE_SINGLE_APP - help - The partition table to flash to the ESP32. The partition table - determines where apps, data and other resources are expected to - be found. + prompt "Partition Table" + default PARTITION_TABLE_SINGLE_APP + help + The partition table to flash to the ESP32. The partition table + determines where apps, data and other resources are expected to + be found. - The predefined partition table CSV descriptions can be found - in the components/partition_table directory. Otherwise it's - possible to create a new custom partition CSV for your application. + The predefined partition table CSV descriptions can be found + in the components/partition_table directory. Otherwise it's + possible to create a new custom partition CSV for your application. config PARTITION_TABLE_SINGLE_APP - bool "Single factory app, no OTA" + bool "Single factory app, no OTA" config PARTITION_TABLE_TWO_OTA - bool "Factory app, two OTA definitions" + bool "Factory app, two OTA definitions" config PARTITION_TABLE_CUSTOM - bool "Custom partition table CSV" + bool "Custom partition table CSV" endchoice config PARTITION_TABLE_CUSTOM_FILENAME - string "Custom partition CSV file" if PARTITION_TABLE_CUSTOM - default partitions.csv - help - Name of the custom partition CSV filename. This path is evaluated - relative to the project root directory. + string "Custom partition CSV file" if PARTITION_TABLE_CUSTOM + default partitions.csv + help + Name of the custom partition CSV filename. This path is evaluated + relative to the project root directory. config PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET - hex "App offset in flash" if PARTITION_TABLE_CUSTOM - default 0x10000 - help - If using a custom partition table, specify the offset in the flash - where 'make flash' should write the built app. + hex "Factory app partition offset" if PARTITION_TABLE_CUSTOM + default 0x10000 + help + If using a custom partition table, specify the offset in the flash + where 'make flash' should write the built app. + +config PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET + hex "PHY data partition offset" if PARTITION_TABLE_CUSTOM + depends on ESP32_PHY_INIT_DATA_IN_PARTITION + default 0xf000 + help + If using a custom partition table, specify the offset in the flash + where 'make flash' should write the initial PHY data file. + config PARTITION_TABLE_FILENAME - string - default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP - default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA - default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM + string + default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP + default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA + default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM config APP_OFFSET - hex - default PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET if PARTITION_TABLE_CUSTOM - default 0x10000 # this is the factory app offset used by the default tables + hex + default PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET if PARTITION_TABLE_CUSTOM + default 0x10000 # this is the factory app offset used by the default tables + +config PHY_DATA_OFFSET + hex + default PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET if PARTITION_TABLE_CUSTOM + default 0xf000 # this is the factory app offset used by the default tables endmenu diff --git a/components/partition_table/partitions_singleapp.csv b/components/partition_table/partitions_singleapp.csv index 8517b1196f..e1647008ee 100644 --- a/components/partition_table/partitions_singleapp.csv +++ b/components/partition_table/partitions_singleapp.csv @@ -1,4 +1,5 @@ # Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x6000 phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M diff --git a/components/partition_table/partitions_two_ota.csv b/components/partition_table/partitions_two_ota.csv index 58c1127d8c..afb43967a3 100644 --- a/components/partition_table/partitions_two_ota.csv +++ b/components/partition_table/partitions_two_ota.csv @@ -1,4 +1,5 @@ # Name, Type, SubType, Offset, Size +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, 0x9000, 0x4000 otadata, data, ota, 0xd000, 0x2000 phy_init, data, phy, 0xf000, 0x1000 From 541b1426546a4ee22089e78177ad13d380d3a3d6 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 18 Nov 2016 01:18:39 +0800 Subject: [PATCH 257/285] phy_init: reduce the amount of hardwired logic, add coexist init --- components/esp32/Kconfig | 19 +--- components/esp32/cpu_start.c | 45 +++++++--- components/esp32/include/esp_phy_init.h | 107 ++++++++++++++++++++--- components/esp32/include/soc/dport_reg.h | 1 + components/esp32/lib | 2 +- components/esp32/phy.h | 15 +++- components/esp32/phy_init.c | 104 +++++++--------------- components/nvs_flash/src/nvs_api.cpp | 14 +-- 8 files changed, 185 insertions(+), 122 deletions(-) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 6bf6499112..206ea669e1 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -364,21 +364,6 @@ config ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL depends on DOCUMENTATION_FOR_RTC_CNTL endchoice -config ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS - bool "Store PHY calibration data in NVS" - default y - help - Choose whether to use non-volatile storage library (NVS) - to store PHY calibration data obtained at run time. - If enabled, this will use approximately 2kB of NVS storage - for PHY calibration data. - If this option is not enabled, calibration data will not be stored, - unless application provides its own implementations of - esp_phy_store_cal_data and esp_phy_load_cal_data functions. - See esp_phy_init.h for details. - - If unsure, choose 'y'. - config ESP32_PHY_AUTO_INIT bool "Initialize PHY in startup code" @@ -389,8 +374,8 @@ config ESP32_PHY_AUTO_INIT If this is undesired, disable this option and call esp_phy_init from the application before enabling WiFi or BT. - If this option is enabled along with ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS, - startup code will also initialize NVS prior to initializing PHY. + If this option is enabled, startup code will also initialize + NVS prior to initializing PHY. If unsure, choose 'y'. diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index df839069f9..1eb0a5355e 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -63,6 +63,7 @@ static bool app_cpu_started = false; #endif //!CONFIG_FREERTOS_UNICORE static void do_global_ctors(void); +static void do_phy_init(); static void main_task(void* args); extern void app_main(void); @@ -189,17 +190,8 @@ void start_cpu0_default(void) spi_flash_init(); #if CONFIG_ESP32_PHY_AUTO_INIT -#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS nvs_flash_init(); -#endif - esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; - if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { - calibration_mode = PHY_RF_CAL_NONE; - } - if (esp_phy_init(calibration_mode) != ESP_OK) { - ESP_LOGD(TAG, "phy init has failed"); - abort(); - } + do_phy_init(); #endif xTaskCreatePinnedToCore(&main_task, "main", @@ -239,3 +231,36 @@ static void main_task(void* args) vTaskDelete(NULL); } +static void do_phy_init() +{ + esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; + if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { + calibration_mode = PHY_RF_CAL_NONE; + } + const esp_phy_init_data_t* init_data = esp_phy_get_init_data(); + if (init_data == NULL) { + ESP_LOGE(TAG, "failed to obtain PHY init data"); + abort(); + } + esp_phy_calibration_data_t* cal_data = + (esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1); + if (cal_data == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for RF calibration data"); + abort(); + } + esp_err_t err = esp_phy_load_cal_data_from_nvs(cal_data); + if (err != ESP_OK) { + ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration"); + calibration_mode = PHY_RF_CAL_FULL; + } + + esp_phy_init(init_data, calibration_mode, cal_data); + + if (calibration_mode != PHY_RF_CAL_NONE) { + err = esp_phy_store_cal_data_to_nvs(cal_data); + } else { + err = ESP_OK; + } + esp_phy_release_init_data(init_data); + free(cal_data); // PHY maintains a copy of calibration data, so we can free this +} diff --git a/components/esp32/include/esp_phy_init.h b/components/esp32/include/esp_phy_init.h index f911276dff..7bc0536103 100644 --- a/components/esp32/include/esp_phy_init.h +++ b/components/esp32/include/esp_phy_init.h @@ -20,6 +20,14 @@ extern "C" { #endif +/** + * @file PHY init parameters and API + */ + + +/** + * @brief Structure holding PHY init parameters + */ typedef struct { uint8_t param_ver_id; /*!< init_data structure version */ uint8_t crystal_select; /*!< 0: 40MHz, 1: 26 MHz, 2: 24 MHz, 3: auto */ @@ -129,8 +137,11 @@ typedef struct { uint8_t reserved[23]; /*!< reserved for future expansion */ } esp_phy_init_data_t; +/** + * @brief Opaque PHY calibration data + */ typedef struct { - uint8_t opaque[1904]; /*!< opaque calibration data */ + uint8_t opaque[1904]; /*!< calibration data */ } esp_phy_calibration_data_t; typedef enum { @@ -140,29 +151,97 @@ typedef enum { } esp_phy_calibration_mode_t; /** + * @brief Get PHY init data * - * @param mode - * @return + * If "Use a partition to store PHY init data" option is set in menuconfig, + * This function will load PHY init data from a partition. Otherwise, + * PHY init data will be compiled into the application itself, and this function + * will return a pointer to PHY init data located in read-only memory (DROM). + * + * If "Use a partition to store PHY init data" option is enabled, this function + * may return NULL if the data loaded from flash is not valid. + * + * @note Call esp_phy_release_init_data to release the pointer obtained using + * this function after the call to esp_wifi_init. + * + * @return pointer to PHY init data structure */ -esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode); - -#ifndef CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS +const esp_phy_init_data_t* esp_phy_get_init_data(); /** - * - * @param cal_data - * @return + * @brief Release PHY init data + * @param data pointer to PHY init data structure obtained from + * esp_phy_get_init_data function */ -esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data); +void esp_phy_release_init_data(const esp_phy_init_data_t* data); /** + * @brief Function called by esp_phy_init to load PHY calibration data * - * @param out_cal_data - * @return + * This is a convenience function which can be used to load PHY calibration + * data from NVS. Data can be stored to NVS using esp_phy_store_cal_data_to_nvs + * function. + * + * If calibration data is not present in the NVS, or + * data is not valid (was obtained for a chip with a different MAC address, + * or obtained for a different version of software), this function will + * return an error. + * + * If "Initialize PHY in startup code" option is set in menuconfig, this + * function will be used to load calibration data. To provide a different + * mechanism for loading calibration data, disable + * "Initialize PHY in startup code" option in menuconfig and call esp_phy_init + * function from the application. For an example usage of esp_phy_init and + * this function, see do_phy_init function in cpu_start.c + * + * @param out_cal_data pointer to calibration data structure to be filled with + * loaded data. + * @return ESP_OK on success */ -esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data); +esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data); + +/** + * @brief Function called by esp_phy_init to store PHY calibration data + * + * This is a convenience function which can be used to store PHY calibration + * data to the NVS. Calibration data is returned by esp_phy_init function. + * Data saved using this function to the NVS can later be loaded using + * esp_phy_store_cal_data_to_nvs function. + * + * If "Initialize PHY in startup code" option is set in menuconfig, this + * function will be used to store calibration data. To provide a different + * mechanism for storing calibration data, disable + * "Initialize PHY in startup code" option in menuconfig and call esp_phy_init + * function from the application. + * + * @param cal_data pointer to calibration data which has to be saved. + * @return ESP_OK on success + */ +esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_data); + +/** + * @brief Initialize PHY module + * + * PHY module should be initialized in order to use WiFi or BT. + * If "Initialize PHY in startup code" option is set in menuconfig, + * this function will be called automatically before app_main is called, + * using parameters obtained from esp_phy_get_init_data. + * + * Applications which don't need to enable PHY on every start up should + * disable this menuconfig option and call esp_phy_init before calling + * esp_wifi_init or bt_controller_init. See do_phy_init function in + * cpu_start.c for an example of using this function. + * + * @param init_data PHY parameters. Default set of parameters can + * be obtained by calling esp_phy_get_default_init_data + * function. + * @param mode Calibration mode (Full, partial, or no calibration) + * @param[inout] calibration_data + * @return ESP_OK on success. + */ +esp_err_t esp_phy_init(const esp_phy_init_data_t* init_data, + esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data); -#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS #ifdef __cplusplus } diff --git a/components/esp32/include/soc/dport_reg.h b/components/esp32/include/soc/dport_reg.h index 0c43c08740..1be0fdee14 100644 --- a/components/esp32/include/soc/dport_reg.h +++ b/components/esp32/include/soc/dport_reg.h @@ -1028,6 +1028,7 @@ #define DPORT_WIFI_RST_EN_REG (DR_REG_DPORT_BASE + 0x0D0) /* DPORT_WIFI_RST : R/W ;bitpos:[31:0] ;default: 32'h0 ; */ /*description: */ +#define DPORT_MAC_RST (BIT(2)) #define DPORT_WIFI_RST 0xFFFFFFFF #define DPORT_WIFI_RST_M ((DPORT_WIFI_RST_V)<<(DPORT_WIFI_RST_S)) #define DPORT_WIFI_RST_V 0xFFFFFFFF diff --git a/components/esp32/lib b/components/esp32/lib index db867fe912..a580f70a64 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit db867fe9128cc1fc273d76af5a412f6743519149 +Subproject commit a580f70a64872a7cc291b1f22455f6adbc2e35cf diff --git a/components/esp32/phy.h b/components/esp32/phy.h index ad6b9003ec..81990f2e36 100644 --- a/components/esp32/phy.h +++ b/components/esp32/phy.h @@ -41,10 +41,23 @@ int register_chipv7_phy(const esp_phy_init_data_t* init_data, esp_phy_calibratio /** * @brief Get the format version of calibration data used by PHY library. - * @return Format version number + * @return Format version number, OR'ed with BIT(16) if PHY is in WIFI only mode. */ uint32_t phy_get_rf_cal_version(); +/** + * @brief Set RF/BB for only WIFI mode or coexist(WIFI & BT) mode + * @param[in] true is for only WIFI mode, false is for coexist mode. default is 0. + * @return NULL + */ +void phy_set_wifi_mode_only(bool wifi_only); + +/** + * @brief Set BT the highest priority in coexist mode. + * @return NULL + */ +void coex_bt_high_prio(void); + #ifdef __cplusplus } #endif diff --git a/components/esp32/phy_init.c b/components/esp32/phy_init.c index 11d571f04e..32d8f64edf 100644 --- a/components/esp32/phy_init.c +++ b/components/esp32/phy_init.c @@ -25,60 +25,37 @@ #include "esp_system.h" #include "phy.h" #include "esp_log.h" +#include "nvs.h" #include "sdkconfig.h" #include "phy_init_data.h" static const char* TAG = "phy_init"; -static const esp_phy_init_data_t* phy_get_init_data(); -static void phy_release_init_data(const esp_phy_init_data_t*); -esp_err_t esp_phy_init(esp_phy_calibration_mode_t mode) +esp_err_t esp_phy_init(const esp_phy_init_data_t* init_data, + esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data) { - ESP_LOGD(TAG, "esp_phy_init, mode=%d", mode); - esp_err_t err; - const esp_phy_init_data_t* init_data = phy_get_init_data(); - if (init_data == NULL) { - ESP_LOGE(TAG, "failed to obtain PHY init data"); - return ESP_FAIL; - } - esp_phy_calibration_data_t* cal_data = - (esp_phy_calibration_data_t*) calloc(sizeof(esp_phy_calibration_data_t), 1); - if (cal_data == NULL) { - ESP_LOGE(TAG, "failed to allocate memory for RF calibration data"); - return ESP_ERR_NO_MEM; - } - // Initialize PHY function pointer table + assert(init_data); + assert(calibration_data); + // Initialize PHY pointer table phy_get_romfunc_addr(); + REG_SET_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST); + REG_CLR_BIT(DPORT_WIFI_RST_EN_REG, DPORT_MAC_RST); // Enable WiFi peripheral clock SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, 0x87cf); - // If full calibration is requested, don't need to load previous calibration data - if (mode != PHY_RF_CAL_FULL) { - err = esp_phy_load_cal_data(cal_data); - if (err != ESP_OK) { - ESP_LOGW(TAG, "failed to load RF calibration data, falling back to full calibration"); - mode = PHY_RF_CAL_FULL; - } - } - ESP_LOGV(TAG, "calling register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", init_data, cal_data, mode); - register_chipv7_phy(init_data, cal_data, mode); - if (mode != PHY_RF_CAL_NONE) { - err = esp_phy_store_cal_data(cal_data); - } else { - err = ESP_OK; - } - phy_release_init_data(init_data); - free(cal_data); // PHY maintains a copy of calibration data, so we can free this - return err; + ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d", + init_data, calibration_data, mode); + phy_set_wifi_mode_only(0); + register_chipv7_phy(init_data, calibration_data, mode); + coex_bt_high_prio(); + return ESP_OK; } // PHY init data handling functions - #if CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION -#define NO_DEFAULT_INIT_DATA #include "esp_partition.h" -static const esp_phy_init_data_t* phy_get_init_data() +const esp_phy_init_data_t* esp_phy_get_init_data() { const esp_partition_t* partition = esp_partition_find_first( ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_PHY, NULL); @@ -109,7 +86,7 @@ static const esp_phy_init_data_t* phy_get_init_data() return (const esp_phy_init_data_t*) (init_data_store + sizeof(phy_init_magic_pre)); } -static void phy_release_init_data(const esp_phy_init_data_t* init_data) +void esp_phy_release_init_data(const esp_phy_init_data_t* init_data) { free((uint8_t*) init_data - sizeof(phy_init_magic_pre)); } @@ -118,13 +95,13 @@ static void phy_release_init_data(const esp_phy_init_data_t* init_data) // phy_init_data.h will declare static 'phy_init_data' variable initialized with default init data -static const esp_phy_init_data_t* phy_get_init_data() +const esp_phy_init_data_t* esp_phy_get_init_data() { ESP_LOGD(TAG, "loading PHY init data from application binary"); return &phy_init_data; } -static void phy_release_init_data(const esp_phy_init_data_t* init_data) +void esp_phy_release_init_data(const esp_phy_init_data_t* init_data) { // no-op } @@ -132,22 +109,18 @@ static void phy_release_init_data(const esp_phy_init_data_t* init_data) // PHY calibration data handling functions - -#if CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS -#include "nvs.h" - static const char* PHY_NAMESPACE = "phy"; static const char* PHY_CAL_VERSION_KEY = "cal_version"; static const char* PHY_CAL_MAC_KEY = "cal_mac"; static const char* PHY_CAL_DATA_KEY = "cal_data"; -static esp_err_t load_cal_data_from_nvs(nvs_handle handle, +static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle, esp_phy_calibration_data_t* out_cal_data); -static esp_err_t store_cal_data_to_nvs(nvs_handle handle, +static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle, const esp_phy_calibration_data_t* cal_data); -esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data) +esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data) { nvs_handle handle; esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle); @@ -156,13 +129,13 @@ esp_err_t esp_phy_load_cal_data(esp_phy_calibration_data_t* out_cal_data) return err; } else { - err = load_cal_data_from_nvs(handle, out_cal_data); + err = load_cal_data_from_nvs_handle(handle, out_cal_data); nvs_close(handle); return err; } } -esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) +esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_data) { nvs_handle handle; esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READWRITE, &handle); @@ -171,13 +144,14 @@ esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) return err; } else { - err = store_cal_data_to_nvs(handle, cal_data); + err = store_cal_data_to_nvs_handle(handle, cal_data); nvs_close(handle); return err; } } -static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_data_t* out_cal_data) +static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle, + esp_phy_calibration_data_t* out_cal_data) { esp_err_t err; uint32_t cal_data_version; @@ -186,7 +160,8 @@ static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_d ESP_LOGD(TAG, "%s: failed to get cal_version (%d)", __func__, err); return err; } - uint32_t cal_format_version = phy_get_rf_cal_version(); + uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16)); + ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version); if (cal_data_version != cal_format_version) { ESP_LOGD(TAG, "%s: expected calibration data format %d, found %d", __func__, cal_format_version, cal_data_version); @@ -224,11 +199,12 @@ static esp_err_t load_cal_data_from_nvs(nvs_handle handle, esp_phy_calibration_d return ESP_OK; } -static esp_err_t store_cal_data_to_nvs(nvs_handle handle, +static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle, const esp_phy_calibration_data_t* cal_data) { esp_err_t err; - uint32_t cal_format_version = phy_get_rf_cal_version(); + uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16)); + ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version); err = nvs_set_u32(handle, PHY_CAL_VERSION_KEY, cal_format_version); if (err != ESP_OK) { return err; @@ -243,22 +219,6 @@ static esp_err_t store_cal_data_to_nvs(nvs_handle handle, return err; } -#else // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS - -// Default implementation: don't store or load calibration data. -// These functions are defined as weak and can be overridden in the application. - -esp_err_t esp_phy_store_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) +void register_chipv7_phy_stub() { - // pretend that calibration data is stored - return ESP_OK; } - -esp_err_t esp_phy_load_cal_data(const esp_phy_calibration_data_t* cal_data) __attribute__((weak)) -{ - // nowhere to load data from - return ESP_ERR_NOT_SUPPORTED; -} - -#endif // CONFIG_ESP32_STORE_PHY_CALIBRATION_DATA_IN_NVS - diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 751542ee12..7c9ec89a6f 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -62,6 +62,13 @@ extern "C" void nvs_dump() s_nvs_storage.debugDump(); } +extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) +{ + ESP_LOGD(TAG, "nvs_flash_init_custom start=%d count=%d", baseSector, sectorCount); + s_nvs_handles.clear(); + return s_nvs_storage.init(baseSector, sectorCount); +} + #ifdef ESP_PLATFORM extern "C" esp_err_t nvs_flash_init(void) { @@ -81,13 +88,6 @@ extern "C" esp_err_t nvs_flash_init(void) } #endif -extern "C" esp_err_t nvs_flash_init_custom(uint32_t baseSector, uint32_t sectorCount) -{ - ESP_LOGD(TAG, "nvs_flash_init_custom start=%d count=%d", baseSector, sectorCount); - s_nvs_handles.clear(); - return s_nvs_storage.init(baseSector, sectorCount); -} - static esp_err_t nvs_find_ns_handle(nvs_handle handle, HandleEntry& entry) { auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), [=](HandleEntry& e) -> bool { From 0b265dc2a71247eccf0574b3a60fcebb7f99ceee Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 18 Nov 2016 19:17:13 +0800 Subject: [PATCH 258/285] nvs, spi_flash: handle case when source data is in DROM --- components/nvs_flash/src/nvs_page.cpp | 25 +++++++++++++++++++- components/spi_flash/flash_ops.c | 6 +++++ components/spi_flash/include/esp_spi_flash.h | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/components/nvs_flash/src/nvs_page.cpp b/components/nvs_flash/src/nvs_page.cpp index d2ca225352..80ccb1f6d0 100644 --- a/components/nvs_flash/src/nvs_page.cpp +++ b/components/nvs_flash/src/nvs_page.cpp @@ -114,7 +114,30 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size) assert(mFirstUsedEntry != INVALID_ENTRY); const uint16_t count = size / ENTRY_SIZE; - auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), data, size); + const uint8_t* buf = data; + +#ifdef ESP_PLATFORM + /* On the ESP32, data can come from DROM, which is not accessible by spi_flash_write + * function. To work around this, we copy the data to heap if it came from DROM. + * Hopefully this won't happen very often in practice. For data from DRAM, we should + * still be able to write it to flash directly. + * TODO: figure out how to make this platform-specific check nicer (probably by introducing + * a platform-specific flash layer). + */ + if ((uint32_t) data < 0x3ff00000) { + buf = (uint8_t*) malloc(size); + if (!buf) { + return ESP_ERR_NO_MEM; + } + memcpy((void*)buf, data, size); + } +#endif //ESP_PLATFORM + auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), buf, size); +#ifdef ESP_PLATFORM + if (buf != data) { + free((void*)buf); + } +#endif //ESP_PLATFORM if (rc != ESP_OK) { mState = PageState::INVALID; return rc; diff --git a/components/spi_flash/flash_ops.c b/components/spi_flash/flash_ops.c index 134e1fe65b..3358c550f2 100644 --- a/components/spi_flash/flash_ops.c +++ b/components/spi_flash/flash_ops.c @@ -135,6 +135,12 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t si if (size % 4 != 0) { return ESP_ERR_INVALID_SIZE; } + if ((uint32_t) src < 0x3ff00000) { + // if source address is in DROM, we won't be able to read it + // from within SPIWrite + // TODO: consider buffering source data using heap and writing it anyway? + return ESP_ERR_INVALID_ARG; + } // Out of bound writes are checked in ROM code, but we can give better // error code here if (dest_addr + size > g_rom_flashchip.chip_size) { diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 5d124da6b2..f940c0ad50 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -76,6 +76,8 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size); * * @note Address in flash, dest, has to be 4-byte aligned. * This is a temporary limitation which will be removed. + * @note If source address is in DROM, this function will return + * ESP_ERR_INVALID_ARG. * * @param dest destination address in Flash * @param src pointer to the source buffer From 1f6585dd4ff67c9d3feef78691478c9e64967f88 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 18 Nov 2016 20:34:54 +0800 Subject: [PATCH 259/285] docs: update partition tables documentation --- docs/partition-tables.rst | 46 ++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index a1a46866ee..c9709029d8 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -17,40 +17,30 @@ The simplest way to use the partition table is to `make menuconfig` and choose o In both cases the factory app is flashed at offset 0x10000. If you `make partition_table` then it will print a summary of the partition table. -Known Issues ------------- - -The below design document outlines the goals for the partition table system. At the moment, only some features are used: - -- data partition types "rf" & "wifi" are unused and can be entirely omitted to save space. -- NVS (non-volatile-storage) uses a hardcoded 12KB (0x3000 byte) region at offset 0x9000. - -Once a full user API is in place for partition access, these limitations will be resolved and you'll be able to use the partition mechanism fully for storing data in flash. - Built-in Partition Tables ------------------------- Here is the summary printed for the "Single factory app, no OTA" configuration:: # Espressif ESP32 Partition Table - # Name, Type, SubType, Offset, Size - factory, app, factory, 0x10000, 1M - rfdata, data, rf, 0x110000, 256K - wifidata,data, wifi, 0x150000, 256K + # Name, Type, SubType, Offset, Size + nvs, data, nvs, 0x9000, 0x6000 + phy_init, data, phy, 0xf000, 0x1000 + factory, app, factory, 0x10000, 1M * At a 0x10000 (64KB) offset in the flash is the app labelled "factory". The bootloader will run this app by default. -* There are also two data regions defined in the partition table for storing RF & Wifi calibration data. +* There are also two data regions defined in the partition table for storing NVS library partition and PHY init data. Here is the summary printed for the "Factory app, two OTA definitions" configuration:: # Espressif ESP32 Partition Table - # Name, Type, SubType, Offset, Size - factory, app, factory, 0x10000, 1M - ota_0, app, ota_0, 0x110000, 1M - ota_1, app, ota_1, 0x210000, 1M - rfdata, data, rf, 0x310000, 256K - wifidata,data, wifi, 0x350000, 256K - otadata, data, ota, 0x390000, 256K + # Name, Type, SubType, Offset, Size + nvs, data, nvs, 0x9000, 0x4000 + otadata, data, ota, 0xd000, 0x2000 + phy_init, data, phy, 0xf000, 0x1000 + factory, 0, 0, 0x10000, 1M + ota_0, 0, ota_0, , 1M + ota_1, 0, ota_1, , 1M * There are now three app partition definitions. * The type of all three are set as "app", but the subtype varies between the factory app at 0x10000 and the next two "OTA" apps. @@ -65,13 +55,13 @@ If you choose "Custom partition table CSV" in menuconfig then you can also enter The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table:: # Name, Type, SubType, Offset, Size + nvs, data, nvs, 0x9000, 0x4000 + otadata, data, ota, 0xd000, 0x2000 + phy_init, data, phy, 0xf000, 0x1000 factory, app, factory, 0x10000, 1M ota_0, app, ota_0, , 1M ota_1, app, ota_1, , 1M - rfdata, data, rf, , 256K - wifidata, data, wifi, , 256K - otadata, data, ota, , 256K - + * Whitespace between fields is ignored, and so is any line starting with # (comments). * Each non-comment line in the CSV file is a partition definition. * Only the offset for the first partition is supplied. The gen_esp32part.py tool fills in each remaining offset to start after the preceding partition. @@ -93,7 +83,7 @@ Subtype When type is "app", the subtype field can be specified as factory (0), ota_0 (0x10) ... ota_15 (0x1F) and test (0x20). Or it can be any number 0-255 (0x00-0xFF). The bootloader will execute the factory app unless there it sees a partition of type data/ota, in which case it reads this partition to determine which OTA image to boot -When type is "data", the subtype field can be specified as ota (0), rf (1), wifi (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Other "data" subtypes are reserved for Espressif use. To create custom data partition subtypes then use a custom type value, and choose any subtype 0x00-0xFF. +When type is "data", the subtype field can be specified as ota (0), phy (1), nvs (2). Or it can be a number 0x00-0xFF. The bootloader ignores all data subtypes except for ota. Subtypes 0-0x7f are reserved for Espressif use. To create custom data partition subtypes use "data" type, and choose any unused subtype in 0x80-0xFF range. If you are porting a filesystem to the ESP-IDF, consider opening a PR to add the new subtype to esp_partition.h file. Offset & Size ~~~~~~~~~~~~~ @@ -104,6 +94,8 @@ App partitions have to be at offsets aligned to 0x10000 (64K). If you leave the Sizes and offsets can be specified as decimal numbers, hex numbers with the prefix 0x, or size multipliers M or K (1024 and 1024*1024 bytes). +NVS data partition has to be at least 0x3000 bytes long, and OTA data parition has to be 0x2000 bytes long. If you are using NVS in your application to store a lot of data, consider using a custom partition table with larger NVS partition. + Generating Binary Partition Table --------------------------------- From 659ec8facbedd9863a437626af6fb96c42c1f9b3 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Fri, 18 Nov 2016 21:11:54 +0800 Subject: [PATCH 260/285] esp32: add phy_printf 1. add phy_printf into libcore 2. update libphy.a which has important optimization for ht40 traffic --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index a580f70a64..ea9c156e8a 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit a580f70a64872a7cc291b1f22455f6adbc2e35cf +Subproject commit ea9c156e8a67d27623eab6f98ce5a55a00c8fb19 From fa57720cdf54f3f71317a43eed77e2380c760a29 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Sat, 19 Nov 2016 22:25:30 +0800 Subject: [PATCH 261/285] lwip: optimize low_level_output When the parameter pbuf for low_level_output is a list, malloc a new pbuf of which the length equals to the total length of pbuf and send the new pbuf to L2 --- components/lwip/port/netif/wlanif.c | 44 ++++++++++++----------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index 3fd2a3192a..2c2d75b74d 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -118,37 +118,29 @@ low_level_init(struct netif *netif) static err_t low_level_output(struct netif *netif, struct pbuf *p) { - struct pbuf *q; - wifi_interface_t wifi_if = tcpip_adapter_get_wifi_if(netif); + wifi_interface_t wifi_if = tcpip_adapter_get_wifi_if(netif); + struct pbuf *q = p; + err_t ret; + + if (wifi_if >= WIFI_IF_MAX) { + return ERR_IF; + } - if (wifi_if >= WIFI_IF_MAX) { - return ERR_IF; - } - -#if ESP_LWIP - q = p; - u16_t pbuf_x_len = 0; - pbuf_x_len = q->len; if(q->next !=NULL) { - //char cnt = 0; - struct pbuf *tmp = q->next; - while(tmp != NULL) - { - memcpy( (u8_t *)( (u8_t *)(q->payload) + pbuf_x_len), (u8_t *)tmp->payload , tmp->len ); - pbuf_x_len += tmp->len; - //cnt++; - tmp = tmp->next; + LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); + printf("low level_output: len=%d\n", p->tot_len); + q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); + if (q != NULL) { + pbuf_copy(q, p); + } else { + return ERR_MEM; } } - - return esp_wifi_internal_tx(wifi_if, q->payload, pbuf_x_len); -#else - for(q = p; q != NULL; q = q->next) { - esp_wifi_internal_tx(wifi_if, q->payload, q->len); - } - return ERR_OK; -#endif + + ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + pbuf_free(q); + return ret; } /** From 8cd48d9f9c1afd7c9929333df7bff60dc8f00d73 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Sun, 20 Nov 2016 17:05:44 +0800 Subject: [PATCH 262/285] lwip: rework for low_level_output --- components/lwip/port/netif/wlanif.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index 2c2d75b74d..3a4688004b 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -126,8 +126,9 @@ low_level_output(struct netif *netif, struct pbuf *p) return ERR_IF; } - if(q->next !=NULL) - { + if(q->next == NULL) { + ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + } else { LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); printf("low level_output: len=%d\n", p->tot_len); q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); @@ -136,10 +137,10 @@ low_level_output(struct netif *netif, struct pbuf *p) } else { return ERR_MEM; } + ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); + pbuf_free(q); } - ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); - pbuf_free(q); return ret; } From fa3e4103abd1a0d8af787cf79b757797a86bedd1 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 16 Nov 2016 20:11:32 +0100 Subject: [PATCH 263/285] docs: Hyperlinks to guides --- CONTRIBUTING.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 968826790a..5810d06b98 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -15,7 +15,9 @@ Before sending us a Pull Request, please consider this list of points: * Is the contribution entirely your own work, or already licensed under an Apache License 2.0 compatible Open Source License? If not then we unfortunately cannot accept it. -* Does any new code conform to the esp-idf Style Guide? (Style Guide currently pending). +* Does any new code conform to the esp-idf :doc:`Style Guide `? + +* Does the code documentation follow requirements in :doc:`documenting-code`? * Is the code adequately commented for people to understand how it is structured? From 4e9e8f52acdba97d7f10d16ba440904d9c709425 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 16 Nov 2016 20:13:18 +0100 Subject: [PATCH 264/285] doc: Documenting Code clean up --- .../_static/doc-code-documentation-inline.png | Bin 52513 -> 52554 bytes docs/documenting-code.rst | 121 +++++++++--------- 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/docs/_static/doc-code-documentation-inline.png b/docs/_static/doc-code-documentation-inline.png index 72bf22f156224e4e32e7934828ccc73f15cea67e..9de5d51a2bd0c0fca7f0cb53b8b0a6c3ff9ed38c 100644 GIT binary patch delta 14525 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 delta 14441 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

gnqNgsd-rC0@|9-?9@pqq%hiic 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 Date: Wed, 16 Nov 2016 20:14:48 +0100 Subject: [PATCH 265/285] doc: API Reference - examples & fixes --- docs/api/bt.rst | 11 +++++++---- docs/api/ledc.rst | 3 +++ docs/api/nvs_flash.rst | 19 +++++++++++++++++-- docs/api/vfs.rst | 1 + 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/api/bt.rst b/docs/api/bt.rst index 0ab17b2aa5..1824bc454a 100644 --- a/docs/api/bt.rst +++ b/docs/api/bt.rst @@ -9,15 +9,19 @@ Overview Application Example ------------------- -`Instructions`_ +Check `/examples `_ folder of `espressif/esp-idf `_ repository, that contains the following example: -API Reference -------------- +`05_ble_adv `_ + + This is a BLE advertising demo with virtual HCI interface. Send Reset/ADV_PARAM/ADV_DATA/ADV_ENABLE HCI command for BLE advertising. `Instructions`_ .. _Instructions: template.html +API Reference +------------- + Header Files ^^^^^^^^^^^^ @@ -35,4 +39,3 @@ Functions .. doxygenfunction:: API_vhci_host_register_callback .. doxygenfunction:: API_vhci_host_send_packet .. doxygenfunction:: bt_controller_init - diff --git a/docs/api/ledc.rst b/docs/api/ledc.rst index f379e9d008..855f822163 100644 --- a/docs/api/ledc.rst +++ b/docs/api/ledc.rst @@ -27,7 +27,10 @@ Data Structures ^^^^^^^^^^^^^^^ .. doxygenstruct:: ledc_channel_config_t + :members: + .. doxygenstruct:: ledc_timer_config_t + :members: Macros ^^^^^^ diff --git a/docs/api/nvs_flash.rst b/docs/api/nvs_flash.rst index 5d36343552..a765f7608e 100644 --- a/docs/api/nvs_flash.rst +++ b/docs/api/nvs_flash.rst @@ -5,8 +5,23 @@ Application Example Two examples are provided in ESP-IDF examples directory: -- `07_nvs_rw_value `_ demostrates how to read and write integer values -- `08_nvs_rw_blob `_ demostrates how to read and write variable length binary values +`07_nvs_rw_value `_ + + Demonstrates how to read and write a single integer value using NVS. + + The value holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts. + + Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVS. Diagnostic is provided in plain text to help track program flow and capture any issues on the way. + +`08_nvs_rw_blob `_ + + Demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP32 module restarts. + + * value - tracks number of ESP32 module soft and hard restarts. + * blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. New run time is added to the table on each manually triggered soft restart and written back to NVS. Triggering is done by pulling down GPIO0. + + Example also shows how to implement diagnostics if read / write operation was successful. + API Reference ------------- diff --git a/docs/api/vfs.rst b/docs/api/vfs.rst index df6cd03f67..798aac5492 100644 --- a/docs/api/vfs.rst +++ b/docs/api/vfs.rst @@ -25,6 +25,7 @@ Structures ^^^^^^^^^^ .. doxygenstruct:: esp_vfs_t + :members: Functions ^^^^^^^^^ From a86c431f981cb20031997c95352232d49865a5dd Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 16 Nov 2016 20:16:47 +0100 Subject: [PATCH 266/285] doc: Formating clean up --- docs/build_system.rst | 15 ++++++++++----- docs/security/secure-boot.rst | 8 ++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/build_system.rst b/docs/build_system.rst index aa14cdda56..4ba89f8652 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -57,6 +57,7 @@ Example Project --------------- An example project directory tree might look like this:: + - myProject/ - Makefile - sdkconfig @@ -66,11 +67,11 @@ An example project directory tree might look like this:: - component2/ - component.mk - Kconfig - src1.c - - include/ - - component2.h + - include/ - component2.h - main/ - src1.c - src2.c - component.mk + - build/ This example "myProject" contains the following elements: @@ -101,6 +102,7 @@ Minimal Example Makefile ^^^^^^^^^^^^^^^^^^^^^^^^ :: + PROJECT_NAME := myProject include $(IDF_PATH)/make/project.mk @@ -135,7 +137,8 @@ Minimal Component Makefile ^^^^^^^^^^^^^^^^^^^^^^^^^^ The minimal ``component.mk`` file is an empty file(!). If the file is empty, the default component behaviour is set: -- All source files in the same directory as the makefile (*.c, *.cpp, *.S) will be compiled into the component library + +- All source files in the same directory as the makefile (``*.c``, ``*.cpp``, ``*.S``) will be compiled into the component library - A sub-directory "include" will be added to the global include search path for all other components. - The component library will be linked into the project app. @@ -209,8 +212,8 @@ The following variables can be set inside ``component.mk`` to control the build ``COMPONENT_PRIV_INCLUDEDIRS`` variable, except these paths are not expanded relative to the component directory. - ``COMPONENT_SRCDIRS``: Directory paths, must be relative to the - component directory, which will be searched for source files (*.cpp, - *.c, *.S). Defaults to '.', ie the component directory + component directory, which will be searched for source files (``*.cpp``, + ``*.c``, ``*.S``). Defaults to '.', ie the component directory itself. Override this to specify a different list of directories which contain source files. - ``COMPONENT_OBJS``: Object files to compile. Default value is a .o @@ -366,12 +369,14 @@ The configuration system can be used to conditionally compile some files depending on the options selected in ``make menuconfig``: ``Kconfig``:: + config FOO_ENABLE_BAR bool "Enable the BAR feature." help This enables the BAR feature of the FOO component. ``component.mk``:: + COMPONENT_OBJS := foo_a.o foo_b.o ifdef CONFIG_FOO_BAR diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index b352b3964c..113d250902 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -27,18 +27,18 @@ This is a high level overview of the secure boot process. Step by step instructi 2. Secure Boot Configuration includes "Secure boot signing key", which is a file path. This file is a ECDSA public/private key pair in a PEM format file. -2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. +3. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. -3. On first boot, the software bootloader follows the following process to enable secure boot: +4. On first boot, the software bootloader follows the following process to enable secure boot: - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) -4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`. +5. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify the signature appended to all subsequent partition tables and app images before they are booted. +6. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify the signature appended to all subsequent partition tables and app images before they are booted. Keys ---- From dd0585e84e6e28039131bc227b1b0010eeeea28a Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 16 Nov 2016 20:17:44 +0100 Subject: [PATCH 267/285] doc: Index clean up --- docs/index.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 8bc7a72f29..adf07b586e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,6 +5,9 @@ ESP32 Programming Guide Until ESP-IDF release 1.0, this documentation is a draft. It is incomplete and may have mistakes. Please mind your step! +Documentation adressed to developers of applications for `ESP32 `_ by `Espressif `_ using `esp-idf `_. + + Contents: .. toctree:: @@ -41,7 +44,7 @@ Contents: 1. System - TBA 1.1. Fundamentals of multiprocessor programming with FreeRTOS - TBA 1.2. Application startup flow - TBA - 1.3. Flash encryption and secure boot: how they work and APIs - TBA + 1.3. Flash encryption and secure boot: how they work and APIs 1.4. Lower Power Coprocessor - TBA 1.5. Watchdogs 1.6. ... @@ -61,7 +64,7 @@ Contents: 6.1. GPIO 6.2. ADC - TBA 6.3. DAC - TBA - 6.4. UART - TBA + 6.4. UART 6.5. I2C - TBA 6.6. I2S - TBA 6.7. SPI - TBA @@ -78,9 +81,9 @@ Contents: 7.3. Touch Sensor - TBA 8. Protocols - TBA 9. Components - 9.1. Logging - 9.2 Non-Volatile Storage - 9.3 Virtual Filesystem + 9.1. Logging + 9.2 Non-Volatile Storage + 9.3 Virtual Filesystem 9.3. Http sever - TBA 10. Applications - TBA .. @@ -128,11 +131,9 @@ Contents: COPYRIGHT -.. About - TBA - Indices -======= +------- * :ref:`genindex` * :ref:`search` From d4b5bd347da854a79bee24a3ee88741fcca4a362 Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Wed, 16 Nov 2016 20:27:55 +0100 Subject: [PATCH 268/285] Doxygen configuration clean up --- docs/Doxyfile | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index 1f53a85931..eeb4fe740d 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,3 +1,18 @@ +# This is Doxygen configuration file +# +# Doxygen provides over 260 configuration statements +# To make this file easier to follow, +# it contains only statements that are non-default +# +# NOTE: +# It is recommended not to change defaults unless specifically required +# Test any changes how they affect generated documentation +# Make sure that correct warnings are generated to flag issues with documented code +# +# For the complete list of configuration statements see: +# http://www.stack.nl/~dimitri/doxygen/manual/config.html + + PROJECT_NAME = "ESP32 Programming Guide" INPUT = ../components/esp32/include/esp_wifi.h \ @@ -10,14 +25,18 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_int_wdt.h \ ../components/esp32/include/esp_task_wdt.h -WARN_NO_PARAMDOC = YES +## Get warnings for functions that have no documentation for their parameters or return value +## +WARN_NO_PARAMDOC = YES -RECURSIVE = NO -CASE_SENSE_NAMES = NO -EXTRACT_ALL = NO +## Do not complain about not having dot +## +HAVE_DOT = NO -GENERATE_XML = YES -XML_OUTPUT = xml +## Generate XML that is required for Breathe +## +GENERATE_XML = YES +XML_OUTPUT = xml GENERATE_HTML = NO HAVE_DOT = NO @@ -25,5 +44,9 @@ GENERATE_LATEX = NO GENERATE_MAN = YES GENERATE_RTF = NO +## Skip distracting progress messages +## +QUIET = YES +## Log warnings in a file for further review +## WARN_LOGFILE = "doxygen-warning-log.txt" - From a8fb9f2b84321baf1ff0e4f28e458c23f02a05e1 Mon Sep 17 00:00:00 2001 From: tzx Date: Thu, 10 Nov 2016 11:24:31 +0800 Subject: [PATCH 269/285] ipv6 interface: add branch for ipv6 interface --- components/esp32/include/esp_event.h | 8 ++- components/lwip/api/sockets.c | 6 +- components/lwip/core/ipv4/autoip.c | 7 ++ components/lwip/core/ipv6/nd6.c | 22 ++++++ components/lwip/core/netif.c | 41 ------------ components/lwip/include/lwip/lwip/autoip.h | 5 ++ components/lwip/include/lwip/lwip/nd6.h | 4 ++ components/lwip/include/lwip/lwip/netif.h | 9 ++- components/lwip/include/lwip/port/lwipopts.h | 50 +++++++++++++- .../tcpip_adapter/include/tcpip_adapter.h | 37 +++++++++- components/tcpip_adapter/tcpip_adapter_lwip.c | 67 ++++++++++++++++++- 11 files changed, 202 insertions(+), 54 deletions(-) diff --git a/components/esp32/include/esp_event.h b/components/esp32/include/esp_event.h index 55575b13a8..06e35d6ad6 100644 --- a/components/esp32/include/esp_event.h +++ b/components/esp32/include/esp_event.h @@ -44,6 +44,7 @@ typedef enum { SYSTEM_EVENT_AP_STACONNECTED, /**< a station connected to ESP32 soft-AP */ SYSTEM_EVENT_AP_STADISCONNECTED, /**< a station disconnected from ESP32 soft-AP */ SYSTEM_EVENT_AP_PROBEREQRECVED, /**< Receive probe request packet in soft-AP interface */ + SYSTEM_EVENT_AP_STA_GOT_IP6, /**< ESP32 station or ap interface v6IP addr is preferred */ SYSTEM_EVENT_MAX } system_event_id_t; @@ -79,7 +80,11 @@ typedef struct { typedef struct { uint8_t pin_code[8]; /**< PIN code of station in enrollee mode */ -}system_event_sta_wps_er_pin_t; +} system_event_sta_wps_er_pin_t; + +typedef struct { + tcpip_adapter_ip6_info_t ip6_info; +} system_event_ap_sta_got_ip6_t; typedef struct { uint8_t mac[6]; /**< MAC address of the station connected to ESP32 soft-AP */ @@ -106,6 +111,7 @@ typedef union { system_event_ap_staconnected_t sta_connected; /**< a station connected to ESP32 soft-AP */ system_event_ap_stadisconnected_t sta_disconnected; /**< a station disconnected to ESP32 soft-AP */ system_event_ap_probe_req_rx_t ap_probereqrecved; /**< ESP32 soft-AP receive probe request packet */ + system_event_ap_sta_got_ip6_t got_ip6; /**< ESP32 station or ap ipv6 addr state change to preferred */ } system_event_info_t; typedef struct { diff --git a/components/lwip/api/sockets.c b/components/lwip/api/sockets.c index 1529382f50..4acf518cc9 100755 --- a/components/lwip/api/sockets.c +++ b/components/lwip/api/sockets.c @@ -2775,8 +2775,12 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ case IPPROTO_IPV6: switch (optname) { case IPV6_V6ONLY: - /* @todo: this does not work for datagram sockets, yet */ + /* @todo: this does not work for datagram sockets, yet */ +#if CONFIG_MDNS + //LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); +#else LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); +#endif if (*(const int*)optval) { netconn_set_ipv6only(sock->conn, 1); } else { diff --git a/components/lwip/core/ipv4/autoip.c b/components/lwip/core/ipv4/autoip.c index 19b1928368..faac4957ca 100755 --- a/components/lwip/core/ipv4/autoip.c +++ b/components/lwip/core/ipv4/autoip.c @@ -72,6 +72,7 @@ #include "lwip/netif.h" #include "lwip/autoip.h" #include "netif/etharp.h" +#include "lwip/dhcp.h" #include #include @@ -269,6 +270,12 @@ autoip_bind(struct netif *netif) netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr); /* interface is used by routing now that an address is set */ +#if ESP_LWIP + struct dhcp *dhcp = netif->dhcp; + if (dhcp->cb != NULL) { + dhcp->cb(); + } +#endif return ERR_OK; } diff --git a/components/lwip/core/ipv6/nd6.c b/components/lwip/core/ipv6/nd6.c index 36f8f78c35..1cec55db9e 100755 --- a/components/lwip/core/ipv6/nd6.c +++ b/components/lwip/core/ipv6/nd6.c @@ -632,6 +632,22 @@ nd6_input(struct pbuf *p, struct netif *inp) pbuf_free(p); } +#ifdef ESP_LWIP + +/** Set callback for ipv6 addr status changed . + * + * @param netif the netif from which to remove the struct dhcp + * @param cb callback for dhcp + */ +void nd6_set_cb(struct netif *netif, void (*cb)(struct netif *netif, u8_t ip_index)) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + + if (netif != NULL && netif_is_up(netif)) { + netif->ipv6_addr_cb = cb; + } +} +#endif /** * Periodic timer for Neighbor discovery functions: @@ -797,6 +813,12 @@ nd6_tmr(void) if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { /* No NA received in response. Mark address as valid. */ netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED; +#ifdef ESP_LWIP + if (netif->ipv6_addr_cb != NULL) { + netif->ipv6_addr_cb(netif, i); + } +#endif + /* TODO implement preferred and valid lifetimes. */ } else if (netif->flags & NETIF_FLAG_UP) { #if LWIP_IPV6_MLD diff --git a/components/lwip/core/netif.c b/components/lwip/core/netif.c index 5c308a957c..f745f29ac5 100755 --- a/components/lwip/core/netif.c +++ b/components/lwip/core/netif.c @@ -968,9 +968,6 @@ netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit) } } -#if ESP_LWIP - ip6_addr_set( ip_2_ip6(&netif->link_local_addr), ip_2_ip6(&netif->ip6_addr[0]) ); -#endif /* Set address state. */ #if LWIP_IPV6_DUP_DETECT_ATTEMPTS @@ -1022,44 +1019,6 @@ netif_add_ip6_address(struct netif *netif, const ip6_addr_t *ip6addr, s8_t *chos return ERR_VAL; } - -#if ESP_LWIP -void -netif_create_ip4_linklocal_address(struct netif * netif) -{ -#if 1 - ip_addr_t linklocal; - ip_addr_t linklocal_mask; - ip4_addr_t addr = {0}; - /* Link-local prefix and mask. */ - IP4_ADDR(ip_2_ip4(&linklocal), 169, 254, 0, 0); - IP4_ADDR(ip_2_ip4(&linklocal_mask), 255, 255, 0, 0); - - if (!ip4_addr_netcmp( ip_2_ip4(&linklocal), ip_2_ip4(&netif->link_local_addr), ip_2_ip4(&linklocal_mask) ) && - !ip4_addr_isany(ip_2_ip4(&netif->ip_addr)) ) { - IP4_ADDR( ip_2_ip4(&netif->link_local_addr), 169, 254, ip4_addr3( ip_2_ip4(&netif->ip_addr) ) - , ip4_addr4( ip_2_ip4(&netif->ip_addr) ) ); - return; - } - - while ( !(addr.addr) || !ip4_addr4(&addr) ) - //os_get_random((unsigned char *)&addr, sizeof(addr)); - addr.addr = LWIP_RAND(); - - - if ( ip_2_ip4(&netif->netmask)->addr > IP_CLASSB_NET && - !ip4_addr_isany( ip_2_ip4(&netif->ip_addr) )) { // random host address - IP4_ADDR( ip_2_ip4(&netif->link_local_addr), 169, 254, ip4_addr3( ip_2_ip4(&netif->ip_addr)) - , ip4_addr4(&addr)); - } else { - IP4_ADDR( ip_2_ip4(&netif->link_local_addr), 169, 254, ip4_addr3(&addr), ip4_addr4(&addr) ); - } -#endif -} - -#endif - - /** Dummy IPv6 output function for netifs not supporting IPv6 */ static err_t diff --git a/components/lwip/include/lwip/lwip/autoip.h b/components/lwip/include/lwip/lwip/autoip.h index c89fe3ff12..3bb413f493 100755 --- a/components/lwip/include/lwip/lwip/autoip.h +++ b/components/lwip/include/lwip/lwip/autoip.h @@ -68,8 +68,13 @@ extern "C" { #define ANNOUNCE_NUM 2 /* (number of announcement packets) */ #define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */ #define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */ +#if CONFIG_MDNS +#define MAX_CONFLICTS 9 /* (max conflicts before rate limiting) */ +#define RATE_LIMIT_INTERVAL 20 /* seconds (delay between successive attempts) */ +#else #define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */ #define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */ +#endif #define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */ /* AutoIP client states */ diff --git a/components/lwip/include/lwip/lwip/nd6.h b/components/lwip/include/lwip/lwip/nd6.h index d0646f1b4e..27a4c8137a 100755 --- a/components/lwip/include/lwip/lwip/nd6.h +++ b/components/lwip/include/lwip/lwip/nd6.h @@ -352,6 +352,10 @@ err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf * p); void nd6_reachability_hint(const ip6_addr_t * ip6addr); #endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ +#if ESP_LWIP +/** set nd6 callback when ipv6 addr state pref*/ +void nd6_set_cb(struct netif *netif, void (*cb)(struct netif *netif, u8_t ip_index)); +#endif #ifdef __cplusplus } #endif diff --git a/components/lwip/include/lwip/lwip/netif.h b/components/lwip/include/lwip/lwip/netif.h index 666f77eb96..34e6d44897 100755 --- a/components/lwip/include/lwip/lwip/netif.h +++ b/components/lwip/include/lwip/lwip/netif.h @@ -190,11 +190,6 @@ struct netif { /** pointer to next in linked list */ struct netif *next; -#if ESP_LWIP -//ip_addr_t is changed by marco IPV4, IPV6 - ip_addr_t link_local_addr; -#endif - #if LWIP_IPV4 /** IP address configuration in network byte order */ ip_addr_t ip_addr; @@ -207,6 +202,10 @@ struct netif { /** The state of each IPv6 address (Tentative, Preferred, etc). * @see ip6_addr.h */ u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; +#if ESP_LWIP + void (*ipv6_addr_cb)(struct netif* netif, u8_t ip_idex); /* callback for ipv6 addr states changed */ +#endif + #endif /* LWIP_IPV6 */ /** This function is called by the network device driver * to pass a packet up the TCP/IP stack. */ diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 26bdc3a4e9..b63fc80e57 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -65,8 +65,8 @@ */ #define SMEMCPY(dst,src,len) memcpy(dst,src,len) -extern unsigned long os_random(void); -#define LWIP_RAND rand +#define LWIP_RAND rand + /* ------------------------------------ ---------- Memory options ---------- @@ -200,13 +200,35 @@ extern unsigned long os_random(void); */ #define LWIP_DHCP 1 +#define DHCP_MAXRTX 0 -#define DHCP_MAXRTX 0 //(*(volatile uint32*)0x600011E0) /* ------------------------------------ ---------- AUTOIP options ---------- ------------------------------------ */ +#if CONFIG_MDNS + /** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#define LWIP_AUTOIP 1 + +/** +* LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on +* the same interface at the same time. +*/ +#define LWIP_DHCP_AUTOIP_COOP 1 + +/** +* LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes +* that should be sent before falling back on AUTOIP. This can be set +* as low as 1 to get an AutoIP address very quickly, but you should +* be prepared to handle a changing IP address when DHCP overrides +* AutoIP. +*/ +#define LWIP_DHCP_AUTOIP_COOP_TRIES 2 +#endif + /* ---------------------------------- ---------- SNMP options ---------- @@ -308,6 +330,19 @@ extern unsigned long os_random(void); ---------- LOOPIF options ---------- ------------------------------------ */ +#if CONFIG_MDNS +/** + * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP + * address equal to the netif IP address, looping them back up the stack. + */ +#define LWIP_NETIF_LOOPBACK 1 + +/** + * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback + * sending for each netif (0 = disabled) + */ +#define LWIP_LOOPBACK_MAX_PBUFS 8 +#endif /* ------------------------------------ @@ -414,6 +449,15 @@ extern unsigned long os_random(void); */ #define SO_REUSE CONFIG_LWIP_SO_REUSE +#if CONFIG_MDNS +/** + * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets + * to all local matches if SO_REUSEADDR is turned on. + * WARNING: Adds a memcpy for every packet if passing to more than one pcb! + */ +#define SO_REUSE_RXTOALL 1 +#endif + /* ---------------------------------------- ---------- Statistics options ---------- diff --git a/components/tcpip_adapter/include/tcpip_adapter.h b/components/tcpip_adapter/include/tcpip_adapter.h index 45d6ade305..f7063a8e17 100644 --- a/components/tcpip_adapter/include/tcpip_adapter.h +++ b/components/tcpip_adapter/include/tcpip_adapter.h @@ -64,18 +64,22 @@ typedef struct { ip4_addr_t gw; } tcpip_adapter_ip_info_t; +typedef struct { + ip6_addr_t ip; +} tcpip_adapter_ip6_info_t; + typedef dhcps_lease_t tcpip_adapter_dhcps_lease_t; #if CONFIG_DHCP_STA_LIST typedef struct { uint8_t mac[6]; ip4_addr_t ip; -}tcpip_adapter_sta_info_t; +} tcpip_adapter_sta_info_t; typedef struct { tcpip_adapter_sta_info_t sta[ESP_WIFI_MAX_CONN_NUM]; int num; -}tcpip_adapter_sta_list_t; +} tcpip_adapter_sta_list_t; #endif #endif @@ -211,6 +215,35 @@ esp_err_t tcpip_adapter_get_ip_info(tcpip_adapter_if_t tcpip_if, tcpip_adapter_i */ esp_err_t tcpip_adapter_set_ip_info(tcpip_adapter_if_t tcpip_if, tcpip_adapter_ip_info_t *ip_info); +/** + * @brief create interface's linklocal IPv6 information + * + * @note this function will create a linklocal IPv6 address about input interface, + * if this address status changed to preferred, will call event call back , + * notify user linklocal IPv6 address has been verified + * + * @param[in] tcpip_if: the interface which we want to set IP information + * + * + * @return ESP_OK + * ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS + */ +esp_err_t tcpip_adapter_create_ip6_linklocal(tcpip_adapter_if_t tcpip_if); + +/** + * @brief get interface's linkloacl IPv6 information + * + * There has an IPv6 information copy in adapter library, if interface is up,and IPv6 info + * is preferred,it will get IPv6 linklocal IP successfully + * + * @param[in] tcpip_if: the interface which we want to set IP information + * @param[in] if_ip6: If successful, IPv6 information will be returned in this argument. + * + * @return ESP_OK + * ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS + */ +esp_err_t tcpip_adapter_get_ip6_linklocal(tcpip_adapter_if_t tcpip_if, ip6_addr_t *if_ip6); + #if 0 esp_err_t tcpip_adapter_get_mac(tcpip_adapter_if_t tcpip_if, uint8_t *mac); diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index 3edc90509e..25c801bd01 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -23,7 +23,8 @@ #include "lwip/tcpip.h" #include "lwip/dhcp.h" #include "lwip/ip_addr.h" - +#include "lwip/ip6_addr.h" +#include "lwip/nd6.h" #include "netif/wlanif.h" #include "apps/dhcpserver.h" @@ -32,6 +33,7 @@ static struct netif *esp_netif[TCPIP_ADAPTER_IF_MAX]; static tcpip_adapter_ip_info_t esp_ip[TCPIP_ADAPTER_IF_MAX]; +static tcpip_adapter_ip6_info_t esp_ip6[TCPIP_ADAPTER_IF_MAX]; static tcpip_adapter_dhcp_status_t dhcps_status = TCPIP_ADAPTER_DHCP_INIT; static tcpip_adapter_dhcp_status_t dhcpc_status = TCPIP_ADAPTER_DHCP_INIT; @@ -234,6 +236,69 @@ esp_err_t tcpip_adapter_set_ip_info(tcpip_adapter_if_t tcpip_if, tcpip_adapter_i return ESP_OK; } +static void tcpip_adapter_nd6_cb(struct netif *p_netif, uint8_t ip_idex) +{ + tcpip_adapter_ip6_info_t *ip6_info; + + if (!p_netif) { + TCPIP_ADAPTER_DEBUG("null p_netif=%p\n", p_netif); + return; + } + + if (p_netif == esp_netif[TCPIP_ADAPTER_IF_STA]) { + ip6_info = &esp_ip6[TCPIP_ADAPTER_IF_STA]; + } else if(p_netif == esp_netif[TCPIP_ADAPTER_IF_AP]) { + ip6_info = &esp_ip6[TCPIP_ADAPTER_IF_AP]; + } else { + return; + } + + system_event_t evt; + + ip6_addr_set(&ip6_info->ip, ip_2_ip6(&p_netif->ip6_addr[ip_idex])); + + //notify event + evt.event_id = SYSTEM_EVENT_AP_STA_GOT_IP6; + memcpy(&evt.event_info.got_ip6.ip6_info, ip6_info, sizeof(tcpip_adapter_ip6_info_t)); + esp_event_send(&evt); +} + +esp_err_t tcpip_adapter_create_ip6_linklocal(tcpip_adapter_if_t tcpip_if) +{ + struct netif *p_netif; + + if (tcpip_if >= TCPIP_ADAPTER_IF_MAX) { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } + + p_netif = esp_netif[tcpip_if]; + if(p_netif != NULL && netif_is_up(p_netif)) { + netif_create_ip6_linklocal_address(p_netif, 1); + nd6_set_cb(p_netif, tcpip_adapter_nd6_cb); + + return ESP_OK; + } else { + return ESP_FAIL; + } +} + +esp_err_t tcpip_adapter_get_ip6_linklocal(tcpip_adapter_if_t tcpip_if, ip6_addr_t *if_ip6) +{ + struct netif *p_netif; + + if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || if_ip6 == NULL) { + return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; + } + + p_netif = esp_netif[tcpip_if]; + if (p_netif != NULL && netif_is_up(p_netif) && ip6_addr_ispreferred(netif_ip6_addr_state(p_netif, 0))) { + memcpy(if_ip6, &p_netif->ip6_addr[0], sizeof(ip6_addr_t)); + } else { + return ESP_FAIL; + } + return ESP_OK; +} + #if 0 esp_err_t tcpip_adapter_get_mac(tcpip_adapter_if_t tcpip_if, uint8_t mac[6]) { From c6016952e81b5f62ad651678b720700acd2a2339 Mon Sep 17 00:00:00 2001 From: Gabe Date: Sat, 19 Nov 2016 10:03:15 -0800 Subject: [PATCH 270/285] Pullup is not configured by gpio_config #110 --- components/driver/gpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index ddbcb352c0..d5d4d9d40a 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -321,9 +321,9 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) } if(pGPIOConfig->pull_up_en) { pu_en = 1; - REG_SET_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); + REG_SET_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pu); } else { - REG_CLR_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pd); + REG_CLR_BIT(gpio_pu_pd_desc[io_num].reg, gpio_pu_pd_desc[io_num].pu); } if(pGPIOConfig->pull_down_en) { pd_en = 1; From ed0612c56b30e8b9e4c2026293d3bb8fece1aed6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 21 Nov 2016 17:45:28 +1100 Subject: [PATCH 271/285] Build system: Fix a bug with embedding binaries in object files Sometimes paths were generated absolute, need to keep those as-is --- make/component_wrapper.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 3018c18b55..2f492bd183 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -204,7 +204,7 @@ embed_txt/$$(notdir $(1)): $(call resolvepath,$(1),$(COMPONENT_PATH)) | embed_tx # full path passed to OBJCOPY makes it into the name of the symbols in the .o file $(1).$(2).o: embed_$(2)/$$(notdir $(1)) | $$(dir $(1)) $(summary) EMBED $$@ - cd embed_$(2); $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) ../$$@ + cd embed_$(2); $(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$(call resolvepath,$$@,../) CLEAN_FILES += embed_$(2)/$$(notdir $(1)) endef From 1d4775558814b6673d59078cc37c8c65274b5d49 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 21 Nov 2016 18:08:22 +1100 Subject: [PATCH 272/285] mbedtls hardware bignum: Use memcpy instead of REG_WRITE/REG_READ in a loop Removes memory barriers for better performance, thanks Ivan for pointing this out. Manually unrolling the loop further seemed like diminishing returns. --- components/mbedtls/port/esp_bignum.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 68fd8bec98..7570820e3b 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -121,12 +121,16 @@ static inline size_t bits_to_hardware_words(size_t num_bits) */ static inline void mpi_to_mem_block(uint32_t mem_base, const mbedtls_mpi *mpi, size_t num_words) { - for(size_t i = 0; i < mpi->n && i < num_words; i++) { - REG_WRITE(mem_base + i * 4, mpi->p[i]); - } - for(size_t i = mpi->n; i < num_words; i++) { - REG_WRITE(mem_base + i * 4, 0); - } + uint32_t *pbase = (uint32_t *)mem_base; + uint32_t copy_words = num_words < mpi->n ? num_words : mpi->n; + + /* Copy MPI data to memory block registers */ + memcpy(pbase, mpi->p, copy_words * 4); + + /* Zero any remaining memory block data */ + bzero(pbase + copy_words, (num_words - copy_words) * 4); + + /* Note: not executing memw here, can do it before we start a bignum operation */ } /* Read mbedTLS MPI bignum back from hardware memory block. @@ -141,15 +145,16 @@ static inline int mem_block_to_mpi(mbedtls_mpi *x, uint32_t mem_base, int num_wo MBEDTLS_MPI_CHK( mbedtls_mpi_grow(x, num_words) ); - for(int i = 0; i < num_words; i++) { - x->p[i] = REG_READ(mem_base + i * 4); - } + /* Copy data from memory block registers */ + memcpy(x->p, (uint32_t *)mem_base, num_words * 4); + /* Zero any remaining limbs in the bignum, if the buffer is bigger than num_words */ for(size_t i = num_words; i < x->n; i++) { x->p[i] = 0; } + asm volatile ("memw"); cleanup: return ret; } @@ -218,6 +223,9 @@ static inline void execute_op(uint32_t op_reg) /* Clear interrupt status */ REG_WRITE(RSA_INTERRUPT_REG, 1); + /* Note: above REG_WRITE includes a memw, so we know any writes + to the memory blocks are also complete. */ + REG_WRITE(op_reg, 1); #ifdef CONFIG_MBEDTLS_MPI_USE_INTERRUPT From 586c17f831b0280b0e766b52399caab823ef0284 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 21 Nov 2016 16:08:39 +0800 Subject: [PATCH 273/285] lwip: remove useless printf info --- components/lwip/port/netif/wlanif.c | 1 - 1 file changed, 1 deletion(-) diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index 3a4688004b..f5d34179af 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -130,7 +130,6 @@ low_level_output(struct netif *netif, struct pbuf *p) ret = esp_wifi_internal_tx(wifi_if, q->payload, q->len); } else { LWIP_DEBUGF(PBUF_DEBUG, ("low_level_output: pbuf is a list, application may has bug")); - printf("low level_output: len=%d\n", p->tot_len); q = pbuf_alloc(PBUF_RAW_TX, p->tot_len, PBUF_RAM); if (q != NULL) { pbuf_copy(q, p); From 6a1dbc3f1c8db53a7985754844352fb7283f2290 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 10 Nov 2016 11:23:40 +0800 Subject: [PATCH 274/285] add RMT driver and example --- .../driver/include/driver/periph_ctrl.h | 1 + components/driver/include/driver/rmt.h | 785 ++++++++++++++++++ components/driver/periph_ctrl.c | 4 + components/driver/rmt.c | 716 ++++++++++++++++ components/esp32/include/esp_intr.h | 120 --- components/esp32/include/soc/rmt_reg.h | 3 +- components/esp32/include/soc/rmt_struct.h | 35 +- examples/11_rmt_nec_tx_rx/Makefile | 9 + examples/11_rmt_nec_tx_rx/main/component.mk | 4 + examples/11_rmt_nec_tx_rx/main/infrared_nec.c | 359 ++++++++ .../11_rmt_nec_tx_rx/main/infrared_nec_main.c | 23 + 11 files changed, 1929 insertions(+), 130 deletions(-) create mode 100644 components/driver/include/driver/rmt.h create mode 100644 components/driver/rmt.c create mode 100644 examples/11_rmt_nec_tx_rx/Makefile create mode 100644 examples/11_rmt_nec_tx_rx/main/component.mk create mode 100644 examples/11_rmt_nec_tx_rx/main/infrared_nec.c create mode 100644 examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c diff --git a/components/driver/include/driver/periph_ctrl.h b/components/driver/include/driver/periph_ctrl.h index 3faa347b54..d7a98284b7 100644 --- a/components/driver/include/driver/periph_ctrl.h +++ b/components/driver/include/driver/periph_ctrl.h @@ -39,6 +39,7 @@ typedef enum { PERIPH_PWM3_MODULE, PERIPH_UHCI0_MODULE, PERIPH_UHCI1_MODULE, + PERIPH_RMT_MODULE, } periph_module_t; /** diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h new file mode 100644 index 0000000000..d5d8d19b63 --- /dev/null +++ b/components/driver/include/driver/rmt.h @@ -0,0 +1,785 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _DRIVER_RMT_CTRL_H_ +#define _DRIVER_RMT_CTRL_H_ +#include "esp_err.h" +#include "soc/rmt_reg.h" +#include "soc/dport_reg.h" +#include "soc/rmt_struct.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/ringbuf.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RMT_MEM_BLOCK_BYTE_NUM (256) +#define RMT_MEM_ITEM_NUM (RMT_MEM_BLOCK_BYTE_NUM/4) + +typedef enum { + RMT_CHANNEL_0=0, /*!< RMT Channel0 */ + RMT_CHANNEL_1, /*!< RMT Channel1 */ + RMT_CHANNEL_2, /*!< RMT Channel2 */ + RMT_CHANNEL_3, /*!< RMT Channel3 */ + RMT_CHANNEL_4, /*!< RMT Channel4 */ + RMT_CHANNEL_5, /*!< RMT Channel5 */ + RMT_CHANNEL_6, /*!< RMT Channel6 */ + RMT_CHANNEL_7, /*!< RMT Channel7 */ + RMT_CHANNEL_MAX +} rmt_channel_t; + +typedef enum { + RMT_MEM_OWNER_TX = 0, /*!< RMT RX mode, RMT transmitter owns the memory block*/ + RMT_MEM_OWNER_RX = 1, /*!< RMT RX mode, RMT receiver owns the memory block*/ + RMT_MEM_OWNER_MAX, +}rmt_mem_owner_t; + +typedef enum { + RMT_BASECLK_REF = 0, /*!< RMT source clock system reference tick, 1MHz by default(Not supported in this version) */ + RMT_BASECLK_APB, /*!< RMT source clock is APB CLK, 80Mhz by default */ + RMT_BASECLK_MAX, +} rmt_source_clk_t; + +typedef enum { + RMT_DATA_MODE_FIFO = 0, /* +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/ringbuf.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "esp_err.h" +#include "soc/gpio_sig_map.h" +#include "soc/rmt_struct.h" +#include "driver/periph_ctrl.h" +#include "driver/rmt.h" + +#define RMT_SOUCCE_CLK_APB (APB_CLK_FREQ) /*!< RMT source clock is APB_CLK */ +#define RMT_SOURCE_CLK_REF (1 * 1000000) /*!< not used yet */ +#define RMT_SOURCE_CLK(select) ((select == RMT_BASECLK_REF) ? (RMT_SOURCE_CLK_REF) : (RMT_SOUCCE_CLK_APB)) /*! RMT source clock frequency */ + +#define RMT_CHANNEL_ERROR_STR "RMT CHANNEL ERR" +#define RMT_ADDR_ERROR_STR "RMT ADDRESS ERR" +#define RMT_MEM_CNT_ERROR_STR "RMT MEM BLOCK NUM ERR" +#define RMT_CARRIER_ERROR_STR "RMT CARRIER LEVEL ERR" +#define RMT_MEM_OWNER_ERROR_STR "RMT MEM OWNER_ERR" +#define RMT_BASECLK_ERROR_STR "RMT BASECLK ERR" +#define RMT_WR_MEM_OVF_ERROR_STR "RMT WR MEM OVERFLOW" +#define RMT_GPIO_ERROR_STR "RMT GPIO ERROR" +#define RMT_MODE_ERROR_STR "RMT MODE ERROR" +#define RMT_CLK_DIV_ERROR_STR "RMT CLK DIV ERR" +#define RMT_DRIVER_ERROR_STR "RMT DRIVER ERR" +#define RMT_DRIVER_LENGTH_ERROR_STR "RMT PARAM LEN ERROR" + +static const char* RMT_TAG = "RMT"; +static bool s_rmt_driver_installed = false; + +#define RMT_CHECK(a, str, ret) if (!(a)) { \ + ESP_LOGE(RMT_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } +static portMUX_TYPE rmt_spinlock = portMUX_INITIALIZER_UNLOCKED; + +typedef struct { + int tx_offset; + int tx_len_rem; + int tx_sub_len; + rmt_channel_t channel; + rmt_item32_t* tx_data; + xSemaphoreHandle tx_sem; + RingbufHandle_t tx_buf; + RingbufHandle_t rx_buf; +} rmt_obj_t; + +rmt_obj_t* p_rmt_obj[RMT_CHANNEL_MAX] = {0}; + +static void rmt_set_tx_wrap_en(rmt_channel_t channel, bool en) +{ + portENTER_CRITICAL(&rmt_spinlock); + RMT.apb_conf.mem_tx_wrap_en = en; + portEXIT_CRITICAL(&rmt_spinlock); +} + +static void rmt_set_data_mode(rmt_data_mode_t data_mode) +{ + portENTER_CRITICAL(&rmt_spinlock); + RMT.apb_conf.fifo_mask = data_mode; + portEXIT_CRITICAL(&rmt_spinlock); +} + +esp_err_t rmt_set_clk_div(rmt_channel_t channel, uint8_t div_cnt) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT.conf_ch[channel].conf0.div_cnt = div_cnt; + return ESP_OK; +} + +esp_err_t rmt_get_clk_div(rmt_channel_t channel, uint8_t* div_cnt) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(div_cnt != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + *div_cnt = RMT.conf_ch[channel].conf0.div_cnt; + return ESP_OK; +} + +esp_err_t rmt_set_rx_idle_thresh(rmt_channel_t channel, uint16_t thresh) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT.conf_ch[channel].conf0.idle_thres = thresh; + return ESP_OK; +} + +esp_err_t rmt_get_rx_idle_thresh(rmt_channel_t channel, uint16_t *thresh) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(thresh != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + *thresh = RMT.conf_ch[channel].conf0.idle_thres; + return ESP_OK; +} + +esp_err_t rmt_set_mem_block_num(rmt_channel_t channel, uint8_t rmt_mem_num) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(rmt_mem_num < 16, RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT.conf_ch[channel].conf0.mem_size = rmt_mem_num; + return ESP_OK; +} + +esp_err_t rmt_get_mem_block_num(rmt_channel_t channel, uint8_t* rmt_mem_num) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(rmt_mem_num != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + *rmt_mem_num = RMT.conf_ch[channel].conf0.mem_size; + return ESP_OK; +} + +esp_err_t rmt_set_tx_carrier(rmt_channel_t channel, bool carrier_en, uint16_t high_level, uint16_t low_level, + rmt_carrier_level_t carrier_level) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(carrier_level < RMT_CARRIER_LEVEL_MAX, RMT_CARRIER_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT.carrier_duty_ch[channel].high = high_level; + RMT.carrier_duty_ch[channel].low = low_level; + RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; + RMT.conf_ch[channel].conf0.carrier_en = carrier_en; + return ESP_OK; +} + +esp_err_t rmt_set_mem_pd(rmt_channel_t channel, bool pd_en) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT.conf_ch[channel].conf0.mem_pd = pd_en; + return ESP_OK; +} + +esp_err_t rmt_get_mem_pd(rmt_channel_t channel, bool* pd_en) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + *pd_en = (bool) RMT.conf_ch[channel].conf0.mem_pd; + return ESP_OK; +} + +esp_err_t rmt_tx_start(rmt_channel_t channel, bool tx_idx_rst) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + if(tx_idx_rst) { + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + } + RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX; + RMT.conf_ch[channel].conf1.tx_start = 1; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_tx_stop(rmt_channel_t channel) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.tx_start = 0; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_rx_start(rmt_channel_t channel, bool rx_idx_rst) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + if(rx_idx_rst) { + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + } + RMT.conf_ch[channel].conf1.rx_en = 0; + RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX; + RMT.conf_ch[channel].conf1.rx_en = 1; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_rx_stop(rmt_channel_t channel) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.rx_en = 0; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_memory_rw_rst(rmt_channel_t channel) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_set_memory_owner(rmt_channel_t channel, rmt_mem_owner_t owner) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(owner < RMT_MEM_OWNER_MAX, RMT_MEM_OWNER_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.mem_owner = owner; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_get_memory_owner(rmt_channel_t channel, rmt_mem_owner_t* owner) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(owner != NULL, RMT_MEM_OWNER_ERROR_STR, ESP_ERR_INVALID_ARG); + *owner = (rmt_mem_owner_t) RMT.conf_ch[channel].conf1.mem_owner; + return ESP_OK; +} + +esp_err_t rmt_set_tx_loop_mode(rmt_channel_t channel, bool loop_en) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.tx_conti_mode = loop_en; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_get_tx_loop_mode(rmt_channel_t channel, bool* loop_en) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + *loop_en = (bool) RMT.conf_ch[channel].conf1.tx_conti_mode; + return ESP_OK; +} + +esp_err_t rmt_set_rx_filter(rmt_channel_t channel, bool rx_filter_en, uint8_t thresh) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.rx_filter_en = rx_filter_en; + RMT.conf_ch[channel].conf1.rx_filter_thres = thresh; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_set_source_clk(rmt_channel_t channel, rmt_source_clk_t base_clk) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(base_clk < RMT_BASECLK_MAX, RMT_BASECLK_ERROR_STR, ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.ref_always_on = base_clk; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_get_source_clk(rmt_channel_t channel, rmt_source_clk_t* src_clk) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + *src_clk = (rmt_source_clk_t) (RMT.conf_ch[channel].conf1.ref_always_on); + return ESP_OK; +} + +esp_err_t rmt_set_idle_level(rmt_channel_t channel, bool idle_out_en, rmt_idle_level_t level) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(level < RMT_IDLE_LEVEL_MAX, "RMT IDLE LEVEL ERR", ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.idle_out_en = idle_out_en; + RMT.conf_ch[channel].conf1.idle_out_lv = level; + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +esp_err_t rmt_get_status(rmt_channel_t channel, uint32_t* status) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + *status = RMT.status_ch[channel]; + return ESP_OK; +} + +rmt_data_mode_t rmt_get_data_mode() +{ + return (rmt_data_mode_t) (RMT.apb_conf.fifo_mask); +} + +void rmt_set_intr_enable_mask(uint32_t mask) +{ + portENTER_CRITICAL(&rmt_spinlock); + RMT.int_ena.val |= mask; + portEXIT_CRITICAL(&rmt_spinlock); +} + +void rmt_clr_intr_enable_mask(uint32_t mask) +{ + portENTER_CRITICAL(&rmt_spinlock); + RMT.int_ena.val &= (~mask); + portEXIT_CRITICAL(&rmt_spinlock); +} + +esp_err_t rmt_set_rx_intr_en(rmt_channel_t channel, bool en) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + if(en) { + rmt_set_intr_enable_mask(BIT(channel * 3 + 1)); + } else { + rmt_clr_intr_enable_mask(BIT(channel * 3 + 1)); + } + return ESP_OK; +} + +esp_err_t rmt_set_err_intr_en(rmt_channel_t channel, bool en) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + if(en) { + rmt_set_intr_enable_mask(BIT(channel * 3 + 2)); + } else { + rmt_clr_intr_enable_mask(BIT(channel * 3 + 2)); + } + return ESP_OK; +} + +esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + if(en) { + rmt_set_intr_enable_mask(BIT(channel * 3)); + } else { + rmt_clr_intr_enable_mask(BIT(channel * 3)); + } + return ESP_OK; +} + +esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(evt_thresh < 256, "RMT EVT THRESH ERR", ESP_ERR_INVALID_ARG); + if(en) { + RMT.tx_lim_ch[channel].limit = evt_thresh; + rmt_set_tx_wrap_en(channel, true); + rmt_set_intr_enable_mask(BIT(channel + 24)); + } else { + rmt_clr_intr_enable_mask(BIT(channel + 24)); + } + return ESP_OK; +} + +esp_err_t rmt_set_pin(rmt_channel_t channel, rmt_mode_t mode, gpio_num_t gpio_num) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(mode < RMT_MODE_MAX, RMT_MODE_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(((GPIO_IS_VALID_GPIO(gpio_num) && (mode == RMT_MODE_RX)) || (GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) && (mode == RMT_MODE_TX))), + RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG); + + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], 2); + if(mode == RMT_MODE_TX) { + gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT); + gpio_matrix_out(gpio_num, RMT_SIG_OUT0_IDX + channel, 0, 0); + } else { + gpio_set_direction(gpio_num, GPIO_MODE_INPUT); + gpio_matrix_in(gpio_num, RMT_SIG_IN0_IDX + channel, 0); + } + return ESP_OK; +} + +esp_err_t rmt_config(rmt_config_t* rmt_param) +{ + uint8_t mode = rmt_param->rmt_mode; + uint8_t channel = rmt_param->channel; + uint8_t gpio_num = rmt_param->gpio_num; + uint8_t mem_cnt = rmt_param->mem_block_num; + int clk_div = rmt_param->clk_div; + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(GPIO_IS_VALID_GPIO(gpio_num), RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK((mem_cnt + channel <= 8 && mem_cnt > 0), RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK((clk_div > 0), RMT_CLK_DIV_ERROR_STR, ESP_ERR_INVALID_ARG); + periph_module_enable(PERIPH_RMT_MODULE); + + RMT.conf_ch[channel].conf0.div_cnt = clk_div; + /*Visit data use memory not FIFO*/ + rmt_set_data_mode(RMT_DATA_MODE_MEM); + /*Reset tx/rx memory index */ + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.mem_rd_rst = 1; + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + portEXIT_CRITICAL(&rmt_spinlock); + + if(mode == RMT_MODE_TX) { + uint32_t rmt_source_clk_hz = 0; + uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; + uint16_t carrier_duty_percent = rmt_param->tx_config.carrier_duty_percent; + uint8_t carrier_level = rmt_param->tx_config.carrier_level; + uint8_t idle_level = rmt_param->tx_config.idle_level; + + portENTER_CRITICAL(&rmt_spinlock); + RMT.conf_ch[channel].conf1.tx_conti_mode = rmt_param->tx_config.loop_en; + /*Memory set block number*/ + RMT.conf_ch[channel].conf0.mem_size = mem_cnt; + RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX; + /*We use APB clock in this version, which is 80Mhz, later we will release system reference clock*/ + RMT.conf_ch[channel].conf1.ref_always_on = RMT_BASECLK_APB; + rmt_source_clk_hz = RMT_SOURCE_CLK(RMT_BASECLK_APB); + /*Set idle level */ + RMT.conf_ch[channel].conf1.idle_out_en = rmt_param->tx_config.idle_output_en; + RMT.conf_ch[channel].conf1.idle_out_lv = idle_level; + portEXIT_CRITICAL(&rmt_spinlock); + + /*Set carrier*/ + uint32_t duty_div, duty_h, duty_l; + duty_div = rmt_source_clk_hz / carrier_freq_hz; + duty_h = duty_div * carrier_duty_percent / 100; + duty_l = duty_div - duty_h; + RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; + RMT.carrier_duty_ch[channel].high = duty_h; + RMT.carrier_duty_ch[channel].low = duty_l; + RMT.conf_ch[channel].conf0.carrier_en = rmt_param->tx_config.carrier_en; + ESP_LOGD(RMT_TAG, "Rmt Tx Channel %u|Gpio %u|Sclk_Hz %u|Div %u|Carrier_Hz %u|Duty %u", + channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent); + } + else if(RMT_MODE_RX == mode) { + uint8_t filter_cnt = rmt_param->rx_config.filter_ticks_thresh; + uint16_t threshold = rmt_param->rx_config.idle_threshold; + + portENTER_CRITICAL(&rmt_spinlock); + /*clock init*/ + RMT.conf_ch[channel].conf1.ref_always_on = RMT_BASECLK_APB; + uint32_t rmt_source_clk_hz = RMT_SOURCE_CLK(RMT_BASECLK_APB); + /*memory set block number and owner*/ + RMT.conf_ch[channel].conf0.mem_size = mem_cnt; + RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX; + /*Set idle threshold*/ + RMT.conf_ch[channel].conf0.idle_thres = threshold; + /* Set RX filter */ + RMT.conf_ch[channel].conf1.rx_filter_thres = filter_cnt; + RMT.conf_ch[channel].conf1.rx_filter_en = rmt_param->rx_config.filter_en; + portEXIT_CRITICAL(&rmt_spinlock); + + ESP_LOGD(RMT_TAG, "Rmt Rx Channel %u|Gpio %u|Sclk_Hz %u|Div %u|Thresold %u|Filter %u", + channel, gpio_num, rmt_source_clk_hz, clk_div, threshold, filter_cnt); + } + rmt_set_pin(channel, mode, gpio_num); + return ESP_OK; +} + +static void IRAM_ATTR rmt_fill_memory(rmt_channel_t channel, rmt_item32_t* item, uint16_t item_num, uint16_t mem_offset) +{ + portENTER_CRITICAL(&rmt_spinlock); + RMT.apb_conf.fifo_mask = RMT_DATA_MODE_MEM; + portEXIT_CRITICAL(&rmt_spinlock); + int i; + for(i = 0; i < item_num; i++) { + RMTMEM.chan[channel].data32[i + mem_offset].val = item[i].val; + } +} + +esp_err_t rmt_fill_tx_items(rmt_channel_t channel, rmt_item32_t* item, uint16_t item_num, uint16_t mem_offset) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, (0)); + RMT_CHECK((item != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK((item_num > 0), RMT_DRIVER_LENGTH_ERROR_STR, ESP_ERR_INVALID_ARG); + + /*Each block has 64 x 32 bits of data*/ + uint8_t mem_cnt = RMT.conf_ch[channel].conf0.mem_size; + RMT_CHECK((mem_cnt * RMT_MEM_ITEM_NUM >= item_num), RMT_WR_MEM_OVF_ERROR_STR, ESP_ERR_INVALID_ARG); + rmt_fill_memory(channel, item, item_num, mem_offset); + return ESP_OK; +} + +esp_err_t rmt_isr_register(uint8_t rmt_intr_num, void (*fn)(void*), void * arg) +{ + RMT_CHECK((fn != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(s_rmt_driver_installed == false, "RMT DRIVER INSTALLED, CAN NOT REG ISR HANDLER", ESP_FAIL); + portENTER_CRITICAL(&rmt_spinlock); + ESP_INTR_DISABLE(rmt_intr_num); + intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, rmt_intr_num); + xt_set_interrupt_handler(rmt_intr_num, fn, arg); + ESP_INTR_ENABLE(rmt_intr_num); + portEXIT_CRITICAL(&rmt_spinlock); + return ESP_OK; +} + +static int IRAM_ATTR rmt_get_mem_len(rmt_channel_t channel) +{ + int block_num = RMT.conf_ch[channel].conf0.mem_size; + int item_block_len = block_num * RMT_MEM_ITEM_NUM; + volatile rmt_item32_t* data = RMTMEM.chan[channel].data32; + int idx; + for(idx = 0; idx < item_block_len; idx++) { + if(data[idx].duration0 == 0) { + return idx; + } else if(data[idx].duration1 == 0) { + return idx + 1; + } + } + return idx; +} + +static void IRAM_ATTR rmt_driver_isr_default(void* arg) +{ + uint32_t intr_st = RMT.int_st.val; + uint32_t i = 0; + uint8_t channel; + portBASE_TYPE HPTaskAwoken = 0; + for(i = 0; i < 32; i++) { + if(i < 24) { + if(intr_st & (BIT(i))) { + channel = i / 3; + rmt_obj_t* p_rmt = p_rmt_obj[channel]; + switch(i % 3) { + //TX END + case 0: + ESP_EARLY_LOGD(RMT_TAG, "RMT INTR : TX END\n"); + xSemaphoreGiveFromISR(p_rmt->tx_sem, &HPTaskAwoken); + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + p_rmt->tx_data = NULL; + p_rmt->tx_len_rem = 0; + p_rmt->tx_offset = 0; + p_rmt->tx_sub_len = 0; + break; + //RX_END + case 1: + ESP_EARLY_LOGD(RMT_TAG, "RMT INTR : RX END"); + RMT.conf_ch[channel].conf1.rx_en = 0; + int item_len = rmt_get_mem_len(channel); + //change memory owner to protect data. + RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_TX; + if(p_rmt->rx_buf) { + BaseType_t res = xRingbufferSendFromISR(p_rmt->rx_buf, (void*) RMTMEM.chan[channel].data32, item_len * 4, &HPTaskAwoken); + if(res == pdFALSE) { + ESP_LOGE(RMT_TAG, "RMT RX BUFFER FULL"); + } else { + + } + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + } else { + ESP_EARLY_LOGE(RMT_TAG, "RMT RX BUFFER ERROR\n"); + } + RMT.conf_ch[channel].conf1.mem_wr_rst = 1; + RMT.conf_ch[channel].conf1.mem_owner = RMT_MEM_OWNER_RX; + RMT.conf_ch[channel].conf1.rx_en = 1; + break; + //ERR + case 2: + ESP_EARLY_LOGE(RMT_TAG, "RMT[%d] ERR", channel); + ESP_EARLY_LOGE(RMT_TAG, "status: 0x%08x", RMT.status_ch[channel]); + RMT.int_ena.val &= (~(BIT(i))); + break; + default: + break; + } + RMT.int_clr.val = BIT(i); + } + } else { + if(intr_st & (BIT(i))) { + channel = i - 24; + rmt_obj_t* p_rmt = p_rmt_obj[channel]; + RMT.int_clr.val = BIT(i); + ESP_EARLY_LOGD(RMT_TAG, "RMT CH[%d]: EVT INTR", channel); + if(p_rmt->tx_data == NULL) { + //skip + } else { + rmt_item32_t* pdata = p_rmt->tx_data; + int len_rem = p_rmt->tx_len_rem; + if(len_rem >= p_rmt->tx_sub_len) { + rmt_fill_memory(channel, pdata, p_rmt->tx_sub_len, p_rmt->tx_offset); + p_rmt->tx_data += p_rmt->tx_sub_len; + p_rmt->tx_len_rem -= p_rmt->tx_sub_len; + } else if(len_rem == 0) { + RMTMEM.chan[channel].data32[p_rmt->tx_offset].val = 0; + } else { + rmt_fill_memory(channel, pdata, len_rem, p_rmt->tx_offset); + RMTMEM.chan[channel].data32[p_rmt->tx_offset + len_rem].val = 0; + p_rmt->tx_data += len_rem; + p_rmt->tx_len_rem -= len_rem; + } + if(p_rmt->tx_offset == 0) { + p_rmt->tx_offset = p_rmt->tx_sub_len; + } else { + p_rmt->tx_offset = 0; + } + } + } + } + } +} + +esp_err_t rmt_driver_uninstall(rmt_channel_t channel) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + if(p_rmt_obj[channel] == NULL) { + return ESP_OK; + } + xSemaphoreTake(p_rmt_obj[channel]->tx_sem, portMAX_DELAY); + rmt_set_rx_intr_en(channel, 0); + rmt_set_err_intr_en(channel, 0); + rmt_set_tx_intr_en(channel, 0); + rmt_set_evt_intr_en(channel, 0, 0xffff); + if(p_rmt_obj[channel]->tx_sem) { + vSemaphoreDelete(p_rmt_obj[channel]->tx_sem); + p_rmt_obj[channel]->tx_sem = NULL; + } + if(p_rmt_obj[channel]->rx_buf) { + vRingbufferDelete(p_rmt_obj[channel]->rx_buf); + p_rmt_obj[channel]->rx_buf = NULL; + } + free(p_rmt_obj[channel]); + p_rmt_obj[channel] = NULL; + s_rmt_driver_installed = false; + return ESP_OK; +} + +esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + if(p_rmt_obj[channel] != NULL) { + ESP_LOGD(RMT_TAG, "RMT DRIVER ALREADY INSTALLED"); + return ESP_FAIL; + } + + ESP_INTR_DISABLE(rmt_intr_num); + p_rmt_obj[channel] = (rmt_obj_t*) malloc(sizeof(rmt_obj_t)); + + if(p_rmt_obj[channel] == NULL) { + ESP_LOGE(RMT_TAG, "RMT driver malloc error"); + return ESP_FAIL; + } + memset(p_rmt_obj[channel], 0, sizeof(rmt_obj_t)); + + p_rmt_obj[channel]->tx_len_rem = 0; + p_rmt_obj[channel]->tx_data = NULL; + p_rmt_obj[channel]->channel = channel; + p_rmt_obj[channel]->tx_offset = 0; + p_rmt_obj[channel]->tx_sub_len = 0; + + if(p_rmt_obj[channel]->tx_sem == NULL) { + p_rmt_obj[channel]->tx_sem = xSemaphoreCreateBinary(); + xSemaphoreGive(p_rmt_obj[channel]->tx_sem); + } + if(p_rmt_obj[channel]->rx_buf == NULL && rx_buf_size > 0) { + p_rmt_obj[channel]->rx_buf = xRingbufferCreate(rx_buf_size, RINGBUF_TYPE_NOSPLIT); + rmt_set_rx_intr_en(channel, 1); + rmt_set_err_intr_en(channel, 1); + } + if(s_rmt_driver_installed == false) { + rmt_isr_register(rmt_intr_num, rmt_driver_isr_default, NULL); + s_rmt_driver_installed = true; + } + rmt_set_tx_intr_en(channel, 1); + ESP_INTR_ENABLE(rmt_intr_num); + return ESP_OK; +} + +esp_err_t rmt_write_items(rmt_channel_t channel, rmt_item32_t* rmt_item, int item_num, bool wait_tx_done) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL); + RMT_CHECK(rmt_item != NULL, RMT_ADDR_ERROR_STR, ESP_FAIL); + RMT_CHECK(item_num > 0, RMT_DRIVER_LENGTH_ERROR_STR, ESP_ERR_INVALID_ARG); + rmt_obj_t* p_rmt = p_rmt_obj[channel]; + int block_num = RMT.conf_ch[channel].conf0.mem_size; + int item_block_len = block_num * RMT_MEM_ITEM_NUM; + int item_sub_len = block_num * RMT_MEM_ITEM_NUM / 2; + int len_rem = item_num; + xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY); + // fill the memory block first + if(item_num >= item_block_len) { + rmt_fill_memory(channel, rmt_item, item_block_len, 0); + RMT.tx_lim_ch[channel].limit = item_sub_len; + RMT.apb_conf.mem_tx_wrap_en = 1; + len_rem -= item_block_len; + RMT.conf_ch[channel].conf1.tx_conti_mode = 0; + rmt_set_evt_intr_en(channel, 1, item_sub_len); + p_rmt->tx_data = rmt_item + item_block_len; + p_rmt->tx_len_rem = len_rem; + p_rmt->tx_offset = 0; + p_rmt->tx_sub_len = item_sub_len; + } else { + rmt_fill_memory(channel, rmt_item, len_rem, 0); + RMTMEM.chan[channel].data32[len_rem].val = 0; + len_rem = 0; + } + rmt_tx_start(channel, true); + if(wait_tx_done) { + xSemaphoreTake(p_rmt->tx_sem, portMAX_DELAY); + xSemaphoreGive(p_rmt->tx_sem); + } + return ESP_OK; +} + +esp_err_t rmt_wait_tx_done(rmt_channel_t channel) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL); + xSemaphoreTake(p_rmt_obj[channel]->tx_sem, portMAX_DELAY); + xSemaphoreGive(p_rmt_obj[channel]->tx_sem); + return ESP_OK; +} + +esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_handler) +{ + RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); + RMT_CHECK(p_rmt_obj[channel] != NULL, RMT_DRIVER_ERROR_STR, ESP_FAIL); + RMT_CHECK(buf_handler != NULL, RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + *buf_handler = p_rmt_obj[channel]->rx_buf; + return ESP_OK; +} + diff --git a/components/esp32/include/esp_intr.h b/components/esp32/include/esp_intr.h index e138133f64..579eb6353d 100644 --- a/components/esp32/include/esp_intr.h +++ b/components/esp32/include/esp_intr.h @@ -82,126 +82,6 @@ extern "C" { #define ESP_INTR_DISABLE(inum) \ xt_ints_off((1< +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "esp_err.h" +#include "esp_log.h" +#include "driver/rmt.h" +#include "driver/periph_ctrl.h" +#include "soc/rmt_reg.h" + +static const char* NEC_TAG = "NEC"; + +//CHOOSE SELF TEST OR NORMAL TEST +#define RMT_RX_SELF_TEST 1 + +/******************************************************/ +/***** SELF TEST: *****/ +/*Connect RMT_TX_GPIO_NUM with RMT_RX_GPIO_NUM */ +/*TX task will send NEC data with carrier disabled */ +/*RX task will print NEC data it receives. */ +/******************************************************/ +#if RMT_RX_SELF_TEST +#define RMT_RX_ACTIVE_LEVEL 1 /*!< Data bit is active high for self test mode */ +#define RMT_TX_CARRIER_EN 0 /*!< Disable carrier for self test mode */ +#else +//Test with infrared LED, we have to enable carrier for transmitter +//When testing via IR led, the receiver waveform is usually active-low. +#define RMT_RX_ACTIVE_LEVEL 0 /*!< If we connect with a IR receiver, the data is active low */ +#define RMT_TX_CARRIER_EN 1 /*!< Enable carrier for IR transmitter test with IR led */ +#endif + +#define RMT_TX_CHANNEL 1 /*!< RMT channel for transmitter */ +#define RMT_TX_GPIO_NUM 16 /*!< GPIO number for transmitter signal */ +#define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */ +#define RMT_RX_GPIO_NUM 19 /*!< GPIO number for receiver */ +#define RMT_INTR_NUM 19 /*!< RMT interrupt number, select from soc.h */ +#define RMT_CLK_DIV 100 /*!< RMT counter clock divider */ +#define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */ + +#define NEC_HEADER_HIGH_US 9000 /*!< NEC protocol header: positive 9ms */ +#define NEC_HEADER_LOW_US 4500 /*!< NEC protocol header: negative 4.5ms*/ +#define NEC_BIT_ONE_HIGH_US 560 /*!< NEC protocol data bit 1: positive 0.56ms */ +#define NEC_BIT_ONE_LOW_US (2250-NEC_BIT_ONE_HIGH_US) /*!< NEC protocol data bit 1: negative 1.69ms */ +#define NEC_BIT_ZERO_HIGH_US 560 /*!< NEC protocol data bit 0: positive 0.56ms */ +#define NEC_BIT_ZERO_LOW_US (1120-NEC_BIT_ZERO_HIGH_US) /*!< NEC protocol data bit 0: negative 0.56ms */ +#define NEC_BIT_END 560 /*!< NEC protocol end: positive 0.56ms */ +#define NEC_BIT_MARGIN 20 /*!< NEC parse margin time */ + +#define NEC_ITEM_DURATION(d) ((d & 0x7fff)*10/RMT_TICK_10_US) /*!< Parse duration time from memory register value */ +#define NEC_DATA_ITEM_NUM 34 /*!< NEC code item number: header + 32bit data + end */ +#define RMT_TX_DATA_NUM 100 /*!< NEC tx test data number */ +#define rmt_item32_tIMEOUT_US 9500 /*!< RMT receiver timeout value(us) */ + +/* + * @brief Build register value of waveform for NEC one data bit + */ +inline void nec_fill_item_level(rmt_item32_t* item, int high_us, int low_us) +{ + item->level0 = 1; + item->duration0 = (high_us) / 10 * RMT_TICK_10_US; + item->level1 = 0; + item->duration1 = (low_us) / 10 * RMT_TICK_10_US; +} + +/* + * @brief Generate NEC header value: active 9ms + negative 4.5ms + */ +static void nec_fill_item_header(rmt_item32_t* item) +{ + nec_fill_item_level(item, NEC_HEADER_HIGH_US, NEC_HEADER_LOW_US); +} + +/* + * @brief Generate NEC data bit 1: positive 0.56ms + negative 1.69ms + */ +static void nec_fill_item_bit_one(rmt_item32_t* item) +{ + nec_fill_item_level(item, NEC_BIT_ONE_HIGH_US, NEC_BIT_ONE_LOW_US); +} + +/* + * @brief Generate NEC data bit 0: positive 0.56ms + negative 0.56ms + */ +static void nec_fill_item_bit_zero(rmt_item32_t* item) +{ + nec_fill_item_level(item, NEC_BIT_ZERO_HIGH_US, NEC_BIT_ZERO_LOW_US); +} + +/* + * @brief Generate NEC end signal: positive 0.56ms + */ +static void nec_fill_item_end(rmt_item32_t* item) +{ + nec_fill_item_level(item, NEC_BIT_END, 0x7fff); +} + +/* + * @brief Check whether duration is around target_us + */ +inline bool nec_check_in_range(int duration_ticks, int target_us, int margin_us) +{ + if(( NEC_ITEM_DURATION(duration_ticks) < (target_us + margin_us)) + && ( NEC_ITEM_DURATION(duration_ticks) > (target_us - margin_us))) { + return true; + } else { + return false; + } +} + +/* + * @brief Check whether this value represents an NEC header + */ +static bool nec_header_if(rmt_item32_t* item) +{ + if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) + && nec_check_in_range(item->duration0, NEC_HEADER_HIGH_US, NEC_BIT_MARGIN) + && nec_check_in_range(item->duration1, NEC_HEADER_LOW_US, NEC_BIT_MARGIN)) { + return true; + } + return false; +} + +/* + * @brief Check whether this value represents an NEC data bit 1 + */ +static bool nec_bit_one_if(rmt_item32_t* item) +{ + if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) + && nec_check_in_range(item->duration0, NEC_BIT_ONE_HIGH_US, NEC_BIT_MARGIN) + && nec_check_in_range(item->duration1, NEC_BIT_ONE_LOW_US, NEC_BIT_MARGIN)) { + return true; + } + return false; +} + +/* + * @brief Check whether this value represents an NEC data bit 0 + */ +static bool nec_bit_zero_if(rmt_item32_t* item) +{ + if((item->level0 == RMT_RX_ACTIVE_LEVEL && item->level1 != RMT_RX_ACTIVE_LEVEL) + && nec_check_in_range(item->duration0, NEC_BIT_ZERO_HIGH_US, NEC_BIT_MARGIN) + && nec_check_in_range(item->duration1, NEC_BIT_ZERO_LOW_US, NEC_BIT_MARGIN)) { + return true; + } + return false; +} + + +/* + * @brief Parse NEC 32 bit waveform to address and command. + */ +static int nec_parse_items(rmt_item32_t* item, int item_num, uint16_t* addr, uint16_t* data) +{ + int w_len = item_num; + if(w_len < NEC_DATA_ITEM_NUM) { + return -1; + } + int i = 0, j = 0; + if(!nec_header_if(item++)) { + return -1; + } + uint16_t addr_t = 0; + for(j = 0; j < 16; j++) { + if(nec_bit_one_if(item)) { + addr_t |= (1 << j); + } else if(nec_bit_zero_if(item)) { + addr_t |= (0 << j); + } else { + return -1; + } + item++; + i++; + } + uint16_t data_t = 0; + for(j = 0; j < 16; j++) { + if(nec_bit_one_if(item)) { + data_t |= (1 << j); + } else if(nec_bit_zero_if(item)) { + data_t |= (0 << j); + } else { + return -1; + } + item++; + i++; + } + *addr = addr_t; + *data = data_t; + return i; +} + +/* + * @brief Build NEC 32bit waveform. + */ +static int nec_build_items(int channel, rmt_item32_t* item, int item_num, uint16_t addr, uint16_t cmd_data) +{ + int i = 0, j = 0; + if(item_num < NEC_DATA_ITEM_NUM) { + return -1; + } + nec_fill_item_header(item++); + i++; + for(j = 0; j < 16; j++) { + if(addr & 0x1) { + nec_fill_item_bit_one(item); + } else { + nec_fill_item_bit_zero(item); + } + item++; + i++; + addr >>= 1; + } + for(j = 0; j < 16; j++) { + if(cmd_data & 0x1) { + nec_fill_item_bit_one(item); + } else { + nec_fill_item_bit_zero(item); + } + item++; + i++; + cmd_data >>= 1; + } + nec_fill_item_end(item); + i++; + return i; +} + +/* + * @brief RMT transmitter initialization + */ +static void rmt_tx_init() +{ + rmt_config_t rmt_tx; + rmt_tx.channel = RMT_TX_CHANNEL; + rmt_tx.gpio_num = RMT_TX_GPIO_NUM; + rmt_tx.mem_block_num = 1; + rmt_tx.clk_div = RMT_CLK_DIV; + rmt_tx.tx_config.loop_en = false; + rmt_tx.tx_config.carrier_duty_percent = 50; + rmt_tx.tx_config.carrier_freq_hz = 38000; + rmt_tx.tx_config.carrier_level = 1; + rmt_tx.tx_config.carrier_en = RMT_TX_CARRIER_EN; + rmt_tx.tx_config.idle_level = 0; + rmt_tx.tx_config.idle_output_en = true; + rmt_tx.rmt_mode = 0; + rmt_config(&rmt_tx); + rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM); +} + +/* + * @brief RMT receiver initialization + */ +void rmt_rx_init() +{ + rmt_config_t rmt_rx; + rmt_rx.channel = RMT_RX_CHANNEL; + rmt_rx.gpio_num = RMT_RX_GPIO_NUM; + rmt_rx.clk_div = RMT_CLK_DIV; + rmt_rx.mem_block_num = 1; + rmt_rx.rmt_mode = RMT_MODE_RX; + rmt_rx.rx_config.filter_en = true; + rmt_rx.rx_config.filter_ticks_thresh = 100; + rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US); + rmt_config(&rmt_rx); + rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM); +} + +/** + * @brief RMT receiver demo, this task will print each received NEC data. + * + */ +void rmt_nec_rx_task() +{ + int channel = RMT_RX_CHANNEL; + rmt_rx_init(); + RingbufHandle_t rb = NULL; + //get RMT RX ringbuffer + rmt_get_ringbuf_handler(channel, &rb); + rmt_rx_start(channel, 1); + while(rb) { + size_t rx_size = 0; + //try to receive data from ringbuffer. + //RMT driver will push all the data it receives to its ringbuffer. + //We just need to parse the value and return the spaces of ringbuffer. + rmt_item32_t* item = (rmt_item32_t*) xRingbufferReceive(rb, &rx_size, 1000); + if(item) { + uint16_t rmt_addr; + uint16_t rmt_cmd; + int offset = 0; + while(1) { + //parse data value from ringbuffer. + int res = nec_parse_items(item + offset, rx_size / 4 - offset, &rmt_addr, &rmt_cmd); + if(res > 0) { + offset += res + 1; + ESP_LOGI(NEC_TAG, "RMT RCV --- addr: 0x%04x cmd: 0x%04x", rmt_addr, rmt_cmd); + } else { + break; + } + } + //after parsing the data, return spaces to ringbuffer. + vRingbufferReturnItem(rb, (void*) item); + } else { + break; + } + } + vTaskDelete(NULL); +} + +/** + * @brief RMT transmitter demo, this task will periodically send NEC data. (100 * 32 bits each time.) + * + */ +void rmt_nec_tx_task() +{ + vTaskDelay(10); + rmt_tx_init(); + esp_log_level_set(NEC_TAG, ESP_LOG_INFO); + int channel = RMT_TX_CHANNEL; + uint16_t cmd = 0x0; + uint16_t addr = 0x11; + int nec_tx_num = RMT_TX_DATA_NUM; + for(;;) { + ESP_LOGI(NEC_TAG, "RMT TX DATA"); + size_t size = (sizeof(rmt_item32_t) * NEC_DATA_ITEM_NUM * nec_tx_num); + //each item represent a cycle of waveform. + rmt_item32_t* item = (rmt_item32_t*) malloc(size); + int item_num = NEC_DATA_ITEM_NUM * nec_tx_num; + memset((void*) item, 0, size); + int i, offset = 0; + while(1) { + //To build a series of waveforms. + i = nec_build_items(channel, item + offset, item_num - offset, ((~addr) << 8) | addr, cmd); + if(i < 0) { + break; + } + cmd++; + addr++; + offset += i; + } + //To send data according to the waveform items. + rmt_write_items(channel, item, item_num, true); + //Wait until sending is done. + rmt_wait_tx_done(channel); + //before we free the data, make sure sending is already done. + free(item); + vTaskDelay(2000 / portTICK_RATE_MS); + } + vTaskDelete(NULL); +} diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c new file mode 100644 index 0000000000..c680f25a5c --- /dev/null +++ b/examples/11_rmt_nec_tx_rx/main/infrared_nec_main.c @@ -0,0 +1,23 @@ +/* NEC remote infrared RMT example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "driver/rmt.h" +#include "driver/periph_ctrl.h" +extern void rmt_nec_tx_task(); +extern void rmt_nec_rx_task(); + +void app_main() +{ + xTaskCreate(rmt_nec_rx_task, "rmt_nec_rx_task", 2048, NULL, 10, NULL); + xTaskCreate(rmt_nec_tx_task, "rmt_nec_tx_task", 2048, NULL, 10, NULL); +} From 235eceea068f11ee31217cb4e15203e7a1c9faae Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 22 Nov 2016 01:31:56 +0800 Subject: [PATCH 275/285] rmt: add documentation --- components/driver/include/driver/rmt.h | 9 +++ docs/api/rmt.rst | 93 ++++++++++++++++++++++++++ docs/doxygen_xml_to_rst.xslt | 2 + docs/index.rst | 1 + 4 files changed, 105 insertions(+) create mode 100644 docs/api/rmt.rst diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index d5d8d19b63..50a5c743dd 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -80,6 +80,9 @@ typedef enum { RMT_CARRIER_LEVEL_MAX } rmt_carrier_level_t; +/** + * @brief Data struct of RMT TX configure parameters + */ typedef struct { bool loop_en; /*!< RMT loop output mode*/ uint32_t carrier_freq_hz; /*!< RMT carrier frequency */ @@ -90,12 +93,18 @@ typedef struct { bool idle_output_en; /*!< RMT idle level output enable*/ }rmt_tx_config_t; +/** + * @brief Data struct of RMT RX configure parameters + */ typedef struct { bool filter_en; /*!< RMT receiver filer enable*/ uint8_t filter_ticks_thresh; /*!< RMT filter tick number */ uint16_t idle_threshold; /*!< RMT RX idle threshold */ }rmt_rx_config_t; +/** + * @brief Data struct of RMT configure parameters + */ typedef struct { rmt_mode_t rmt_mode; /*!< RMT mode: transmitter or receiver */ rmt_channel_t channel; /*!< RMT channel */ diff --git a/docs/api/rmt.rst b/docs/api/rmt.rst new file mode 100644 index 0000000000..9d834b1e28 --- /dev/null +++ b/docs/api/rmt.rst @@ -0,0 +1,93 @@ +RMT +======== + +Overview +-------- + +The RMT (Remote Control) module driver can be used to send and receive infrared remote control signals. Due to flexibility of RMT module, the driver can also be used to generate many other types of signals. + +Application Example +------------------- + +NEC remote control TX and RX example: `examples/11_rmt_nec_tx_rx `_. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `driver/rmt.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: RMT_MEM_BLOCK_BYTE_NUM +.. doxygendefine:: RMT_MEM_ITEM_NUM + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: rmt_channel_t +.. doxygenenum:: rmt_mem_owner_t +.. doxygenenum:: rmt_source_clk_t +.. doxygenenum:: rmt_data_mode_t +.. doxygenenum:: rmt_mode_t +.. doxygenenum:: rmt_idle_level_t +.. doxygenenum:: rmt_carrier_level_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: rmt_tx_config_t + :members: + +.. doxygenstruct:: rmt_rx_config_t + :members: + +.. doxygenstruct:: rmt_config_t + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: rmt_set_clk_div +.. doxygenfunction:: rmt_get_clk_div +.. doxygenfunction:: rmt_set_rx_idle_thresh +.. doxygenfunction:: rmt_get_rx_idle_thresh +.. doxygenfunction:: rmt_set_mem_block_num +.. doxygenfunction:: rmt_get_mem_block_num +.. doxygenfunction:: rmt_set_tx_carrier +.. doxygenfunction:: rmt_set_mem_pd +.. doxygenfunction:: rmt_get_mem_pd +.. doxygenfunction:: rmt_tx_start +.. doxygenfunction:: rmt_tx_stop +.. doxygenfunction:: rmt_rx_start +.. doxygenfunction:: rmt_rx_stop +.. doxygenfunction:: rmt_memory_rw_rst +.. doxygenfunction:: rmt_set_memory_owner +.. doxygenfunction:: rmt_get_memory_owner +.. doxygenfunction:: rmt_set_tx_loop_mode +.. doxygenfunction:: rmt_get_tx_loop_mode +.. doxygenfunction:: rmt_set_rx_filter +.. doxygenfunction:: rmt_set_source_clk +.. doxygenfunction:: rmt_get_source_clk +.. doxygenfunction:: rmt_set_idle_level +.. doxygenfunction:: rmt_get_status +.. doxygenfunction:: rmt_set_intr_enable_mask +.. doxygenfunction:: rmt_clr_intr_enable_mask +.. doxygenfunction:: rmt_set_rx_intr_en +.. doxygenfunction:: rmt_set_err_intr_en +.. doxygenfunction:: rmt_set_tx_intr_en +.. doxygenfunction:: rmt_set_evt_intr_en +.. doxygenfunction:: rmt_set_pin +.. doxygenfunction:: rmt_config +.. doxygenfunction:: rmt_isr_register +.. doxygenfunction:: rmt_fill_tx_items +.. doxygenfunction:: rmt_driver_install +.. doxygenfunction:: rmt_driver_uninstall +.. doxygenfunction:: rmt_write_items +.. doxygenfunction:: rmt_wait_tx_done +.. doxygenfunction:: rmt_get_ringbuf_handler + diff --git a/docs/doxygen_xml_to_rst.xslt b/docs/doxygen_xml_to_rst.xslt index c4570a9df6..0212457e57 100644 --- a/docs/doxygen_xml_to_rst.xslt +++ b/docs/doxygen_xml_to_rst.xslt @@ -40,6 +40,8 @@ .. doxygenstruct:: + :members: + diff --git a/docs/index.rst b/docs/index.rst index 8bc7a72f29..7cb641ee0d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -98,6 +98,7 @@ Contents: api/gpio api/uart api/ledc + api/rmt SPI Flash and Partition APIs Logging Non-Volatile Storage From cc39b7b286e242ed024c55c2574258b501a448a2 Mon Sep 17 00:00:00 2001 From: tianzhongxing Date: Mon, 21 Nov 2016 14:09:52 +0800 Subject: [PATCH 276/285] components/lwip/arp: change the arp dropping packet function LWIP will drop the oldest package at the arp packet cache queue and return "ERR_OK" We change it that ARP level will drop the current packet and return "ERR_MEM" to tell user that you should send the packet again latter. --- components/lwip/include/lwip/port/lwipopts.h | 1 + components/lwip/netif/etharp.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 26bdc3a4e9..487aff323a 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -515,6 +515,7 @@ extern unsigned long os_random(void); /* Enable all Espressif-only options */ #define ESP_LWIP 1 +#define ESP_LWIP_ARP 1 #define ESP_PER_SOC_TCP_WND 1 #define ESP_THREAD_SAFE 1 #define ESP_THREAD_SAFE_DEBUG LWIP_DBG_OFF diff --git a/components/lwip/netif/etharp.c b/components/lwip/netif/etharp.c index 776e949f75..b51a202222 100755 --- a/components/lwip/netif/etharp.c +++ b/components/lwip/netif/etharp.c @@ -1192,11 +1192,28 @@ etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q) } #if ARP_QUEUE_LEN if (qlen >= ARP_QUEUE_LEN) { +#if ESP_LWIP_ARP + int l; + struct etharp_q_entry *r; + + l = qlen - 1; + r = arp_table[i].q; + while (l--) + r = r->next; + r->next = NULL; + + pbuf_free(new_entry->p); + memp_free(MEMP_ARP_QUEUE, new_entry); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue the packet %p (queue is full)\n", (void *)q)); + return ERR_MEM; +#else struct etharp_q_entry *old; old = arp_table[i].q; arp_table[i].q = arp_table[i].q->next; pbuf_free(old->p); memp_free(MEMP_ARP_QUEUE, old); +#endif } #endif LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); From 4bf0465dabddc24a98a409fa701212058aeaade5 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 22 Nov 2016 11:46:10 +0800 Subject: [PATCH 277/285] esp32: fix random pkt cause esp32 watchdog This change fixes the ESP32 watchdog issue. When the MPDU of AMPDU is longer than 1600B, esp32 will check whether the last 4B is DEADBEEF, if it's not DEADBEEF, it will assert and the assert will cause esp32 watchdog. The solution is to init the last 4B to DEADBEEF when malloc new ebuf for the packet longer than 1600B. --- components/esp32/lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp32/lib b/components/esp32/lib index ea9c156e8a..e2e5781dc2 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit ea9c156e8a67d27623eab6f98ce5a55a00c8fb19 +Subproject commit e2e5781dc27e638c5e63f85bc23590dd21af1619 From d3904493719d70cd205e01dedd1e0eeef77bfe16 Mon Sep 17 00:00:00 2001 From: antti Date: Thu, 17 Nov 2016 16:36:10 +0800 Subject: [PATCH 278/285] add unit tests to esp-idf rename nvs host test folder, modify .gitlab-ci.yml remove unit-test-app build re-format unit test files remove extra newlines in project.mk some refactoring for unit test part in project.mk add build files of unit-test-app in gitignore add README.md for unit test app correct headings in README.md remove files and make minor tweaks in unit test app update .gitlab-ci.yml to use unit test app delete unused lines in component_wrapper.mk delete periph_i2s.h and lcd test add text floating point in components/esp32/test/Kconfig correct idf test build paths in .gitlab-ci.yml --- .gitignore | 5 + .gitlab-ci.yml | 13 +- components/bootloader/Makefile.projbuild | 2 +- components/esp32/test/Kconfig | 9 + components/esp32/test/component.mk | 17 + components/esp32/test/logo.jpg | Bin 0 -> 7561 bytes components/esp32/test/test_ahb_arb.c | 293 ++++ components/esp32/test/test_ahb_arb_asm.S | 51 + components/esp32/test/test_fastbus.c | 132 ++ components/esp32/test/test_fastbus_asm.S | 32 + components/esp32/test/test_fp.c | 195 +++ components/esp32/test/test_miniz.c | 77 + components/esp32/test/test_tjpgd.c | 91 ++ components/esp32/test/test_unal_dma.c | 205 +++ components/freertos/test/component.mk | 5 + components/freertos/test/test_freertos.c | 229 +++ .../freertos/test/test_freertos_eventgroups.c | 105 ++ .../freertos/test/test_freertos_task_delete.c | 22 + components/freertos/test/test_newlib_reent.c | 60 + components/freertos/test/test_panic.c | 26 + components/freertos/test/test_ringbuf.c | 197 +++ components/freertos/test/test_tls_deletecb.c | 58 + components/mbedtls/test/component.mk | 5 + components/mbedtls/test/test_mbedtls.c | 134 ++ components/newlib/test/component.mk | 5 + components/newlib/test/test_newlib.c | 102 ++ components/nvs_flash/test/component.mk | 5 + components/nvs_flash/test/test_nvs.c | 54 + .../{test => test_nvs_host}/Makefile | 0 .../{test => test_nvs_host}/catch.hpp | 0 .../nvs_flash/{test => test_nvs_host}/crc.cpp | 0 .../nvs_flash/{test => test_nvs_host}/crc.h | 0 .../{test => test_nvs_host}/main.cpp | 0 .../{test => test_nvs_host}/sdkconfig.h | 0 .../spi_flash_emulation.cpp | 0 .../spi_flash_emulation.h | 0 .../test_compressed_enum_table.cpp | 0 .../test_intrusive_list.cpp | 0 .../{test => test_nvs_host}/test_nvs.cpp | 0 .../test_spi_flash_emulation.cpp | 0 components/partition_table/test/component.mk | 5 + .../partition_table/test/test_partition.c | 95 ++ components/spi_flash/test/component.mk | 5 + components/spi_flash/test/test_mmap.c | 83 ++ components/spi_flash/test/test_spi_flash.c | 92 ++ make/component_wrapper.mk | 3 - make/project.mk | 15 +- tools/unit-test-app/Makefile | 9 + tools/unit-test-app/README.md | 12 + .../components/unity/component.mk | 3 + .../components/unity/include/unity.h | 292 ++++ .../components/unity/include/unity_config.h | 75 + .../unity/include/unity_internals.h | 772 ++++++++++ .../components/unity/license.txt | 21 + tools/unit-test-app/components/unity/unity.c | 1306 +++++++++++++++++ .../components/unity/unity_platform.c | 165 +++ tools/unit-test-app/main/app_main.c | 18 + tools/unit-test-app/main/component.mk | 5 + tools/unit-test-app/sdkconfig | 125 ++ 59 files changed, 5215 insertions(+), 15 deletions(-) create mode 100644 components/esp32/test/Kconfig create mode 100644 components/esp32/test/component.mk create mode 100644 components/esp32/test/logo.jpg create mode 100644 components/esp32/test/test_ahb_arb.c create mode 100644 components/esp32/test/test_ahb_arb_asm.S create mode 100644 components/esp32/test/test_fastbus.c create mode 100644 components/esp32/test/test_fastbus_asm.S create mode 100644 components/esp32/test/test_fp.c create mode 100644 components/esp32/test/test_miniz.c create mode 100644 components/esp32/test/test_tjpgd.c create mode 100644 components/esp32/test/test_unal_dma.c create mode 100644 components/freertos/test/component.mk create mode 100644 components/freertos/test/test_freertos.c create mode 100644 components/freertos/test/test_freertos_eventgroups.c create mode 100644 components/freertos/test/test_freertos_task_delete.c create mode 100644 components/freertos/test/test_newlib_reent.c create mode 100644 components/freertos/test/test_panic.c create mode 100644 components/freertos/test/test_ringbuf.c create mode 100644 components/freertos/test/test_tls_deletecb.c create mode 100644 components/mbedtls/test/component.mk create mode 100644 components/mbedtls/test/test_mbedtls.c create mode 100644 components/newlib/test/component.mk create mode 100644 components/newlib/test/test_newlib.c create mode 100644 components/nvs_flash/test/component.mk create mode 100644 components/nvs_flash/test/test_nvs.c rename components/nvs_flash/{test => test_nvs_host}/Makefile (100%) rename components/nvs_flash/{test => test_nvs_host}/catch.hpp (100%) rename components/nvs_flash/{test => test_nvs_host}/crc.cpp (100%) rename components/nvs_flash/{test => test_nvs_host}/crc.h (100%) rename components/nvs_flash/{test => test_nvs_host}/main.cpp (100%) rename components/nvs_flash/{test => test_nvs_host}/sdkconfig.h (100%) rename components/nvs_flash/{test => test_nvs_host}/spi_flash_emulation.cpp (100%) rename components/nvs_flash/{test => test_nvs_host}/spi_flash_emulation.h (100%) rename components/nvs_flash/{test => test_nvs_host}/test_compressed_enum_table.cpp (100%) rename components/nvs_flash/{test => test_nvs_host}/test_intrusive_list.cpp (100%) rename components/nvs_flash/{test => test_nvs_host}/test_nvs.cpp (100%) rename components/nvs_flash/{test => test_nvs_host}/test_spi_flash_emulation.cpp (100%) create mode 100644 components/partition_table/test/component.mk create mode 100644 components/partition_table/test/test_partition.c create mode 100644 components/spi_flash/test/component.mk create mode 100644 components/spi_flash/test/test_mmap.c create mode 100644 components/spi_flash/test/test_spi_flash.c create mode 100644 tools/unit-test-app/Makefile create mode 100644 tools/unit-test-app/README.md create mode 100644 tools/unit-test-app/components/unity/component.mk create mode 100644 tools/unit-test-app/components/unity/include/unity.h create mode 100644 tools/unit-test-app/components/unity/include/unity_config.h create mode 100644 tools/unit-test-app/components/unity/include/unity_internals.h create mode 100644 tools/unit-test-app/components/unity/license.txt create mode 100644 tools/unit-test-app/components/unity/unity.c create mode 100644 tools/unit-test-app/components/unity/unity_platform.c create mode 100644 tools/unit-test-app/main/app_main.c create mode 100644 tools/unit-test-app/main/component.mk create mode 100644 tools/unit-test-app/sdkconfig diff --git a/.gitignore b/.gitignore index 2870b4a805..25a5a30910 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,8 @@ examples/*/build docs/_build/ docs/doxygen-warning-log.txt docs/xml/ + +# Unit test app files +tools/unit-test-app/sdkconfig +tools/unit-test-app/sdkconfig.old +tools/unit-test-app/build diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 814279ce15..1d68d2f601 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -78,15 +78,14 @@ build_esp_idf_tests: <<: *build_template artifacts: paths: - - ./esp-idf-tests/build/*.bin - - ./esp-idf-tests/build/*.elf - - ./esp-idf-tests/build/*.map - - ./esp-idf-tests/build/bootloader/*.bin + - ./tools/unit-test-app/build/*.bin + - ./tools/unit-test-app/build/*.elf + - ./tools/unit-test-app/build/*.map + - ./tools/unit-test-app/build/bootloader/*.bin expire_in: 6 mos script: - - git clone $GITLAB_SSH_SERVER/idf/esp-idf-tests.git - - cd esp-idf-tests + - cd tools/unit-test-app - git checkout ${CI_BUILD_REF_NAME} || echo "Using default branch..." - make defconfig - make @@ -131,7 +130,7 @@ test_nvs_on_host: tags: - nvs_host_test script: - - cd components/nvs_flash/test + - cd components/nvs_flash/test_nvs_host - make test test_build_system: diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3faff53d62..a49f3d8686 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -20,7 +20,7 @@ export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ - V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) + V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) TEST_COMPONENTS= .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) diff --git a/components/esp32/test/Kconfig b/components/esp32/test/Kconfig new file mode 100644 index 0000000000..af5e5b45e7 --- /dev/null +++ b/components/esp32/test/Kconfig @@ -0,0 +1,9 @@ +menu "TESTS" + +config FP_TEST_ENABLE + bool "Enable test fp" + default "y" + help + For FPGA single core CPU which has no floating point support, floating point test should be disabled. + +endmenu diff --git a/components/esp32/test/component.mk b/components/esp32/test/component.mk new file mode 100644 index 0000000000..5e7d11ddf8 --- /dev/null +++ b/components/esp32/test/component.mk @@ -0,0 +1,17 @@ +# +#Component Makefile +# + +COMPONENT_EXTRA_CLEAN := test_tjpgd_logo.h + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive + +COMPONENT_SRCDIRS := . test_vectors + +include $(IDF_PATH)/make/component_common.mk + +test_tjpgd.o: test_tjpgd_logo.h + +test_tjpgd_logo.h: $(COMPONENT_PATH)/logo.jpg + $(summary) XXD logo.jpg + $(Q) cd $(COMPONENT_PATH); xxd -i logo.jpg $(COMPONENT_BUILD_DIR)/test_tjpgd_logo.h diff --git a/components/esp32/test/logo.jpg b/components/esp32/test/logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2bd9e775eac4d4195d9d7465239fa007715ff428 GIT binary patch literal 7561 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 +#include +#include +#include "rom/ets_sys.h" +#include "rom/lldesc.h" +#include "rom/gpio.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" + +#include "unity.h" + +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_reg.h" +#include "soc/i2s_reg.h" + + +#define DPORT_I2S0_CLK_EN (BIT(4)) +#define DPORT_I2S0_RST (BIT(4)) + + +/* +This test tests the s32c1i instruction when the AHB bus is also used. To create some AHB traffic, we use the I2S interface +to copy bytes over from one memory location to another. DO NOT USE the i2s routines inhere, they've been trial-and-error'ed until +the point where they happened to do what I want. +*/ + +static void lcdIfaceInit() +{ + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); + + //Init pins to i2s functions + SET_PERI_REG_MASK(GPIO_ENABLE_W1TS_REG, (1 << 11) | (1 << 3) | (1 << 0) | (1 << 2) | (1 << 5) | (1 << 16) | (1 << 17) | (1 << 18) | (1 << 19) | (1 << 20)); //ENABLE GPIO oe_enable + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO18_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO19_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO20_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 2); //11 + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO26_U, 0); //RS + + WRITE_PERI_REG(GPIO_FUNC0_OUT_SEL_CFG_REG, (148 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC2_OUT_SEL_CFG_REG, (149 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC5_OUT_SEL_CFG_REG, (150 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC16_OUT_SEL_CFG_REG, (151 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC17_OUT_SEL_CFG_REG, (152 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC18_OUT_SEL_CFG_REG, (153 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC19_OUT_SEL_CFG_REG, (154 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC20_OUT_SEL_CFG_REG, (155 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC26_OUT_SEL_CFG_REG, (156 << GPIO_FUNC0_OUT_SEL_S)); //RS + WRITE_PERI_REG(GPIO_FUNC11_OUT_SEL_CFG_REG, (I2S0O_WS_OUT_IDX << GPIO_FUNC0_OUT_SEL_S)); +// WRITE_PERI_REG(GPIO_FUNC11_OUT_SEL_CFG, (I2S0O_BCK_OUT_IDX< +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" + + +/* +This test tests the 'fast' peripherial bus at 0x3ff40000. This bus is connected directly to the core, and as such +can receive 'speculative' reads, that is, reads that may or may not actually be executed in the code flow. This +may mess with any FIFOs mapped in the region: if a byte gets dropped due to a missed speculative read, the fifo +may advance to the next byte anyway. + +This code tests reading/writing from the UART1 FIFO, using both cores. For this to work, it's required that the +UARTs RX and TX lines are connected. +*/ + + +void test_fastbus_cp(int fifo_addr, unsigned char *buf, int len, int *dummy); + +static volatile int state = 0; +static volatile int xor = 0; +static unsigned char res[128]; + +static void tskOne(void *pvParameters) +{ + int run = 0, err = 0; + int x; + int ct[256]; + volatile int w; + int dummy; + while (1) { + state = 1; + for (x = 0; x < 64; x++) { + WRITE_PERI_REG(UART_FIFO_REG(1), x ^ xor); + } + for (w = 0; w < (1 << 14); w++); //delay + state = 2; + test_fastbus_cp(UART_FIFO_REG(1), &res[0], 64, &dummy); + for (w = 0; w < (1 << 10); w++); //delay + for (x = 0; x < 255; x++) { + ct[x] = 0; //zero ctrs + } + for (x = 0; x < 128; x++) { + ct[(int)res[x]^xor]++; //count values + } + for (x = 0; x < 255; x++) { //check counts + if (ct[x] != (x < 128 ? 1 : 0)) { + //Disregard first few loops; there may be crap in the fifo. + if (run > 2) { + err++; + printf("Error! Received value %d %d times!\n", x, ct[x]); + } + } + } + run++; + if ((run & 255) == 0) { + printf("Loop %d errct %d\n", run, err); + } + xor = (xor + 1) & 0xff; + } +} + +#define FB2ADDR 0x40098000 + +static void tskTwo(void *pvParameters) +{ + int x; + int dummy; + int *p = (int *)FB2ADDR; + int *s = (int *)test_fastbus_cp; + for (x = 0; x < 100; x++) { + *p++ = *s++; + } + void (*test_fastbus_cp2)(int fifo_addr, unsigned char * buf, int len, int * dummy) = (void *)FB2ADDR; + + + while (1) { + while (state != 1) ; + for (x = 64; x < 128; x++) { + WRITE_PERI_REG(UART_FIFO_REG(1), x ^ xor); + } + while (state != 2); + test_fastbus_cp2(UART_FIFO_REG(1), &res[64], 64, &dummy); + } +} + + +// TODO: split this thing into separate orthogonal tests +TEST_CASE("Fast I/O bus test", "[hw]") +{ + int i; + if ((REG_UART_BASE(0) >> 16) != 0x3ff4) { + printf("Error! Uart base isn't on fast bus.\n"); + TEST_ASSERT(0); + } + + PIN_PULLUP_DIS(PERIPHS_IO_MUX_SD_DATA3_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, FUNC_SD_DATA2_U1RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, FUNC_SD_DATA3_U1TXD); + + int reg_val = (1 << UART_RXFIFO_FULL_THRHD_S); + WRITE_PERI_REG(UART_CONF1_REG(1), reg_val); + WRITE_PERI_REG(UART_CLKDIV_REG(1), 0x30); //semi-random +// CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(1), UART_TXFIFO_EMPTY_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); + + TaskHandle_t th[2]; + printf("Creating tasks\n"); + xTaskCreatePinnedToCore(tskOne , "tskone" , 2048, NULL, 3, &th[0], 0); + xTaskCreatePinnedToCore(tskTwo , "tsktwo" , 2048, NULL, 3, &th[1], 1); + + // Let stuff run for 20s + while (1) { + vTaskDelay(20000 / portTICK_PERIOD_MS); + } + + //Shut down all the tasks + for (i = 0; i < 2; i++) { + vTaskDelete(th[i]); + } + xt_ints_off(1 << ETS_UART0_INUM); +} + diff --git a/components/esp32/test/test_fastbus_asm.S b/components/esp32/test/test_fastbus_asm.S new file mode 100644 index 0000000000..1ced09fcf7 --- /dev/null +++ b/components/esp32/test/test_fastbus_asm.S @@ -0,0 +1,32 @@ +/* +This little bit of code is executed in-place by one CPU, but copied to a different memory region +by the other CPU. Make sure it stays position-independent. +*/ + .text + .align 4 + .global test_fastbus_cp + .type test_fastbus_cp,@function +//Args: +//a2 - fifo addr +//a3 - buf addr +//a4 - len +//a5 - ptr to int to use +test_fastbus_cp: + entry a1,64 +back: + beqi a4, 0, out //check if loop done + s32i a4, a5, 0 //store value, for shits and/or giggles + memw //make sure write happens + l32i a4, a5, 0 //load value again, to thwart any prediction in the pipeline + bbsi a4, 0, pred //Random jump to check predictive reads. Both branches should do the same. + l32i a6, a2, 0 //read from fifo 1 + j predout +pred: + l32i a6, a2, 0 //read from fifo 2 +predout: + s8i a6, a3, 0 //store result + addi a3, a3, 1 //inc ptr + addi a4, a4, -1 //next + j back //loop again +out: + retw //and we are done diff --git a/components/esp32/test/test_fp.c b/components/esp32/test/test_fp.c new file mode 100644 index 0000000000..5c2573b227 --- /dev/null +++ b/components/esp32/test/test_fp.c @@ -0,0 +1,195 @@ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" + +#if CONFIG_FP_TEST_ENABLE +static float addsf(float a, float b) +{ + float result; + asm volatile ( + "wfr f0, %1\n" + "wfr f1, %2\n" + "add.s f2, f0, f1\n" + "rfr %0, f2\n" + :"=r"(result):"r"(a), "r"(b) + ); + return result; +} + +static float mulsf(float a, float b) +{ + float result; + asm volatile ( + "wfr f0, %1\n" + "wfr f1, %2\n" + "mul.s f2, f0, f1\n" + "rfr %0, f2\n" + :"=r"(result):"r"(a), "r"(b) + ); + return result; +} + +static float divsf(float a, float b) +{ + float result; + asm volatile ( + "wfr f0, %1\n" + "wfr f1, %2\n" + "div0.s f3, f1 \n" + "nexp01.s f4, f1 \n" + "const.s f5, 1 \n" + "maddn.s f5, f4, f3 \n" + "mov.s f6, f3 \n" + "mov.s f7, f1 \n" + "nexp01.s f8, f0 \n" + "maddn.s f6, f5, f3 \n" + "const.s f5, 1 \n" + "const.s f2, 0 \n" + "neg.s f9, f8 \n" + "maddn.s f5,f4,f6 \n" + "maddn.s f2, f0, f3 \n" + "mkdadj.s f7, f0 \n" + "maddn.s f6,f5,f6 \n" + "maddn.s f9,f4,f2 \n" + "const.s f5, 1 \n" + "maddn.s f5,f4,f6 \n" + "maddn.s f2,f9,f6 \n" + "neg.s f9, f8 \n" + "maddn.s f6,f5,f6 \n" + "maddn.s f9,f4,f2 \n" + "addexpm.s f2, f7 \n" + "addexp.s f6, f7 \n" + "divn.s f2,f9,f6\n" + "rfr %0, f2\n" + :"=r"(result):"r"(a), "r"(b) + ); + return result; +} + +static float sqrtsf(float a) +{ + float result; + asm volatile ( + "wfr f0, %1\n" + "sqrt0.s f2, f0\n" + "const.s f5, 0\n" + "maddn.s f5, f2, f2\n" + "nexp01.s f3, f0\n" + "const.s f4, 3\n" + "addexp.s f3, f4\n" + "maddn.s f4, f5, f3\n" + "nexp01.s f5, f0\n" + "neg.s f6, f5\n" + "maddn.s f2, f4, f2\n" + "const.s f1, 0\n" + "const.s f4, 0\n" + "const.s f7, 0\n" + "maddn.s f1, f6, f2\n" + "maddn.s f4, f2, f3\n" + "const.s f6, 3\n" + "maddn.s f7, f6, f2\n" + "maddn.s f5, f1, f1\n" + "maddn.s f6, f4, f2\n" + "neg.s f3, f7\n" + "maddn.s f1, f5, f3\n" + "maddn.s f7, f6, f7\n" + "mksadj.s f2, f0\n" + "nexp01.s f5, f0\n" + "maddn.s f5, f1, f1\n" + "neg.s f3, f7\n" + "addexpm.s f1, f2\n" + "addexp.s f3, f2\n" + "divn.s f1, f5, f3\n" + "rfr %0, f1\n" + :"=r"(result):"r"(a) + ); + return result; +} + +TEST_CASE("test FP add", "[fp]") +{ + float a = 100.0f; + float b = 0.5f; + float c = addsf(a, b); + float eps = c - 100.5f; + printf("a=%g b=%g c=%g eps=%g\r\n", a, b, c, eps); + TEST_ASSERT_TRUE(fabs(eps) < 0.000001); +} + +TEST_CASE("test FP mul", "[fp]") +{ + float a = 100.0f; + float b = 0.05f; + float c = mulsf(a, b); + float eps = c - 5.0f; + printf("a=%g b=%g c=%g eps=%g\r\n", a, b, c, eps); + TEST_ASSERT_TRUE(fabs(eps) < 0.000001); +} + +TEST_CASE("test FP div", "[fp]") +{ + float a = 100.0f; + float b = 5.0f; + float c = divsf(a, b); + float eps = c - 20.0f; + printf("a=%g b=%g c=%g eps=%g\r\n", a, b, c, eps); + TEST_ASSERT_TRUE(fabs(eps) < 0.000001); +} + +TEST_CASE("test FP sqrt", "[fp]") +{ + float a = 100.0f; + float c = sqrtsf(a); + float eps = c - 10.0f; + printf("a=%g c=%g eps=%g\r\n", a, c, eps); + TEST_ASSERT_TRUE(fabs(eps) < 0.000001); +} + + +struct TestFPState { + int fail; + int done; +}; + +static const int testFpIter = 100000; + +static void tskTestFP(void *pvParameters) +{ + struct TestFPState *state = (struct TestFPState *) pvParameters; + for (int i = 0; i < testFpIter; ++i) { + // calculate zero in a slightly obscure way + float y = sqrtsf(addsf(1.0f, divsf(mulsf(sqrtsf(2), sqrtsf(2)), 2.0f))); + y = mulsf(y, y); + y = addsf(y, -2.0f); + // check that result is not far from zero + float eps = fabs(y); + if (eps > 1e-6f) { + state->fail++; + printf("%s: i=%d y=%f eps=%f\r\n", __func__, i, y, eps); + } + } + state->done++; + vTaskDelete(NULL); +} + +TEST_CASE("context switch saves FP registers", "[fp]") +{ + struct TestFPState state; + state.done = 0; + state.fail = 0; + xTaskCreatePinnedToCore(tskTestFP, "tsk1", 2048, &state, 3, NULL, 0); + xTaskCreatePinnedToCore(tskTestFP, "tsk2", 2048, &state, 3, NULL, 0); + xTaskCreatePinnedToCore(tskTestFP, "tsk3", 2048, &state, 3, NULL, 1); + xTaskCreatePinnedToCore(tskTestFP, "tsk4", 2048, &state, 3, NULL, 0); + while (state.done != 4) { + vTaskDelay(100 / portTICK_PERIOD_MS); + } + if (state.fail) { + const int total = testFpIter * 4; + printf("Failed: %d, total: %d\r\n", state.fail, total); + } + TEST_ASSERT(state.fail == 0); +} +#endif diff --git a/components/esp32/test/test_miniz.c b/components/esp32/test/test_miniz.c new file mode 100644 index 0000000000..3453c859a9 --- /dev/null +++ b/components/esp32/test/test_miniz.c @@ -0,0 +1,77 @@ + + +#include +#include "rom/miniz.h" +#include "unity.h" + + +#define DATASIZE (1024*64) + +TEST_CASE("Test miniz compression/decompression", "[miniz]") +{ + int x; + char b; + char *inbuf, *outbuf; + tdefl_compressor *comp; + tinfl_decompressor *decomp; + tdefl_status status; + size_t inbytes = 0, outbytes = 0, inpos = 0, outpos = 0, compsz; + printf("Allocating data buffer and filling it with semi-random data\n"); + inbuf = malloc(DATASIZE); + TEST_ASSERT(inbuf != NULL); + srand(0); + for (x = 0; x < DATASIZE; x++) { + inbuf[x] = (x & 1) ? rand() & 0xff : 0; + } + printf("Allocating compressor & outbuf (%d bytes)\n", sizeof(tdefl_compressor)); + comp = malloc(sizeof(tdefl_compressor)); + TEST_ASSERT(comp != NULL); + outbuf = malloc(DATASIZE); + TEST_ASSERT(outbuf != NULL); + printf("Compressing...\n"); + status = tdefl_init(comp, NULL, NULL, TDEFL_WRITE_ZLIB_HEADER | 1500); + TEST_ASSERT(status == TDEFL_STATUS_OKAY); + while (inbytes != DATASIZE) { + outbytes = DATASIZE - outpos; + inbytes = DATASIZE - inpos; + tdefl_compress(comp, &inbuf[inpos], &inbytes, &outbuf[outpos], &outbytes, TDEFL_FINISH); + printf("...Compressed %d into %d bytes\n", inbytes, outbytes); + inpos += inbytes; outpos += outbytes; + } + compsz = outpos; + free(comp); + //Kill inbuffer + for (x = 0; x < DATASIZE; x++) { + inbuf[x] = 0; + } + free(inbuf); + + inbuf = outbuf; + outbuf = malloc(DATASIZE); + TEST_ASSERT(outbuf != NULL); + printf("Reinflating...\n"); + decomp = malloc(sizeof(tinfl_decompressor)); + TEST_ASSERT(decomp != NULL); + tinfl_init(decomp); + inpos = 0; outpos = 0; + while (inbytes != compsz) { + outbytes = DATASIZE - outpos; + inbytes = compsz - inpos; + tinfl_decompress(decomp, (const mz_uint8 *)&inbuf[inpos], &inbytes, (uint8_t *)outbuf, (mz_uint8 *)&outbuf[outpos], &outbytes, TINFL_FLAG_PARSE_ZLIB_HEADER); + printf("...Decompressed %d into %d bytes\n", inbytes, outbytes); + inpos += inbytes; outpos += outbytes; + } + printf("Checking if same...\n"); + srand(0); + for (x = 0; x < DATASIZE; x++) { + b = (x & 1) ? rand() & 0xff : 0; + if (outbuf[x] != b) { + printf("Pos %x: %hhx!=%hhx\n", x, outbuf[x], b); + TEST_ASSERT(0); + } + } + printf("Great Success!\n"); + free(inbuf); + free(outbuf); + free(decomp); +} diff --git a/components/esp32/test/test_tjpgd.c b/components/esp32/test/test_tjpgd.c new file mode 100644 index 0000000000..60c687f079 --- /dev/null +++ b/components/esp32/test/test_tjpgd.c @@ -0,0 +1,91 @@ + + +#include +#include "rom/tjpgd.h" +#include +#include +#include +#include "unity.h" + +#include "test_tjpgd_logo.h" + +typedef struct { + const unsigned char *inData; + int inPos; + unsigned char *outData; + int outW; + int outH; +} JpegDev; + + +static UINT infunc(JDEC *decoder, BYTE *buf, UINT len) +{ + JpegDev *jd = (JpegDev *)decoder->device; + printf("Reading %d bytes from pos %d\n", len, jd->inPos); + if (buf != NULL) { + memcpy(buf, jd->inData + jd->inPos, len); + } + jd->inPos += len; + return len; +} + + +static UINT outfunc(JDEC *decoder, void *bitmap, JRECT *rect) +{ + unsigned char *in = (unsigned char *)bitmap; + unsigned char *out; + int y; + printf("Rect %d,%d - %d,%d\n", rect->top, rect->left, rect->bottom, rect->right); + JpegDev *jd = (JpegDev *)decoder->device; + for (y = rect->top; y <= rect->bottom; y++) { + out = jd->outData + ((jd->outW * y) + rect->left) * 3; + memcpy(out, in, ((rect->right - rect->left) + 1) * 3); + in += ((rect->right - rect->left) + 1) * 3; + } + return 1; +} + +#define TESTW 48 +#define TESTH 48 +#define WORKSZ 3100 + +TEST_CASE("Test JPEG decompression library", "[tjpgd]") +{ + char aapix[] = " .:;+=xX$$"; + unsigned char *decoded, *p; + char *work; + int r; + int x, y, v; + JDEC decoder; + JpegDev jd; + decoded = malloc(48 * 48 * 3); + for (x = 0; x < 48 * 48 * 3; x += 2) { + decoded[x] = 0; decoded[x + 1] = 0xff; + } + work = malloc(WORKSZ); + memset(work, 0, WORKSZ); + + jd.inData = logo_jpg; + jd.inPos = 0; + jd.outData = decoded; + jd.outW = TESTW; + jd.outH = TESTH; + + r = jd_prepare(&decoder, infunc, work, WORKSZ, (void *)&jd); + TEST_ASSERT_EQUAL(r, JDR_OK); + r = jd_decomp(&decoder, outfunc, 0); + TEST_ASSERT_EQUAL(r, JDR_OK); + + p = decoded + 2; + for (y = 0; y < TESTH; y++) { + for (x = 0; x < TESTH; x++) { + v = ((*p) * (sizeof(aapix) - 2) * 2) / 256; + printf("%c%c", aapix[v / 2], aapix[(v + 1) / 2]); + p += 3; + } + printf("%c%c", ' ', '\n'); + } + + free(work); + free(decoded); +} diff --git a/components/esp32/test/test_unal_dma.c b/components/esp32/test/test_unal_dma.c new file mode 100644 index 0000000000..3726632a41 --- /dev/null +++ b/components/esp32/test/test_unal_dma.c @@ -0,0 +1,205 @@ + +#include +#include +#include +#include +#include "rom/ets_sys.h" +#include "rom/lldesc.h" +#include "rom/gpio.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" + +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_reg.h" +#include "soc/i2s_reg.h" + + +#define DPORT_I2S0_CLK_EN (BIT(4)) +#define DPORT_I2S0_RST (BIT(4)) + +static volatile lldesc_t dmaDesc[2]; + + +//hacked up routine to essentially do a memcpy() using dma. Supports max 4K-1 bytes. +static void dmaMemcpy(void *in, void *out, int len) +{ + volatile int i; + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN); + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST); + + //Init pins to i2s functions + SET_PERI_REG_MASK(GPIO_ENABLE_W1TS_REG, (1 << 11) | (1 << 3) | (1 << 0) | (1 << 2) | (1 << 5) | (1 << 16) | (1 << 17) | (1 << 18) | (1 << 19) | (1 << 20)); //ENABLE GPIO oe_enable + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO18_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO19_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO20_U, 0); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 2); //11 + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO26_U, 0); //RS + + WRITE_PERI_REG(GPIO_FUNC0_OUT_SEL_CFG_REG, (148 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC2_OUT_SEL_CFG_REG, (149 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC5_OUT_SEL_CFG_REG, (150 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC16_OUT_SEL_CFG_REG, (151 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC17_OUT_SEL_CFG_REG, (152 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC18_OUT_SEL_CFG_REG, (153 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC19_OUT_SEL_CFG_REG, (154 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC20_OUT_SEL_CFG_REG, (155 << GPIO_FUNC0_OUT_SEL_S)); + WRITE_PERI_REG(GPIO_FUNC26_OUT_SEL_CFG_REG, (156 << GPIO_FUNC0_OUT_SEL_S)); //RS + WRITE_PERI_REG(GPIO_FUNC11_OUT_SEL_CFG_REG, (I2S0O_WS_OUT_IDX << GPIO_FUNC0_OUT_SEL_S)); +// WRITE_PERI_REG(GPIO_FUNC11_OUT_SEL_CFG, (I2S0O_BCK_OUT_IDX< +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" + + +void ets_isr_unmask(uint32_t unmask); + +static xQueueHandle myQueue; +static xQueueHandle uartRxQueue; + +int ctr; + +#if 1 +//Idle-loop for delay. Tests involuntary yielding +static void cvTaskDelay(int dummy) +{ + volatile int i; + for (i = 0; i < (1 << 17); i++); +} +#else +//Delay task execution using FreeRTOS methods. Tests voluntary yielding. +#define cvTaskDelay(x) vTaskDelay(x) +#endif + +#if 0 +static void dosegfault3(int i) +{ + volatile char *p = (volatile char *)0; + *p = i; +} + +static void dosegfault2(int i) +{ + if (i > 3) { + dosegfault3(i); + } +} + +static void dosegfault(int i) +{ + if (i < 5) { + dosegfault(i + 1); + } + dosegfault2(i); +} +#endif + +static void queueSender(void *pvParameters) +{ + int myCtr = xPortGetCoreID() * 100000; + while (1) { + printf("Core %d: Send to queue: %d\n", xPortGetCoreID(), myCtr); + xQueueSend(myQueue, (void *)(&myCtr), portMAX_DELAY); + printf("Send to queue done.\n"); + cvTaskDelay(100); + myCtr++; + } +} + +static void queueReceiver(void *pvParameters) +{ + int theCtr; + while (1) { + xQueueReceive(myQueue, &theCtr, portMAX_DELAY); + printf("Core %d: Receive from queue: %d\n", xPortGetCoreID(), theCtr); + } +} + + +static void tskone(void *pvParameters) +{ +// char *p=(char *)0; + while (1) { + ctr++; +// if (ctr>60) dosegfault(3); + printf("Task1, core %d, ctr=%d\n", xPortGetCoreID(), ctr); + cvTaskDelay(500); + } +} + +static void tsktwo(void *pvParameters) +{ + while (1) { + ctr++; + printf("Task2, core %d, ctr=%d\n", xPortGetCoreID(), ctr); + cvTaskDelay(500); + } +} + +static void tskthree(void *pvParameters) +{ + while (1) { + ctr++; + printf("Task3, core %d, ctr=%d\n", xPortGetCoreID(), ctr); + cvTaskDelay(500); + } +} + +static void tskfour(void *pvParameters) +{ + while (1) { + ctr++; + printf("Task4, core %d, ctr=%d\n", xPortGetCoreID(), ctr); + cvTaskDelay(500); + } +} + +static void tskfive(void *pvParameters) +{ + while (1) { + ctr++; + printf("Task5, core %d, ctr=%d\n", xPortGetCoreID(), ctr); + cvTaskDelay(500); + } +} + +static void tskyield(void *pvParameters) +{ + while (1) { + portYIELD(); + } +} + +static void tskUartRecv(void *pvParameters) +{ + char c; + while (1) { + xQueueReceive(uartRxQueue, &c, portMAX_DELAY); + printf("Uart received %c!\n", c); + } +} + + +static void uartIsrHdl(void *arg) +{ + char c; + BaseType_t xHigherPriorityTaskWoken; + SET_PERI_REG_MASK(UART_INT_CLR_REG(0), UART_RXFIFO_FULL_INT_CLR); + while (READ_PERI_REG(UART_STATUS_REG(0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) { + c = READ_PERI_REG(UART_FIFO_REG(0)); + xQueueSendFromISR(uartRxQueue, &c, &xHigherPriorityTaskWoken); + printf("ISR: %c\n", c); + } + if (xHigherPriorityTaskWoken) { + portYIELD_FROM_ISR(); + } +} + +static void uartRxInit(xQueueHandle q) +{ + uint32_t reg_val; + + PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + +// reg_val = READ_PERI_REG(UART_CONF1(0)); + reg_val = (1 << UART_RXFIFO_FULL_THRHD_S); + WRITE_PERI_REG(UART_CONF1_REG(0), reg_val); + CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_TXFIFO_EMPTY_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RXFIFO_FULL_INT_ENA); + + printf("Enabling int %d\n", ETS_UART0_INUM); + REG_SET_FIELD(DPORT_PRO_UART_INTR_MAP_REG, DPORT_PRO_UART_INTR_MAP, ETS_UART0_INUM); + REG_SET_FIELD(DPORT_PRO_UART1_INTR_MAP_REG, DPORT_PRO_UART1_INTR_MAP, ETS_UART0_INUM); + + xt_set_interrupt_handler(ETS_UART0_INUM, uartIsrHdl, NULL); + xt_ints_on(1 << ETS_UART0_INUM); + +} + +// TODO: split this thing into separate orthogonal tests +TEST_CASE("Bunch of FreeRTOS tests", "[freertos]") +{ + char *tst; + TaskHandle_t th[12]; + int i; + printf("%s\n", __FUNCTION__); + tst = pvPortMalloc(16); + printf("Test malloc returns addr %p\n", tst); + printf("Free heap: %u\n", xPortGetFreeHeapSize()); + myQueue = xQueueCreate(10, sizeof(int)); + uartRxQueue = xQueueCreate(256, sizeof(char)); + printf("Free heap: %u\n", xPortGetFreeHeapSize()); + + printf("Creating tasks\n"); + xTaskCreatePinnedToCore(tskyield , "tskyield1" , 2048, NULL, 3, &th[0], 0); + xTaskCreatePinnedToCore(tskyield , "tskyield2" , 2048, NULL, 3, &th[1], 1); + + xTaskCreatePinnedToCore(tskone , "tskone" , 2048, NULL, 3, &th[2], 0); + xTaskCreatePinnedToCore(tsktwo , "tsktwo" , 2048, NULL, 3, &th[3], 1); + xTaskCreatePinnedToCore(tskthree, "tskthree", 2048, NULL, 3, &th[4], 0); + xTaskCreatePinnedToCore(tskfour , "tskfour" , 2048, NULL, 3, &th[5], tskNO_AFFINITY); + xTaskCreatePinnedToCore(tskfive , "tskfive" , 2048, NULL, 3, &th[6], tskNO_AFFINITY); + xTaskCreatePinnedToCore(queueSender , "qsend1" , 2048, NULL, 3, &th[7], 0); + xTaskCreatePinnedToCore(queueSender , "qsend2" , 2048, NULL, 3, &th[8], 1); + xTaskCreatePinnedToCore(queueReceiver , "qrecv1" , 2048, NULL, 3, &th[9], 1); + xTaskCreatePinnedToCore(queueReceiver , "qrecv2" , 2048, NULL, 3, &th[10], 0); + xTaskCreatePinnedToCore(tskUartRecv , "tskuart" , 2048, NULL, 4, &th[11], 1); + printf("Free heap: %u\n", xPortGetFreeHeapSize()); + uartRxInit(uartRxQueue); + + // Let stuff run for 20s + vTaskDelay(20000 / portTICK_PERIOD_MS); + + //Shut down all the tasks + for (i = 0; i < 12; i++) { + vTaskDelete(th[i]); + } + xt_ints_off(1 << ETS_UART0_INUM); +} + diff --git a/components/freertos/test/test_freertos_eventgroups.c b/components/freertos/test/test_freertos_eventgroups.c new file mode 100644 index 0000000000..35a5cc4ed2 --- /dev/null +++ b/components/freertos/test/test_freertos_eventgroups.c @@ -0,0 +1,105 @@ +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" +#include "unity.h" + +#define BIT_CALL (1 << 0) +#define BIT_RESPONSE(TASK) (1 << (TASK+1)) +#define ALL_RESPONSE_BITS (((1 << NUM_TASKS) - 1) << 1) + +static const int NUM_TASKS = 4; +static const int COUNT = 4000; +static EventGroupHandle_t eg; + +static void task_event_group_call_response(void *param) +{ + int task_num = (int)param; + + printf("Started %d\n", task_num); + + for (int i = 0; i < COUNT; i++) { + /* Wait until the common "call" bit is set, starts off all tasks + (clear on return) */ + while (!xEventGroupWaitBits(eg, BIT_CALL, true, false, portMAX_DELAY)) { + } + + /* Set our individual "response" bit */ + xEventGroupSetBits(eg, BIT_RESPONSE(task_num)); + } + + printf("Task %d done\n", task_num); + + /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ + vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelete(NULL); +} + +TEST_CASE("FreeRTOS Event Groups", "[freertos]") +{ + eg = xEventGroupCreate(); + + /* Note: task_event_group_call_response all have higher priority than us, so will block together. + + This is important because we need to know they'll all have blocked on BIT_CALL each time we + signal it, or they get out of sync. + */ + for (int c = 0; c < NUM_TASKS; c++) { + xTaskCreatePinnedToCore(task_event_group_call_response, "tsk_call_resp", 4096, (void *)c, configMAX_PRIORITIES - 1, NULL, c % portNUM_PROCESSORS); + } + /* Scheduler weirdness, if we don't sleep a few ticks here then the tasks on the other CPU aren't running yet... */ + vTaskDelay(10); + + for (int i = 0; i < COUNT; i++) { + if (i % 100 == 0) { + //printf("Call %d\n", i); + } + /* signal all tasks with "CALL" bit... */ + xEventGroupSetBits(eg, BIT_CALL); + + while (xEventGroupWaitBits(eg, ALL_RESPONSE_BITS, true, true, portMAX_DELAY) != ALL_RESPONSE_BITS) { + } + } +} + + +#define BIT_DONE(X) (1<<(NUM_TASKS+1+X)) + +static void task_test_sync(void *param) +{ + int task_num = (int)param; + + printf("Started %d\n", task_num); + + for (int i = 0; i < COUNT; i++) { + /* set our bit, and wait on all tasks to set their bits */ + xEventGroupSync(eg, BIT_RESPONSE(task_num), ALL_RESPONSE_BITS, portMAX_DELAY); + /* clear our bit */ + xEventGroupClearBits(eg, BIT_RESPONSE(task_num)); + } + int after_done = xEventGroupSetBits(eg, BIT_DONE(task_num)); + + printf("Done %d = %x\n", task_num, after_done); + + /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ + vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelete(NULL); +} + +TEST_CASE("FreeRTOS Event Group Sync", "[freertos]") +{ + eg = xEventGroupCreate(); + + for (int c = 0; c < NUM_TASKS; c++) { + xTaskCreatePinnedToCore(task_test_sync, "task_test_sync", 4096, (void *)c, configMAX_PRIORITIES - 1, NULL, c % portNUM_PROCESSORS); + } + + for (int c = 0; c < NUM_TASKS; c++) { + printf("Waiting on %d (%x)\n", c, BIT_DONE(c)); + xEventGroupWaitBits(eg, BIT_DONE(c), false, false, portMAX_DELAY); + } +} + diff --git a/components/freertos/test/test_freertos_task_delete.c b/components/freertos/test/test_freertos_task_delete.c new file mode 100644 index 0000000000..3101db2564 --- /dev/null +++ b/components/freertos/test/test_freertos_task_delete.c @@ -0,0 +1,22 @@ +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" +#include "unity.h" + +static void task_delete_self(void *param) +{ + printf("Task %p running on core %d. Deleting shortly...\n", xTaskGetCurrentTaskHandle(), xPortGetCoreID()); + vTaskDelete(NULL); +} + +TEST_CASE("FreeRTOS Delete Tasks", "[freertos]") +{ + xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); + xTaskCreatePinnedToCore(task_delete_self, "tsk_self_a", 4096, NULL, configMAX_PRIORITIES - 1, NULL, 0); + vTaskDelay(200 / portTICK_PERIOD_MS); + printf("Done?\n"); +} diff --git a/components/freertos/test/test_newlib_reent.c b/components/freertos/test/test_newlib_reent.c new file mode 100644 index 0000000000..e0ec4aa458 --- /dev/null +++ b/components/freertos/test/test_newlib_reent.c @@ -0,0 +1,60 @@ +/* + Test for multicore FreeRTOS. This test spins up threads, fiddles with queues etc. +*/ + +#include +#include +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" + + +volatile static int done; +volatile static int error; + +static void tskTestRand(void *pvParameters) +{ + int l; + srand(0x1234); + vTaskDelay((int)pvParameters / portTICK_PERIOD_MS); + l = rand(); + printf("Rand1: %d\n", l); + if (l != 869320854) { + error++; + } + vTaskDelay((int)pvParameters / portTICK_PERIOD_MS); + l = rand(); + printf("Rand2: %d\n", l); + if (l != 1148737841) { + error++; + } + done++; + vTaskDelete(NULL); +} + + + +// TODO: split this thing into separate orthogonal tests +TEST_CASE("Test for per-task non-reentrant tasks", "[freertos]") +{ + done = 0; + error = 0; + xTaskCreatePinnedToCore(tskTestRand, "tsk1", 2048, (void *)100, 3, NULL, 0); + xTaskCreatePinnedToCore(tskTestRand, "tsk2", 2048, (void *)200, 3, NULL, 0); + xTaskCreatePinnedToCore(tskTestRand, "tsk3", 2048, (void *)300, 3, NULL, 1); + xTaskCreatePinnedToCore(tskTestRand, "tsk4", 2048, (void *)400, 3, NULL, 0); + while (done != 4) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + TEST_ASSERT(error == 0); +} + diff --git a/components/freertos/test/test_panic.c b/components/freertos/test/test_panic.c new file mode 100644 index 0000000000..782b7a5642 --- /dev/null +++ b/components/freertos/test/test_panic.c @@ -0,0 +1,26 @@ +/* + Test for multicore FreeRTOS. This test spins up threads, fiddles with queues etc. +*/ + +#include +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" + + +TEST_CASE("Panic handler", "[freertos]") +{ + volatile int *i; + i = (volatile int *)0x0; + *i = 1; +} + diff --git a/components/freertos/test/test_ringbuf.c b/components/freertos/test/test_ringbuf.c new file mode 100644 index 0000000000..7d3c8788c9 --- /dev/null +++ b/components/freertos/test/test_ringbuf.c @@ -0,0 +1,197 @@ +/* + Test for multicore FreeRTOS ringbuffer. +*/ + +#include +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" + +#include +#include + +void ets_isr_unmask(uint32_t unmask); + +static RingbufHandle_t rb; +typedef enum { + TST_MOSTLYFILLED, + TST_MOSTLYEMPTY, + TST_INTTOTASK, + TST_TASKTOINT, +} testtype_t; + +static volatile testtype_t testtype; + +static void task1(void *arg) +{ + testtype_t oldtest; + char buf[100]; + int i = 0; + int x, r; + while (1) { + oldtest = testtype; + if (testtype == TST_MOSTLYFILLED || testtype == TST_MOSTLYEMPTY) { + for (x = 0; x < 10; x++) { + sprintf(buf, "This is test %d item %d.", (int)testtype, i++); + ets_printf("TSK w"); + xRingbufferPrintInfo(rb); + r = xRingbufferSend(rb, buf, strlen(buf) + 1, 2000 / portTICK_PERIOD_MS); + if (!r) { + printf("Test %d: Timeout on send!\n", (int)testtype); + } + if (testtype == TST_MOSTLYEMPTY) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + } + //Send NULL event to stop other side. + r = xRingbufferSend(rb, NULL, 0, 10000 / portTICK_PERIOD_MS); + } + while (oldtest == testtype) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + } +} + +static void task2(void *arg) +{ + testtype_t oldtest; + char *buf; + size_t len; + while (1) { + oldtest = testtype; + if (testtype == TST_MOSTLYFILLED || testtype == TST_MOSTLYEMPTY) { + while (1) { + ets_printf("TSK r"); + xRingbufferPrintInfo(rb); + buf = xRingbufferReceive(rb, &len, 2000 / portTICK_PERIOD_MS); + if (buf == NULL) { + printf("Test %d: Timeout on recv!\n", (int)testtype); + } else if (len == 0) { + printf("End packet received.\n"); + vRingbufferReturnItem(rb, buf); + break; + } else { + printf("Received: %s (%d bytes, %p)\n", buf, len, buf); + vRingbufferReturnItem(rb, buf); + } + if (testtype == TST_MOSTLYFILLED) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + } + } + while (oldtest == testtype) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + } +} + + + +static void uartIsrHdl(void *arg) +{ + char c; + char buf[50]; + char *item; + int r; + size_t len; + BaseType_t xHigherPriorityTaskWoken; + SET_PERI_REG_MASK(UART_INT_CLR_REG(0), UART_RXFIFO_FULL_INT_CLR); + while (READ_PERI_REG(UART_STATUS_REG(0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) { + c = READ_PERI_REG(UART_FIFO_REG(0)); + if (c == 'r') { + ets_printf("ISR r"); + xRingbufferPrintInfo(rb); + item = xRingbufferReceiveFromISR(rb, &len); + if (item == NULL) { + ets_printf("ISR recv fail!\n"); + } else if (len == 0) { + ets_printf("ISR recv NULL!\n"); + vRingbufferReturnItemFromISR(rb, item, &xHigherPriorityTaskWoken); + } else { + ets_printf("ISR recv '%s' (%d bytes, %p)\n", buf, len, buf); + vRingbufferReturnItemFromISR(rb, item, &xHigherPriorityTaskWoken); + } + } else { + sprintf(buf, "UART: %c", c); + ets_printf("ISR w"); + xRingbufferPrintInfo(rb); + r = xRingbufferSendFromISR(rb, buf, strlen(buf) + 1, &xHigherPriorityTaskWoken); + if (!r) { + ets_printf("ISR send fail\n"); + } + } + } + if (xHigherPriorityTaskWoken) { + portYIELD_FROM_ISR(); + } +} + +static void uartRxInit() +{ + uint32_t reg_val; + PIN_PULLUP_DIS(PERIPHS_IO_MUX_U0TXD_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + +// reg_val = READ_PERI_REG(UART_CONF1(0)); + reg_val = (1 << UART_RXFIFO_FULL_THRHD_S); + WRITE_PERI_REG(UART_CONF1_REG(0), reg_val); + CLEAR_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_TXFIFO_EMPTY_INT_ENA | UART_RXFIFO_TOUT_INT_ENA); + SET_PERI_REG_MASK(UART_INT_ENA_REG(0), UART_RXFIFO_FULL_INT_ENA); + + printf("Enabling int %d\n", ETS_UART0_INUM); + REG_SET_FIELD(DPORT_PRO_UART_INTR_MAP_REG, DPORT_PRO_UART_INTR_MAP, ETS_UART0_INUM); + REG_SET_FIELD(DPORT_PRO_UART1_INTR_MAP_REG, DPORT_PRO_UART1_INTR_MAP, ETS_UART0_INUM); + + xt_set_interrupt_handler(ETS_UART0_INUM, uartIsrHdl, NULL); + xt_ints_on(1 << ETS_UART0_INUM); + +} + +static void testRingbuffer(int type) +{ + TaskHandle_t th[2]; + int i; + rb = xRingbufferCreate(32 * 3, type); + + testtype = TST_MOSTLYFILLED; + + xTaskCreatePinnedToCore(task1 , "tskone" , 2048, NULL, 3, &th[0], 0); + xTaskCreatePinnedToCore(task2 , "tsktwo" , 2048, NULL, 3, &th[1], 0); + uartRxInit(); + + printf("Press 'r' to read an event in isr, any other key to write one.\n"); + printf("Test: mostlyfilled; putting 10 items in ringbuff ASAP, reading 1 a second\n"); + vTaskDelay(15000 / portTICK_PERIOD_MS); + printf("Test: mostlyempty; putting 10 items in ringbuff @ 1/sec, reading as fast as possible\n"); + testtype = TST_MOSTLYEMPTY; + vTaskDelay(15000 / portTICK_PERIOD_MS); + + //Shut down all the tasks + for (i = 0; i < 2; i++) { + vTaskDelete(th[i]); + } + xt_ints_off(1 << ETS_UART0_INUM); +} + +// TODO: split this thing into separate orthogonal tests +TEST_CASE("FreeRTOS ringbuffer test, no splitting items", "[freertos]") +{ + testRingbuffer(0); +} + +TEST_CASE("FreeRTOS ringbuffer test, w/ splitting items", "[freertos]") +{ + testRingbuffer(1); +} + diff --git a/components/freertos/test/test_tls_deletecb.c b/components/freertos/test/test_tls_deletecb.c new file mode 100644 index 0000000000..5277b761ab --- /dev/null +++ b/components/freertos/test/test_tls_deletecb.c @@ -0,0 +1,58 @@ +#include +#include +#include "rom/ets_sys.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" +#include "unity.h" +#include "soc/uart_reg.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" + + + +static void tskdelcb(int no, void *arg) +{ + printf("Delete callback: %d = %p!\n", no, arg); +} + + +static void tska(void *pvParameters) +{ + vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), 0, (void *)0xAAAAAAAA, tskdelcb); + while (1) { + vTaskDelay(10000000 / portTICK_PERIOD_MS); + } +} + +static void tskb(void *pvParameters) +{ + vTaskSetThreadLocalStoragePointerAndDelCallback(xTaskGetCurrentTaskHandle(), 0, (void *)0xBBBBBBBB, tskdelcb); + vTaskDelay(2000 / portTICK_PERIOD_MS); + TaskHandle_t a = (TaskHandle_t)pvParameters; + printf("Killing task A\n"); + vTaskDelete(a); + while (1) { + vTaskDelay(10000000 / portTICK_PERIOD_MS); + } +} + + +// TODO: split this thing into separate orthogonal tests +TEST_CASE("Freertos TLS delete cb", "[freertos]") +{ + TaskHandle_t a, b; + + xTaskCreatePinnedToCore(tska , "tska" , 2048, NULL, 3, &a, 0); + xTaskCreatePinnedToCore(tskb , "tska" , 2048, a, 3, &b, 0); + + // Let stuff run for 20s + vTaskDelay(5000 / portTICK_PERIOD_MS); + printf("Killing task B\n"); + //Shut down b + vTaskDelete(b); +} + diff --git a/components/mbedtls/test/component.mk b/components/mbedtls/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/mbedtls/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/mbedtls/test/test_mbedtls.c b/components/mbedtls/test/test_mbedtls.c new file mode 100644 index 0000000000..797c107efb --- /dev/null +++ b/components/mbedtls/test/test_mbedtls.c @@ -0,0 +1,134 @@ +/* mbedTLS internal tests wrapped into Unity + + Focus on testing functionality where we use ESP32 hardware + accelerated crypto features. + + See also test_hwcrypto.c +*/ +#include +#include +#include "mbedtls/sha1.h" +#include "mbedtls/sha256.h" +#include "mbedtls/sha512.h" +#include "mbedtls/aes.h" +#include "mbedtls/bignum.h" +#include "unity.h" + +static int mbedtls_alt_sha256_self_test( int verbose ); + +TEST_CASE("mbedtls SHA self-tests", "[mbedtls]") +{ + TEST_ASSERT_FALSE_MESSAGE(mbedtls_sha1_self_test(1), "SHA1 self-tests should pass."); + TEST_ASSERT_FALSE_MESSAGE(mbedtls_alt_sha256_self_test(1), "SHA256 self-tests should pass."); + TEST_ASSERT_FALSE_MESSAGE(mbedtls_sha512_self_test(1), "SHA512 self-tests should pass."); +} + +TEST_CASE("mbedtls AES self-tests", "[aes]") +{ + TEST_ASSERT_FALSE_MESSAGE(mbedtls_aes_self_test(1), "AES self-tests should pass."); +} + +TEST_CASE("mbedtls MPI self-tests", "[bignum]") +{ + TEST_ASSERT_FALSE_MESSAGE(mbedtls_mpi_self_test(1), "MPI self-tests should pass."); +} + + +/* Following code is a copy of the mbedtls_sha256 test vectors, + with the SHA-224 support removed as we don't currently support this hash. +*/ + +/* + * FIPS-180-2 test vectors + */ +static const unsigned char sha256_test_buf[3][57] = { + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const int sha256_test_buflen[3] = { + 3, 56, 1000 +}; + +static const unsigned char sha256_test_sum[6][32] = { + /* + * SHA-256 test vectors + */ + { + 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, + 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23, + 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, + 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD + }, + { + 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, + 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39, + 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, + 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 + }, + { + 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92, + 0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67, + 0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E, + 0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 + } +}; + +/* + * Checkup routine + */ +static int mbedtls_alt_sha256_self_test( int verbose ) +{ + int j, n, buflen, ret = 0; + unsigned char buf[1024]; + unsigned char sha256sum[32]; + mbedtls_sha256_context ctx; + + for ( j = 0; j < 3; j++ ) { + mbedtls_sha256_init( &ctx ); + + if ( verbose != 0 ) { + printf( " SHA-%d test #%d: ", 256, j + 1 ); + } + + mbedtls_sha256_starts( &ctx, 0 ); + + if ( j == 2 ) { + memset( buf, 'a', buflen = 1000 ); + + for ( n = 0; n < 1000; n++ ) { + mbedtls_sha256_update( &ctx, buf, buflen ); + } + } else + mbedtls_sha256_update( &ctx, sha256_test_buf[j], + sha256_test_buflen[j] ); + + mbedtls_sha256_finish( &ctx, sha256sum ); + + if ( memcmp( sha256sum, sha256_test_sum[j], 32 ) != 0 ) { + if ( verbose != 0 ) { + printf( "failed\n" ); + } + + mbedtls_sha256_free( &ctx ); + + ret = 1; + goto exit; + } + + if ( verbose != 0 ) { + printf( "passed\n" ); + } + + mbedtls_sha256_free( &ctx ); + } + + if ( verbose != 0 ) { + printf( "\n" ); + } + +exit: + + return ( ret ); +} diff --git a/components/newlib/test/component.mk b/components/newlib/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/newlib/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/newlib/test/test_newlib.c b/components/newlib/test/test_newlib.c new file mode 100644 index 0000000000..b498b4bd43 --- /dev/null +++ b/components/newlib/test/test_newlib.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include "unity.h" + + +TEST_CASE("test ctype functions", "[newlib]") +{ + TEST_ASSERT_TRUE( isalnum('a') && isalnum('A') && isalnum('z') && isalnum('Z') && isalnum('0') && isalnum('9') ); + TEST_ASSERT_FALSE( isalnum('(') || isalnum('-') || isalnum(' ') || isalnum('\x81') || isalnum('.') || isalnum('\\') ); + TEST_ASSERT_TRUE( isalpha('a') && isalpha('A') && isalpha('z') && isalpha('Z') ); + TEST_ASSERT_FALSE( isalpha('0') || isalpha('9') || isalpha(')') || isalpha('\t') || isalpha(' ') || isalpha('\x81') ); + TEST_ASSERT_TRUE( isspace(' ') && isspace('\t') && isspace('\n') && isspace('\r') ); + TEST_ASSERT_FALSE( isspace('0') || isspace('9') || isspace(')') || isspace('A') || isspace('*') || isspace('\x81') || isspace('a')); +} + +TEST_CASE("test atoX functions", "[newlib]") +{ + TEST_ASSERT_EQUAL_INT(-2147483648, atoi("-2147483648")); + TEST_ASSERT_EQUAL_INT(2147483647, atoi("2147483647")); + TEST_ASSERT_EQUAL_INT(42, atoi("000000042")); + TEST_ASSERT_EQUAL_INT(0, strtol("foo", NULL, 10)); +} + +TEST_CASE("test sprintf function", "[newlib]") +{ + char *res = NULL; + asprintf(&res, "%d %011i %lu %p %x %c %.4f\n", 42, 2147483647, 2147483648UL, (void *) 0x40010000, 0x40020000, 'Q', 1.0f / 137.0f); + TEST_ASSERT_NOT_NULL(res); + TEST_ASSERT_EQUAL_STRING(res, "42 02147483647 2147483648 0x40010000 40020000 Q 0.0073\n"); + free(res); +} + +TEST_CASE("test sscanf function", "[newlib]") +{ + const char *src = "42 02147483647 2147483648 0x40010000 40020000 Q 0.0073\n"; + int fourty_two; + int int_max; + unsigned long int_max_plus_one; + void *iram_ptr; + int irom_ptr; + char department; + float inv_fine_structure_constant; + int res = sscanf(src, "%d %d %lu %p %x %c %f", &fourty_two, &int_max, &int_max_plus_one, &iram_ptr, &irom_ptr, &department, &inv_fine_structure_constant); + TEST_ASSERT_EQUAL(7, res); + TEST_ASSERT_EQUAL(42, fourty_two); + TEST_ASSERT_EQUAL(2147483647, int_max); + TEST_ASSERT_EQUAL_UINT32(2147483648UL, int_max_plus_one); + TEST_ASSERT_EQUAL(0x40010000, iram_ptr); + TEST_ASSERT_EQUAL(0x40020000, irom_ptr); + TEST_ASSERT_EQUAL('Q', department); + TEST_ASSERT_TRUE(1.0f / inv_fine_structure_constant > 136 && 1.0f / inv_fine_structure_constant < 138); +} + +TEST_CASE("test time functions", "[newlib]") +{ + time_t now = 1464248488; + setenv("TZ", "UTC-8", 1); + struct tm *tm_utc = gmtime(&now); + TEST_ASSERT_EQUAL( 28, tm_utc->tm_sec); + TEST_ASSERT_EQUAL( 41, tm_utc->tm_min); + TEST_ASSERT_EQUAL( 7, tm_utc->tm_hour); + TEST_ASSERT_EQUAL( 26, tm_utc->tm_mday); + TEST_ASSERT_EQUAL( 4, tm_utc->tm_mon); + TEST_ASSERT_EQUAL(116, tm_utc->tm_year); + TEST_ASSERT_EQUAL( 4, tm_utc->tm_wday); + TEST_ASSERT_EQUAL(146, tm_utc->tm_yday); + + struct tm *tm_local = localtime(&now); + TEST_ASSERT_EQUAL( 28, tm_local->tm_sec); + TEST_ASSERT_EQUAL( 41, tm_local->tm_min); + TEST_ASSERT_EQUAL( 15, tm_local->tm_hour); + TEST_ASSERT_EQUAL( 26, tm_local->tm_mday); + TEST_ASSERT_EQUAL( 4, tm_local->tm_mon); + TEST_ASSERT_EQUAL(116, tm_local->tm_year); + TEST_ASSERT_EQUAL( 4, tm_local->tm_wday); + TEST_ASSERT_EQUAL(146, tm_local->tm_yday); + +} + + +static int checkFnRom(void *fn, char *name) +{ + int fnaddr = (int)fn; + printf("%s: 0X%x\n", name, fnaddr); + if ((fnaddr >= 0x40000000) && (fnaddr < 0x40070000)) { + return 1; + } else { + return 0; + } +} + + +TEST_CASE("check if ROM is used for functions", "[newlib]") +{ + TEST_ASSERT(checkFnRom(printf, "printf")); + TEST_ASSERT(checkFnRom(sscanf, "sscanf")); + TEST_ASSERT(checkFnRom(atoi, "atoi")); + TEST_ASSERT(checkFnRom(strtol, "strtol")); +} \ No newline at end of file diff --git a/components/nvs_flash/test/component.mk b/components/nvs_flash/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/nvs_flash/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/nvs_flash/test/test_nvs.c b/components/nvs_flash/test/test_nvs.c new file mode 100644 index 0000000000..c523026432 --- /dev/null +++ b/components/nvs_flash/test/test_nvs.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include "unity.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "esp_spi_flash.h" +#include + +#define NVS_FLASH_SECTOR 6 +#define NVS_FLASH_SECTOR_COUNT_MIN 3 +#define NVS_FLASH_SECTOR_COUNT_MAX 10 + +TEST_CASE("various nvs tests", "[nvs]") +{ + nvs_handle handle_1; + TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED); + for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { + spi_flash_erase_sector(i); + } + TEST_ESP_OK(nvs_flash_init_custom(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + + TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); + + // TEST_ESP_ERR(nvs_set_i32(handle_1, "foo", 0x12345678), ESP_ERR_NVS_READ_ONLY); + // nvs_close(handle_1); + + TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1)); + TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678)); + TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789)); + + nvs_handle handle_2; + TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2)); + TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a)); + const char *str = "value 0123456789abcdef0123456789abcdef"; + TEST_ESP_OK(nvs_set_str(handle_2, "key", str)); + + int32_t v1; + TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1)); + TEST_ASSERT_EQUAL_INT32(0x23456789, v1); + + int32_t v2; + TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2)); + TEST_ASSERT_EQUAL_INT32(0x3456789a, v2); + + char buf[strlen(str) + 1]; + size_t buf_len = sizeof(buf); + + TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len)); + + TEST_ASSERT_EQUAL_INT32(0, strcmp(buf, str)); +} diff --git a/components/nvs_flash/test/Makefile b/components/nvs_flash/test_nvs_host/Makefile similarity index 100% rename from components/nvs_flash/test/Makefile rename to components/nvs_flash/test_nvs_host/Makefile diff --git a/components/nvs_flash/test/catch.hpp b/components/nvs_flash/test_nvs_host/catch.hpp similarity index 100% rename from components/nvs_flash/test/catch.hpp rename to components/nvs_flash/test_nvs_host/catch.hpp diff --git a/components/nvs_flash/test/crc.cpp b/components/nvs_flash/test_nvs_host/crc.cpp similarity index 100% rename from components/nvs_flash/test/crc.cpp rename to components/nvs_flash/test_nvs_host/crc.cpp diff --git a/components/nvs_flash/test/crc.h b/components/nvs_flash/test_nvs_host/crc.h similarity index 100% rename from components/nvs_flash/test/crc.h rename to components/nvs_flash/test_nvs_host/crc.h diff --git a/components/nvs_flash/test/main.cpp b/components/nvs_flash/test_nvs_host/main.cpp similarity index 100% rename from components/nvs_flash/test/main.cpp rename to components/nvs_flash/test_nvs_host/main.cpp diff --git a/components/nvs_flash/test/sdkconfig.h b/components/nvs_flash/test_nvs_host/sdkconfig.h similarity index 100% rename from components/nvs_flash/test/sdkconfig.h rename to components/nvs_flash/test_nvs_host/sdkconfig.h diff --git a/components/nvs_flash/test/spi_flash_emulation.cpp b/components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp similarity index 100% rename from components/nvs_flash/test/spi_flash_emulation.cpp rename to components/nvs_flash/test_nvs_host/spi_flash_emulation.cpp diff --git a/components/nvs_flash/test/spi_flash_emulation.h b/components/nvs_flash/test_nvs_host/spi_flash_emulation.h similarity index 100% rename from components/nvs_flash/test/spi_flash_emulation.h rename to components/nvs_flash/test_nvs_host/spi_flash_emulation.h diff --git a/components/nvs_flash/test/test_compressed_enum_table.cpp b/components/nvs_flash/test_nvs_host/test_compressed_enum_table.cpp similarity index 100% rename from components/nvs_flash/test/test_compressed_enum_table.cpp rename to components/nvs_flash/test_nvs_host/test_compressed_enum_table.cpp diff --git a/components/nvs_flash/test/test_intrusive_list.cpp b/components/nvs_flash/test_nvs_host/test_intrusive_list.cpp similarity index 100% rename from components/nvs_flash/test/test_intrusive_list.cpp rename to components/nvs_flash/test_nvs_host/test_intrusive_list.cpp diff --git a/components/nvs_flash/test/test_nvs.cpp b/components/nvs_flash/test_nvs_host/test_nvs.cpp similarity index 100% rename from components/nvs_flash/test/test_nvs.cpp rename to components/nvs_flash/test_nvs_host/test_nvs.cpp diff --git a/components/nvs_flash/test/test_spi_flash_emulation.cpp b/components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp similarity index 100% rename from components/nvs_flash/test/test_spi_flash_emulation.cpp rename to components/nvs_flash/test_nvs_host/test_spi_flash_emulation.cpp diff --git a/components/partition_table/test/component.mk b/components/partition_table/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/partition_table/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/partition_table/test/test_partition.c b/components/partition_table/test/test_partition.c new file mode 100644 index 0000000000..a4288d8e1d --- /dev/null +++ b/components/partition_table/test/test_partition.c @@ -0,0 +1,95 @@ +#include +#include +#include "unity.h" +#include "esp_partition.h" + + +TEST_CASE("Can read partition table", "[partition]") +{ + + const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL); + TEST_ASSERT_NOT_NULL(p); + TEST_ASSERT_EQUAL(p->address, 0x10000); + TEST_ASSERT_EQUAL(p->subtype, ESP_PARTITION_SUBTYPE_APP_FACTORY); + + esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); + TEST_ASSERT_NOT_NULL(it); + int count = 0; + for (; it != NULL; it = esp_partition_next(it)) { + const esp_partition_t *p = esp_partition_get(it); + TEST_ASSERT_NOT_NULL(p); + ++count; + } + esp_partition_iterator_release(it); + TEST_ASSERT_EQUAL(count, 2); + + printf("%d\n", __builtin_clz(count)); +} + +TEST_CASE("Can write, read, mmap partition", "[partition]") +{ + const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); + TEST_ASSERT_NOT_NULL(p); + const size_t max_size = 2 * SPI_FLASH_SEC_SIZE; + uint8_t *data = (uint8_t *) malloc(max_size); + TEST_ASSERT_NOT_NULL(data); + + TEST_ASSERT_EQUAL(ESP_OK, esp_partition_erase_range(p, 0, p->size)); + + srand(0); + size_t block_size; + for (size_t offset = 0; offset < p->size; offset += block_size) { + block_size = ((rand() + 4) % max_size) & (~0x3); + size_t left = p->size - offset; + if (block_size > left) { + block_size = left; + } + for (size_t i = 0; i < block_size / 4; ++i) { + ((uint32_t *) (data))[i] = rand(); + if (i == 0 && offset == 0) { + printf("write: %08x\n", ((uint32_t *) (data))[i]); + } + } + TEST_ASSERT_EQUAL(ESP_OK, esp_partition_write(p, offset, data, block_size)); + } + + srand(0); + for (size_t offset = 0; offset < p->size; offset += block_size) { + block_size = ((rand() + 4) % max_size) & (~0x3); + size_t left = p->size - offset; + if (block_size > left) { + block_size = left; + } + TEST_ASSERT_EQUAL(ESP_OK, esp_partition_read(p, offset, data, block_size)); + for (size_t i = 0; i < block_size / 4; ++i) { + TEST_ASSERT_EQUAL(rand(), ((uint32_t *) data)[i]); + } + } + + free(data); + + const uint32_t *mmap_data; + spi_flash_mmap_handle_t mmap_handle; + size_t begin = 3000; + size_t size = 12000; + TEST_ASSERT_EQUAL(ESP_OK, esp_partition_mmap(p, begin, size, SPI_FLASH_MMAP_DATA, + (const void **)&mmap_data, &mmap_handle)); + srand(0); + for (size_t offset = 0; offset < p->size; offset += block_size) { + block_size = ((rand() + 4) % max_size) & (~0x3); + size_t left = p->size - offset; + if (block_size > left) { + block_size = left; + } + for (size_t i = 0; i < block_size / 4; ++i) { + size_t pos = offset + i * 4; + uint32_t expected = rand(); + if (pos < begin || pos >= (begin + size)) { + continue; + } + TEST_ASSERT_EQUAL(expected, mmap_data[(pos - begin) / 4]); + } + } + + spi_flash_munmap(mmap_handle); +} diff --git a/components/spi_flash/test/component.mk b/components/spi_flash/test/component.mk new file mode 100644 index 0000000000..5dd172bdb7 --- /dev/null +++ b/components/spi_flash/test/component.mk @@ -0,0 +1,5 @@ +# +#Component Makefile +# + +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/spi_flash/test/test_mmap.c b/components/spi_flash/test/test_mmap.c new file mode 100644 index 0000000000..28d9ae6dda --- /dev/null +++ b/components/spi_flash/test/test_mmap.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +uint32_t buffer[1024]; + +static const uint32_t start = 0x200000; +static const uint32_t end = 0x300000; + + + +TEST_CASE("Prepare data for mmap tests", "[mmap]") +{ + srand(0); + for (int block = start / 0x10000; block < end / 0x10000; ++block) { + printf("Writing block %d\n", block); + for (int sector = 0; sector < 16; ++sector) { + for (uint32_t word = 0; word < 1024; ++word) { + uint32_t val = rand(); + if (block == start / 0x10000 && sector == 0 && word == 0) { + printf("first word: %08x\n", val); + } + buffer[word] = val; + } + uint32_t abs_sector = (block) * 16 + sector; + printf("Writing sector %d\n", abs_sector); + ESP_ERROR_CHECK( spi_flash_erase_sector((uint16_t) abs_sector) ); + ESP_ERROR_CHECK( spi_flash_write(abs_sector * SPI_FLASH_SEC_SIZE, (const uint8_t *) buffer, sizeof(buffer)) ); + } + } +} + +TEST_CASE("Can mmap into data address space", "[mmap]") +{ + + printf("Mapping %x (+%x)\n", start, end - start); + spi_flash_mmap_handle_t handle1; + const void *ptr1; + ESP_ERROR_CHECK( spi_flash_mmap(start, end - start, SPI_FLASH_MMAP_DATA, &ptr1, &handle1) ); + printf("mmap_res: handle=%d ptr=%p\n", handle1, ptr1); + + spi_flash_mmap_dump(); + + srand(0); + const uint32_t *data = (const uint32_t *) ptr1; + for (int block = 0; block < (end - start) / 0x10000; ++block) { + for (int sector = 0; sector < 16; ++sector) { + for (uint32_t word = 0; word < 1024; ++word) { + TEST_ASSERT_EQUAL_UINT32(rand(), data[(block * 16 + sector) * 1024 + word]); + } + } + } + printf("Mapping %x (+%x)\n", start - 0x10000, 0x20000); + spi_flash_mmap_handle_t handle2; + const void *ptr2; + ESP_ERROR_CHECK( spi_flash_mmap(start - 0x10000, 0x20000, SPI_FLASH_MMAP_DATA, &ptr2, &handle2) ); + printf("mmap_res: handle=%d ptr=%p\n", handle2, ptr2); + spi_flash_mmap_dump(); + + printf("Mapping %x (+%x)\n", start, 0x10000); + spi_flash_mmap_handle_t handle3; + const void *ptr3; + ESP_ERROR_CHECK( spi_flash_mmap(start, 0x10000, SPI_FLASH_MMAP_DATA, &ptr3, &handle3) ); + printf("mmap_res: handle=%d ptr=%p\n", handle3, ptr3); + spi_flash_mmap_dump(); + + printf("Unmapping handle1\n"); + spi_flash_munmap(handle1); + spi_flash_mmap_dump(); + + printf("Unmapping handle2\n"); + spi_flash_munmap(handle2); + spi_flash_mmap_dump(); + + printf("Unmapping handle3\n"); + spi_flash_munmap(handle3); +} diff --git a/components/spi_flash/test/test_spi_flash.c b/components/spi_flash/test/test_spi_flash.c new file mode 100644 index 0000000000..330e37bb82 --- /dev/null +++ b/components/spi_flash/test/test_spi_flash.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +#include +#include +#include + +struct flash_test_ctx { + uint32_t offset[2]; + bool fail[2]; + SemaphoreHandle_t done; +}; + +static void flash_test_task(void *arg) +{ + const uint32_t coreid = xPortGetCoreID(); + ets_printf("t%d\n", coreid); + struct flash_test_ctx *ctx = (struct flash_test_ctx *) arg; + vTaskDelay(100 / portTICK_PERIOD_MS); + const uint32_t sector = ctx->offset[coreid]; + ets_printf("es%d\n", coreid); + if (spi_flash_erase_sector(sector) != ESP_OK) { + ctx->fail[coreid] = true; + ets_printf("Erase failed\r\n"); + xSemaphoreGive(ctx->done); + vTaskDelete(NULL); + } + ets_printf("ed%d\n", coreid); + + vTaskDelay(0 / portTICK_PERIOD_MS); + + uint32_t val = 0xabcd1234; + const uint32_t n = 4096; + for (uint32_t offset = 0; offset < n; offset += 4) { + if (spi_flash_write(sector * SPI_FLASH_SEC_SIZE + offset, (const uint8_t *) &val, 4) != ESP_OK) { + ets_printf("Write failed at offset=%d\r\n", offset); + ctx->fail[coreid] = true; + break; + } + } + ets_printf("wd%d\n", coreid); + + vTaskDelay(0 / portTICK_PERIOD_MS); + + uint32_t val_read; + for (uint32_t offset = 0; offset < n; offset += 4) { + if (spi_flash_read(sector * SPI_FLASH_SEC_SIZE + offset, (uint8_t *) &val_read, 4) != ESP_OK) { + ets_printf("Read failed at offset=%d\r\n", offset); + ctx->fail[coreid] = true; + break; + } + if (val_read != val) { + ets_printf("Read invalid value=%08x at offset=%d\r\n", val_read, offset); + ctx->fail[coreid] = true; + break; + } + } + ets_printf("td%d\n", coreid); + xSemaphoreGive(ctx->done); + vTaskDelete(NULL); +} + +TEST_CASE("flash write and erase work both on PRO CPU and on APP CPU", "[spi_flash]") +{ + TaskHandle_t procpu_task; + TaskHandle_t appcpu_task; + struct flash_test_ctx ctx; + + ctx.offset[0] = 6; + ctx.offset[1] = 7; + ctx.fail[0] = 0; + ctx.fail[1] = 0; + ctx.done = xSemaphoreCreateBinary(); + + xTaskCreatePinnedToCore(flash_test_task, "1", 2048, &ctx, 3, &procpu_task, 0); + if (portNUM_PROCESSORS == 2) { + xTaskCreatePinnedToCore(flash_test_task, "2", 2048, &ctx, 3, &appcpu_task, 1); + } + + xSemaphoreTake(ctx.done, portMAX_DELAY); + if (portNUM_PROCESSORS == 2) { + xSemaphoreTake(ctx.done, portMAX_DELAY); + } + + TEST_ASSERT_EQUAL(false, ctx.fail[0]); + if (portNUM_PROCESSORS == 2) { + TEST_ASSERT_EQUAL(false, ctx.fail[1]); + } +} + diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 3018c18b55..16c9c02b5f 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -32,9 +32,6 @@ include $(IDF_PATH)/make/common.mk # Some of the following defaults may be overriden by the component's component.mk makefile, # during the next step: -# Name of the component -COMPONENT_NAME := $(lastword $(subst /, ,$(realpath $(COMPONENT_PATH)))) - # Absolute path of the .a file COMPONENT_LIBRARY = lib$(COMPONENT_NAME).a diff --git a/make/project.mk b/make/project.mk index 870db55f97..0dfce4368a 100644 --- a/make/project.mk +++ b/make/project.mk @@ -94,6 +94,12 @@ COMPONENT_PATHS += $(abspath $(SRCDIRS)) # A component is buildable if it has a component.mk makefile in it COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(cp)/component.mk),$(cp))) +# If TEST_COMPONENTS is set on the command line, create variables for building unit tests +ifdef TEST_COMPONENTS +TEST_COMPONENT_PATHS := $(foreach comp,$(TEST_COMPONENTS),$(IDF_PATH)/components/$(comp)/test) +TEST_COMPONENT_NAMES := $(foreach comp,$(TEST_COMPONENTS),$(comp)_test) +endif + # Initialise project-wide variables which can be added to by # each component. # @@ -113,7 +119,7 @@ COMPONENT_SUBMODULES := # dependencies. # # See the component_project_vars.mk target in component_wrapper.mk -COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE))) +COMPONENT_PROJECT_VARS := $(addsuffix /component_project_vars.mk,$(notdir $(COMPONENT_PATHS_BUILDABLE) ) $(TEST_COMPONENT_NAMES)) COMPONENT_PROJECT_VARS := $(addprefix $(BUILD_DIR_BASE)/,$(COMPONENT_PROJECT_VARS)) # this line is -include instead of include to prevent a spurious error message on make 3.81 -include $(COMPONENT_PROJECT_VARS) @@ -140,7 +146,7 @@ endif LDFLAGS ?= -nostdlib \ -L$(IDF_PATH)/lib \ -L$(IDF_PATH)/ld \ - $(addprefix -L$(BUILD_DIR_BASE)/,$(COMPONENTS) $(SRCDIRS)) \ + $(addprefix -L$(BUILD_DIR_BASE)/,$(COMPONENTS) $(TEST_COMPONENT_NAMES) $(SRCDIRS) ) \ -u call_user_start_cpu0 \ $(EXTRA_LDFLAGS) \ -Wl,--gc-sections \ @@ -257,7 +263,7 @@ endif # A "component" library is any library in the LDFLAGS where # the name of the library is also a name of the component APP_LIBRARIES = $(patsubst -l%,%,$(filter -l%,$(LDFLAGS))) -COMPONENT_LIBRARIES = $(filter $(notdir $(COMPONENT_PATHS_BUILDABLE)),$(APP_LIBRARIES)) +COMPONENT_LIBRARIES = $(filter $(notdir $(COMPONENT_PATHS_BUILDABLE)) $(TEST_COMPONENT_NAMES),$(APP_LIBRARIES)) # ELF depends on the library archive files for COMPONENT_LIBRARIES # the rules to build these are emitted as part of GenerateComponentTarget below @@ -282,7 +288,7 @@ $(BUILD_DIR_BASE): # # Is recursively expanded by the GenerateComponentTargets macro define ComponentMake -+$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk ++$(MAKE) -C $(BUILD_DIR_BASE)/$(2) -f $(IDF_PATH)/make/component_wrapper.mk COMPONENT_MAKEFILE=$(1)/component.mk COMPONENT_NAME=$(2) endef # Generate top-level component-specific targets for each component @@ -325,6 +331,7 @@ $(BUILD_DIR_BASE)/$(2)/component_project_vars.mk: $(1)/component.mk $(COMMON_MAK endef $(foreach component,$(COMPONENT_PATHS_BUILDABLE),$(eval $(call GenerateComponentTargets,$(component),$(notdir $(component))))) +$(foreach component,$(TEST_COMPONENT_PATHS),$(eval $(call GenerateComponentTargets,$(component),$(lastword $(subst /, ,$(dir $(component))))_test))) app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) diff --git a/tools/unit-test-app/Makefile b/tools/unit-test-app/Makefile new file mode 100644 index 0000000000..128e8a0804 --- /dev/null +++ b/tools/unit-test-app/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := unit-test-app + +include $(IDF_PATH)/make/project.mk + diff --git a/tools/unit-test-app/README.md b/tools/unit-test-app/README.md new file mode 100644 index 0000000000..cf45ce287b --- /dev/null +++ b/tools/unit-test-app/README.md @@ -0,0 +1,12 @@ +# Unit Test App + +ESP-IDF unit tests are run using Unit Test App. The app can be built with the unit tests for a specific component. Unit tests are in `test` subdirectories of respective components. + +# Building Unit Test App + +* Follow the setup instructions in the top-level esp-idf README. +* Set IDF_PATH environment variable to point to the path to the esp-idf top-level directory. +* Change into `tools/unit-test-app` directory +* `make menuconfig` to configure the Unit Test App. +* `make TEST_COMPONENTS=` with `TEST_COMPONENTS` set to names of the components to be included in the test app. +* Follow the printed instructions to flash, or run `make flash`. diff --git a/tools/unit-test-app/components/unity/component.mk b/tools/unit-test-app/components/unity/component.mk new file mode 100644 index 0000000000..ebd7a7d59b --- /dev/null +++ b/tools/unit-test-app/components/unity/component.mk @@ -0,0 +1,3 @@ +# +# Component Makefile +# diff --git a/tools/unit-test-app/components/unity/include/unity.h b/tools/unit-test-app/components/unity/include/unity.h new file mode 100644 index 0000000000..3ffc14c0b8 --- /dev/null +++ b/tools/unit-test-app/components/unity/include/unity.h @@ -0,0 +1,292 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_FRAMEWORK_H +#define UNITY_FRAMEWORK_H +#define UNITY + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define UNITY_INCLUDE_CONFIG_H +#include "unity_internals.h" + +void setUp(void); +void tearDown(void); + +/*------------------------------------------------------- + * Configuration Options + *------------------------------------------------------- + * All options described below should be passed as a compiler flag to all files using Unity. If you must add #defines, place them BEFORE the #include above. + + * Integers/longs/pointers + * - Unity attempts to automatically discover your integer sizes + * - define UNITY_EXCLUDE_STDINT_H to stop attempting to look in + * - define UNITY_EXCLUDE_LIMITS_H to stop attempting to look in + * - define UNITY_EXCLUDE_SIZEOF to stop attempting to use sizeof in macros + * - If you cannot use the automatic methods above, you can force Unity by using these options: + * - define UNITY_SUPPORT_64 + * - define UNITY_INT_WIDTH + * - UNITY_LONG_WIDTH + * - UNITY_POINTER_WIDTH + + * Floats + * - define UNITY_EXCLUDE_FLOAT to disallow floating point comparisons + * - define UNITY_FLOAT_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_FLOAT + * - define UNITY_FLOAT_TYPE to specify doubles instead of single precision floats + * - define UNITY_FLOAT_VERBOSE to print floating point values in errors (uses sprintf) + * - define UNITY_INCLUDE_DOUBLE to allow double floating point comparisons + * - define UNITY_EXCLUDE_DOUBLE to disallow double floating point comparisons (default) + * - define UNITY_DOUBLE_PRECISION to specify the precision to use when doing TEST_ASSERT_EQUAL_DOUBLE + * - define UNITY_DOUBLE_TYPE to specify something other than double + * - define UNITY_DOUBLE_VERBOSE to print floating point values in errors (uses sprintf) + * - define UNITY_VERBOSE_NUMBER_MAX_LENGTH to change maximum length of printed numbers (used by sprintf) + + * Output + * - by default, Unity prints to standard out with putchar. define UNITY_OUTPUT_CHAR(a) with a different function if desired + * - define UNITY_DIFFERENTIATE_FINAL_FAIL to print FAILED (vs. FAIL) at test end summary - for automated search for failure + + * Optimization + * - by default, line numbers are stored in unsigned shorts. Define UNITY_LINE_TYPE with a different type if your files are huge + * - by default, test and failure counters are unsigned shorts. Define UNITY_COUNTER_TYPE with a different type if you want to save space or have more than 65535 Tests. + + * Test Cases + * - define UNITY_SUPPORT_TEST_CASES to include the TEST_CASE macro, though really it's mostly about the runner generator script + + * Parameterized Tests + * - you'll want to create a define of TEST_CASE(...) which basically evaluates to nothing + + *------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define TEST_FAIL_MESSAGE(message) UNITY_TEST_FAIL(__LINE__, (message)) +#define TEST_FAIL() UNITY_TEST_FAIL(__LINE__, NULL) +#define TEST_IGNORE_MESSAGE(message) UNITY_TEST_IGNORE(__LINE__, (message)) +#define TEST_IGNORE() UNITY_TEST_IGNORE(__LINE__, NULL) +#define TEST_ONLY() + +/* It is not necessary for you to call PASS. A PASS condition is assumed if nothing fails. + * This method allows you to abort a test immediately with a PASS state, ignoring the remainder of the test. */ +#define TEST_PASS() longjmp(Unity.AbortFrame, 1) + +/*------------------------------------------------------- + * Test Asserts (simple) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expression Evaluated To FALSE") +#define TEST_ASSERT_TRUE(condition) UNITY_TEST_ASSERT( (condition), __LINE__, " Expected TRUE Was FALSE") +#define TEST_ASSERT_UNLESS(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expression Evaluated To TRUE") +#define TEST_ASSERT_FALSE(condition) UNITY_TEST_ASSERT( !(condition), __LINE__, " Expected FALSE Was TRUE") +#define TEST_ASSERT_NULL(pointer) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, " Expected NULL") +#define TEST_ASSERT_NOT_NULL(pointer) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, " Expected Non-NULL") + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL(expected, actual) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_NOT_EQUAL(expected, actual) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, " Expected Not-Equal") +#define TEST_ASSERT_EQUAL_UINT(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64(expected, actual) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64(expected, actual) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS(mask, expected, actual) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_HIGH(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (_UU32)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BITS_LOW(mask, actual) UNITY_TEST_ASSERT_BITS((mask), (_UU32)(0), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((_UU32)1 << (bit)), (_UU32)(-1), (actual), __LINE__, NULL) +#define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((_UU32)1 << (bit)), (_UU32)(0), (actual), __LINE__, NULL) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_INT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_UINT64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX16_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX32_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_HEX64_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, NULL) + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR(expected, actual) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING(expected, actual) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY(expected, actual, len) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, NULL) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, NULL) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT(expected, actual) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE(expected, actual) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, NULL) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, NULL) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, NULL) + +/*------------------------------------------------------- + * Test Asserts (with additional messages) + *-------------------------------------------------------*/ + +/* Boolean */ +#define TEST_ASSERT_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_TRUE_MESSAGE(condition, message) UNITY_TEST_ASSERT( (condition), __LINE__, (message)) +#define TEST_ASSERT_UNLESS_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_FALSE_MESSAGE(condition, message) UNITY_TEST_ASSERT( !(condition), __LINE__, (message)) +#define TEST_ASSERT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NULL( (pointer), __LINE__, (message)) +#define TEST_ASSERT_NOT_NULL_MESSAGE(pointer, message) UNITY_TEST_ASSERT_NOT_NULL((pointer), __LINE__, (message)) + +/* Integers (of all sizes) */ +#define TEST_ASSERT_EQUAL_INT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT8((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_INT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_NOT_EQUAL_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT(((expected) != (actual)), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT16( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT32( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_UINT64( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX8( (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX16((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX32((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_HEX64((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_MESSAGE(mask, expected, actual, message) UNITY_TEST_ASSERT_BITS((mask), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_HIGH_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (_UU32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BITS_LOW_MESSAGE(mask, actual, message) UNITY_TEST_ASSERT_BITS((mask), (_UU32)(0), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((_UU32)1 << (bit)), (_UU32)(-1), (actual), __LINE__, (message)) +#define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((_UU32)1 << (bit)), (_UU32)(0), (actual), __LINE__, (message)) + +/* Integer Ranges (of all sizes) */ +#define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_INT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_UINT64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_UINT64_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX8_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX16_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX16_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX32_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX32_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_HEX64_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_HEX64_WITHIN((delta), (expected), (actual), __LINE__, (message)) + +/* Structs and Strings */ +#define TEST_ASSERT_EQUAL_PTR_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_PTR((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_STRING((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_STRING_LEN((expected), (actual), (len), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_MESSAGE(expected, actual, len, message) UNITY_TEST_ASSERT_EQUAL_MEMORY((expected), (actual), (len), __LINE__, (message)) + +/* Arrays */ +#define TEST_ASSERT_EQUAL_INT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_INT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_UINT64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX8_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX16_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX32_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_HEX64_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_PTR_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_STRING_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_MEMORY_ARRAY_MESSAGE(expected, actual, len, num_elements, message) UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY((expected), (actual), (len), (num_elements), __LINE__, (message)) + +/* Floating Point (If Enabled) */ +#define TEST_ASSERT_FLOAT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_FLOAT((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_FLOAT_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* Double (If Enabled) */ +#define TEST_ASSERT_DOUBLE_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((delta), (expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_MESSAGE(expected, actual, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE((expected), (actual), __LINE__, (message)) +#define TEST_ASSERT_EQUAL_DOUBLE_ARRAY_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) +#define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/* end of UNITY_FRAMEWORK_H */ +#ifdef __cplusplus +} +#endif +#endif diff --git a/tools/unit-test-app/components/unity/include/unity_config.h b/tools/unit-test-app/components/unity/include/unity_config.h new file mode 100644 index 0000000000..b8e56a79c1 --- /dev/null +++ b/tools/unit-test-app/components/unity/include/unity_config.h @@ -0,0 +1,75 @@ +#ifndef UNITY_CONFIG_H +#define UNITY_CONFIG_H + +// This file gets included from unity.h via unity_internals.h +// It is inside #ifdef __cplusplus / extern "C" block, so we can +// only use C features here + +// Adapt Unity to our environment, disable FP support + +#include + +#define UNITY_EXCLUDE_FLOAT +#define UNITY_EXCLUDE_DOUBLE + +#define UNITY_OUTPUT_CHAR unity_putc +#define UNITY_OUTPUT_FLUSH unity_flush + +// Define helpers to register test cases from multiple files + +#define UNITY_EXPAND2(a, b) a ## b +#define UNITY_EXPAND(a, b) UNITY_EXPAND2(a, b) +#define UNITY_TEST_UID(what) UNITY_EXPAND(what, __LINE__) + +#define UNITY_TEST_REG_HELPER reg_helper ## UNITY_TEST_UID +#define UNITY_TEST_DESC_UID desc ## UNITY_TEST_UID +struct test_desc_t +{ + const char* name; + const char* desc; + void (*fn)(void); + const char* file; + int line; + struct test_desc_t* next; +}; + +void unity_testcase_register(struct test_desc_t* desc); + +void unity_run_menu(); + +void unity_run_tests_with_filter(const char* filter); + +void unity_run_all_tests(); + +/* Test case macro, a-la CATCH framework. + First argument is a free-form description, + second argument is (by convention) a list of identifiers, each one in square brackets. + Identifiers are used to group related tests, or tests with specific properties. + Use like: + + TEST_CASE("Frobnicator forbnicates", "[frobnicator][rom]") + { + // test goes here + } +*/ +#define TEST_CASE(name_, desc_) \ + static void UNITY_TEST_UID(test_func_) (void); \ + static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \ + { \ + static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \ + .name = name_, \ + .desc = desc_, \ + .fn = &UNITY_TEST_UID(test_func_), \ + .file = __FILE__, \ + .line = __LINE__ \ + }; \ + unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \ + }\ + static void UNITY_TEST_UID(test_func_) (void) + +// shorthand to check esp_err_t return code +#define TEST_ESP_OK(rc) TEST_ASSERT_EQUAL_INT32(ESP_OK, rc) +#define TEST_ESP_ERR(err, rc) TEST_ASSERT_EQUAL_INT32(err, rc) + + +#endif //UNITY_CONFIG_H diff --git a/tools/unit-test-app/components/unity/include/unity_internals.h b/tools/unit-test-app/components/unity/include/unity_internals.h new file mode 100644 index 0000000000..03d196f4f6 --- /dev/null +++ b/tools/unit-test-app/components/unity/include/unity_internals.h @@ -0,0 +1,772 @@ +/* ========================================== + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +========================================== */ + +#ifndef UNITY_INTERNALS_H +#define UNITY_INTERNALS_H + +#ifdef UNITY_INCLUDE_CONFIG_H +#include "unity_config.h" +#endif + +#include + +/* Unity Attempts to Auto-Detect Integer Types + * Attempt 1: UINT_MAX, ULONG_MAX, etc in + * Attempt 2: UINT_MAX, ULONG_MAX, etc in + * Attempt 3: Deduced from sizeof() macros */ +#ifndef UNITY_EXCLUDE_STDINT_H +#include +#endif + +#ifndef UNITY_EXCLUDE_LIMITS_H +#include +#endif + +#ifndef UNITY_EXCLUDE_SIZEOF +#ifndef UINT_MAX +#define UINT_MAX (sizeof(unsigned int) * 256 - 1) +#endif +#ifndef ULONG_MAX +#define ULONG_MAX (sizeof(unsigned long) * 256 - 1) +#endif +#ifndef UINTPTR_MAX +/* apparently this is not a constant expression: (sizeof(unsigned int *) * 256 - 1) so we have to just let this fall through */ +#endif +#endif + +#ifndef UNITY_EXCLUDE_MATH_H +#include +#endif + +/*------------------------------------------------------- + * Guess Widths If Not Specified + *-------------------------------------------------------*/ + +/* Determine the size of an int, if not already specificied. + * We cannot use sizeof(int), because it is not yet defined + * at this stage in the trnslation of the C program. + * Therefore, infer it from UINT_MAX if possible. */ +#ifndef UNITY_INT_WIDTH + #ifdef UINT_MAX + #if (UINT_MAX == 0xFFFF) + #define UNITY_INT_WIDTH (16) + #elif (UINT_MAX == 0xFFFFFFFF) + #define UNITY_INT_WIDTH (32) + #elif (UINT_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_INT_WIDTH (64) + #endif + #endif +#endif +#ifndef UNITY_INT_WIDTH + #define UNITY_INT_WIDTH (32) +#endif + +/* Determine the size of a long, if not already specified, + * by following the process used above to define + * UNITY_INT_WIDTH. */ +#ifndef UNITY_LONG_WIDTH + #ifdef ULONG_MAX + #if (ULONG_MAX == 0xFFFF) + #define UNITY_LONG_WIDTH (16) + #elif (ULONG_MAX == 0xFFFFFFFF) + #define UNITY_LONG_WIDTH (32) + #elif (ULONG_MAX == 0xFFFFFFFFFFFFFFFF) + #define UNITY_LONG_WIDTH (64) + #endif + #endif +#endif +#ifndef UNITY_LONG_WIDTH + #define UNITY_LONG_WIDTH (32) +#endif + +/* Determine the size of a pointer, if not already specified, + * by following the process used above to define + * UNITY_INT_WIDTH. */ +#ifndef UNITY_POINTER_WIDTH + #ifdef UINTPTR_MAX + #if (UINTPTR_MAX+0 <= 0xFFFF) + #define UNITY_POINTER_WIDTH (16) + #elif (UINTPTR_MAX+0 <= 0xFFFFFFFF) + #define UNITY_POINTER_WIDTH (32) + #elif (UINTPTR_MAX+0 <= 0xFFFFFFFFFFFFFFFF) + #define UNITY_POINTER_WIDTH (64) + #endif + #endif +#endif +#ifndef UNITY_POINTER_WIDTH + #ifdef INTPTR_MAX + #if (INTPTR_MAX+0 <= 0x7FFF) + #define UNITY_POINTER_WIDTH (16) + #elif (INTPTR_MAX+0 <= 0x7FFFFFFF) + #define UNITY_POINTER_WIDTH (32) + #elif (INTPTR_MAX+0 <= 0x7FFFFFFFFFFFFFFF) + #define UNITY_POINTER_WIDTH (64) + #endif + #endif +#endif +#ifndef UNITY_POINTER_WIDTH + #define UNITY_POINTER_WIDTH UNITY_LONG_WIDTH +#endif + +/*------------------------------------------------------- + * Int Support (Define types based on detected sizes) + *-------------------------------------------------------*/ + +#if (UNITY_INT_WIDTH == 32) + typedef unsigned char _UU8; + typedef unsigned short _UU16; + typedef unsigned int _UU32; + typedef signed char _US8; + typedef signed short _US16; + typedef signed int _US32; +#elif (UNITY_INT_WIDTH == 16) + typedef unsigned char _UU8; + typedef unsigned int _UU16; + typedef unsigned long _UU32; + typedef signed char _US8; + typedef signed int _US16; + typedef signed long _US32; +#else + #error Invalid UNITY_INT_WIDTH specified! (16 or 32 are supported) +#endif + +/*------------------------------------------------------- + * 64-bit Support + *-------------------------------------------------------*/ + +#ifndef UNITY_SUPPORT_64 +#if UNITY_LONG_WIDTH > 32 +#define UNITY_SUPPORT_64 +#endif +#endif +#ifndef UNITY_SUPPORT_64 +#if UNITY_POINTER_WIDTH > 32 +#define UNITY_SUPPORT_64 +#endif +#endif + +#ifndef UNITY_SUPPORT_64 + +/* No 64-bit Support */ +typedef _UU32 _U_UINT; +typedef _US32 _U_SINT; + +#else + +/* 64-bit Support */ +#if (UNITY_LONG_WIDTH == 32) + typedef unsigned long long _UU64; + typedef signed long long _US64; +#elif (UNITY_LONG_WIDTH == 64) + typedef unsigned long _UU64; + typedef signed long _US64; +#else + #error Invalid UNITY_LONG_WIDTH specified! (32 or 64 are supported) +#endif +typedef _UU64 _U_UINT; +typedef _US64 _U_SINT; + +#endif + +/*------------------------------------------------------- + * Pointer Support + *-------------------------------------------------------*/ + +#if (UNITY_POINTER_WIDTH == 32) + typedef _UU32 _UP; +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX32 +#elif (UNITY_POINTER_WIDTH == 64) + typedef _UU64 _UP; +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX64 +#elif (UNITY_POINTER_WIDTH == 16) + typedef _UU16 _UP; +#define UNITY_DISPLAY_STYLE_POINTER UNITY_DISPLAY_STYLE_HEX16 +#else + #error Invalid UNITY_POINTER_WIDTH specified! (16, 32 or 64 are supported) +#endif + +#ifndef UNITY_PTR_ATTRIBUTE +#define UNITY_PTR_ATTRIBUTE +#endif + +#ifndef UNITY_INTERNAL_PTR +#define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const void* +/* #define UNITY_INTERNAL_PTR UNITY_PTR_ATTRIBUTE const _UU8* */ +#endif + +/*------------------------------------------------------- + * Float Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_FLOAT + +/* No Floating Point Support */ +#undef UNITY_INCLUDE_FLOAT +#undef UNITY_FLOAT_PRECISION +#undef UNITY_FLOAT_TYPE +#undef UNITY_FLOAT_VERBOSE + +#else + +#ifndef UNITY_INCLUDE_FLOAT +#define UNITY_INCLUDE_FLOAT +#endif + +/* Floating Point Support */ +#ifndef UNITY_FLOAT_PRECISION +#define UNITY_FLOAT_PRECISION (0.00001f) +#endif +#ifndef UNITY_FLOAT_TYPE +#define UNITY_FLOAT_TYPE float +#endif +typedef UNITY_FLOAT_TYPE _UF; + +#ifndef isinf +#define isinf(n) (((1.0f / f_zero) == n) ? 1 : 0) || (((-1.0f / f_zero) == n) ? 1 : 0) +#define UNITY_FLOAT_NEEDS_ZERO +#endif + +#ifndef isnan +#define isnan(n) ((n != n) ? 1 : 0) +#endif + +#ifndef isneg +#define isneg(n) ((n < 0.0f) ? 1 : 0) +#endif + +#ifndef ispos +#define ispos(n) ((n > 0.0f) ? 1 : 0) +#endif + +#endif + +/*------------------------------------------------------- + * Double Float Support + *-------------------------------------------------------*/ + +/* unlike FLOAT, we DON'T include by default */ +#ifndef UNITY_EXCLUDE_DOUBLE +#ifndef UNITY_INCLUDE_DOUBLE +#define UNITY_EXCLUDE_DOUBLE +#endif +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE + +/* No Floating Point Support */ +#undef UNITY_DOUBLE_PRECISION +#undef UNITY_DOUBLE_TYPE +#undef UNITY_DOUBLE_VERBOSE + +#ifdef UNITY_INCLUDE_DOUBLE +#undef UNITY_INCLUDE_DOUBLE +#endif + +#else + +/* Double Floating Point Support */ +#ifndef UNITY_DOUBLE_PRECISION +#define UNITY_DOUBLE_PRECISION (1e-12f) +#endif +#ifndef UNITY_DOUBLE_TYPE +#define UNITY_DOUBLE_TYPE double +#endif +typedef UNITY_DOUBLE_TYPE _UD; + +#endif + +#ifdef UNITY_DOUBLE_VERBOSE +#ifndef UNITY_FLOAT_VERBOSE +#define UNITY_FLOAT_VERBOSE +#endif +#endif + +/*------------------------------------------------------- + * Output Method: stdout (DEFAULT) + *-------------------------------------------------------*/ +#ifndef UNITY_OUTPUT_CHAR +/* Default to using putchar, which is defined in stdio.h */ +#include +#define UNITY_OUTPUT_CHAR(a) (void)putchar(a) +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifndef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +extern void UNITY_OUTPUT_CHAR(int); + #endif +#endif + +#ifndef UNITY_OUTPUT_FLUSH +/* Default to using putchar, which is defined in stdio.h */ +#include +#define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) +#else + /* If defined as something else, make sure we declare it here so it's ready for use */ + #ifndef UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION +extern void UNITY_OUTPUT_FLUSH(void); + #endif +#endif + +#ifndef UNITY_PRINT_EOL +#define UNITY_PRINT_EOL() UNITY_OUTPUT_CHAR('\n') +#endif + +#ifndef UNITY_OUTPUT_START +#define UNITY_OUTPUT_START() +#endif + +#ifndef UNITY_OUTPUT_COMPLETE +#define UNITY_OUTPUT_COMPLETE() +#endif + +/*------------------------------------------------------- + * Footprint + *-------------------------------------------------------*/ + +#ifndef UNITY_LINE_TYPE +#define UNITY_LINE_TYPE _U_UINT +#endif + +#ifndef UNITY_COUNTER_TYPE +#define UNITY_COUNTER_TYPE _U_UINT +#endif + +/*------------------------------------------------------- + * Language Features Available + *-------------------------------------------------------*/ +#if !defined(UNITY_WEAK_ATTRIBUTE) && !defined(UNITY_WEAK_PRAGMA) +# ifdef __GNUC__ /* includes clang */ +# if !(defined(__WIN32__) && defined(__clang__)) +# define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) +# endif +# endif +#endif + +#ifdef UNITY_NO_WEAK +# undef UNITY_WEAK_ATTRIBUTE +# undef UNITY_WEAK_PRAGMA +#endif + + +/*------------------------------------------------------- + * Internal Structs Needed + *-------------------------------------------------------*/ + +typedef void (*UnityTestFunction)(void); + +#define UNITY_DISPLAY_RANGE_INT (0x10) +#define UNITY_DISPLAY_RANGE_UINT (0x20) +#define UNITY_DISPLAY_RANGE_HEX (0x40) +#define UNITY_DISPLAY_RANGE_AUTO (0x80) + +typedef enum +{ +#if (UNITY_INT_WIDTH == 16) + UNITY_DISPLAY_STYLE_INT = 2 + UNITY_DISPLAY_RANGE_INT + UNITY_DISPLAY_RANGE_AUTO, +#elif (UNITY_INT_WIDTH == 32) + UNITY_DISPLAY_STYLE_INT = 4 + UNITY_DISPLAY_RANGE_INT + UNITY_DISPLAY_RANGE_AUTO, +#elif (UNITY_INT_WIDTH == 64) + UNITY_DISPLAY_STYLE_INT = 8 + UNITY_DISPLAY_RANGE_INT + UNITY_DISPLAY_RANGE_AUTO, +#endif + UNITY_DISPLAY_STYLE_INT8 = 1 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT16 = 2 + UNITY_DISPLAY_RANGE_INT, + UNITY_DISPLAY_STYLE_INT32 = 4 + UNITY_DISPLAY_RANGE_INT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_INT64 = 8 + UNITY_DISPLAY_RANGE_INT, +#endif + +#if (UNITY_INT_WIDTH == 16) + UNITY_DISPLAY_STYLE_UINT = 2 + UNITY_DISPLAY_RANGE_UINT + UNITY_DISPLAY_RANGE_AUTO, +#elif (UNITY_INT_WIDTH == 32) + UNITY_DISPLAY_STYLE_UINT = 4 + UNITY_DISPLAY_RANGE_UINT + UNITY_DISPLAY_RANGE_AUTO, +#elif (UNITY_INT_WIDTH == 64) + UNITY_DISPLAY_STYLE_UINT = 8 + UNITY_DISPLAY_RANGE_UINT + UNITY_DISPLAY_RANGE_AUTO, +#endif + UNITY_DISPLAY_STYLE_UINT8 = 1 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT16 = 2 + UNITY_DISPLAY_RANGE_UINT, + UNITY_DISPLAY_STYLE_UINT32 = 4 + UNITY_DISPLAY_RANGE_UINT, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_UINT64 = 8 + UNITY_DISPLAY_RANGE_UINT, +#endif + UNITY_DISPLAY_STYLE_HEX8 = 1 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX16 = 2 + UNITY_DISPLAY_RANGE_HEX, + UNITY_DISPLAY_STYLE_HEX32 = 4 + UNITY_DISPLAY_RANGE_HEX, +#ifdef UNITY_SUPPORT_64 + UNITY_DISPLAY_STYLE_HEX64 = 8 + UNITY_DISPLAY_RANGE_HEX, +#endif + UNITY_DISPLAY_STYLE_UNKNOWN +} UNITY_DISPLAY_STYLE_T; + +#ifndef UNITY_EXCLUDE_FLOAT +typedef enum _UNITY_FLOAT_TRAIT_T +{ + UNITY_FLOAT_IS_NOT_INF = 0, + UNITY_FLOAT_IS_INF, + UNITY_FLOAT_IS_NOT_NEG_INF, + UNITY_FLOAT_IS_NEG_INF, + UNITY_FLOAT_IS_NOT_NAN, + UNITY_FLOAT_IS_NAN, + UNITY_FLOAT_IS_NOT_DET, + UNITY_FLOAT_IS_DET, + UNITY_FLOAT_INVALID_TRAIT +} UNITY_FLOAT_TRAIT_T; +#endif + +struct _Unity +{ + const char* TestFile; + const char* CurrentTestName; +#ifndef UNITY_EXCLUDE_DETAILS + const char* CurrentDetail1; + const char* CurrentDetail2; +#endif + UNITY_LINE_TYPE CurrentTestLineNumber; + UNITY_COUNTER_TYPE NumberOfTests; + UNITY_COUNTER_TYPE TestFailures; + UNITY_COUNTER_TYPE TestIgnores; + UNITY_COUNTER_TYPE CurrentTestFailed; + UNITY_COUNTER_TYPE CurrentTestIgnored; + jmp_buf AbortFrame; +}; + +extern struct _Unity Unity; + +/*------------------------------------------------------- + * Test Suite Management + *-------------------------------------------------------*/ + +void UnityBegin(const char* filename); +int UnityEnd(void); +void UnityConcludeTest(void); +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum); + +/*------------------------------------------------------- + * Details Support + *-------------------------------------------------------*/ + +#ifdef UNITY_EXCLUDE_DETAILS +#define UNITY_CLR_DETAILS() +#define UNITY_SET_DETAIL(d1) +#define UNITY_SET_DETAILS(d1,d2) +#else +#define UNITY_CLR_DETAILS() { Unity.CurrentDetail1 = 0; Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAIL(d1) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = 0; } +#define UNITY_SET_DETAILS(d1,d2) { Unity.CurrentDetail1 = d1; Unity.CurrentDetail2 = d2; } + +#ifndef UNITY_DETAIL1_NAME +#define UNITY_DETAIL1_NAME "Function" +#endif + +#ifndef UNITY_DETAIL2_NAME +#define UNITY_DETAIL2_NAME "Argument" +#endif +#endif + +/*------------------------------------------------------- + * Test Output + *-------------------------------------------------------*/ + +void UnityPrint(const char* string); +void UnityPrintMask(const _U_UINT mask, const _U_UINT number); +void UnityPrintNumberByStyle(const _U_SINT number, const UNITY_DISPLAY_STYLE_T style); +void UnityPrintNumber(const _U_SINT number); +void UnityPrintNumberUnsigned(const _U_UINT number); +void UnityPrintNumberHex(const _U_UINT number, const char nibbles); + +#ifdef UNITY_FLOAT_VERBOSE +void UnityPrintFloat(const _UF number); +#endif + +/*------------------------------------------------------- + * Test Assertion Fuctions + *------------------------------------------------------- + * Use the macros below this section instead of calling + * these directly. The macros have a consistent naming + * convention and will pull in file and line information + * for you. */ + +void UnityAssertEqualNumber(const _U_SINT expected, + const _U_SINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityAssertBits(const _U_SINT mask, + const _U_SINT expected, + const _U_SINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const _UU32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualStringArray( const char** expected, + const char** actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const _UU32 length, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertNumbersWithin(const _U_UINT delta, + const _U_SINT expected, + const _U_SINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + +void UnityFail(const char* message, const UNITY_LINE_TYPE line); + +void UnityIgnore(const char* message, const UNITY_LINE_TYPE line); + +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertFloatsWithin(const _UF delta, + const _UF expected, + const _UF actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const _UF* expected, + UNITY_PTR_ATTRIBUTE const _UF* actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertFloatSpecial(const _UF actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertDoublesWithin(const _UD delta, + const _UD expected, + const _UD actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const _UD* expected, + UNITY_PTR_ATTRIBUTE const _UD* actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber); + +void UnityAssertDoubleSpecial(const _UD actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style); +#endif + +/*------------------------------------------------------- + * Error Strings We Might Need + *-------------------------------------------------------*/ + +extern const char UnityStrErrFloat[]; +extern const char UnityStrErrDouble[]; +extern const char UnityStrErr64[]; + +/*------------------------------------------------------- + * Test Running Macros + *-------------------------------------------------------*/ + +#define TEST_PROTECT() (setjmp(Unity.AbortFrame) == 0) + +#define TEST_ABORT() {longjmp(Unity.AbortFrame, 1);} + +/* This tricky series of macros gives us an optional line argument to treat it as RUN_TEST(func, num=__LINE__) */ +#ifndef RUN_TEST +#ifdef __STDC_VERSION__ +#if __STDC_VERSION__ >= 199901L +#define RUN_TEST(...) UnityDefaultTestRun(RUN_TEST_FIRST(__VA_ARGS__), RUN_TEST_SECOND(__VA_ARGS__)) +#define RUN_TEST_FIRST(...) RUN_TEST_FIRST_HELPER(__VA_ARGS__, throwaway) +#define RUN_TEST_FIRST_HELPER(first, ...) (first), #first +#define RUN_TEST_SECOND(...) RUN_TEST_SECOND_HELPER(__VA_ARGS__, __LINE__, throwaway) +#define RUN_TEST_SECOND_HELPER(first, second, ...) (second) +#endif +#endif +#endif + +/* If we can't do the tricky version, we'll just have to require them to always include the line number */ +#ifndef RUN_TEST +#ifdef CMOCK +#define RUN_TEST(func, num) UnityDefaultTestRun(func, #func, num) +#else +#define RUN_TEST(func) UnityDefaultTestRun(func, #func, __LINE__) +#endif +#endif + +#define TEST_LINE_NUM (Unity.CurrentTestLineNumber) +#define TEST_IS_IGNORED (Unity.CurrentTestIgnored) +#define UNITY_NEW_TEST(a) \ + Unity.CurrentTestName = (a); \ + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)(__LINE__); \ + Unity.NumberOfTests++; + +#ifndef UNITY_BEGIN +#define UNITY_BEGIN() UnityBegin(__FILE__) +#endif + +#ifndef UNITY_END +#define UNITY_END() UnityEnd() +#endif + +#define UNITY_UNUSED(x) (void)(sizeof(x)) + +/*------------------------------------------------------- + * Basic Fail and Ignore + *-------------------------------------------------------*/ + +#define UNITY_TEST_FAIL(line, message) UnityFail( (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_IGNORE(line, message) UnityIgnore( (message), (UNITY_LINE_TYPE)(line)) + +/*------------------------------------------------------- + * Test Asserts + *-------------------------------------------------------*/ + +#define UNITY_TEST_ASSERT(condition, line, message) if (condition) {} else {UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), (message));} +#define UNITY_TEST_ASSERT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) == NULL), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_NOT_NULL(pointer, line, message) UNITY_TEST_ASSERT(((pointer) != NULL), (UNITY_LINE_TYPE)(line), (message)) + +#define UNITY_TEST_ASSERT_EQUAL_INT(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_US8 )(expected), (_U_SINT)(_US8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_US16)(expected), (_U_SINT)(_US16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_US32)(expected), (_U_SINT)(_US32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_UU8 )(expected), (_U_SINT)(_UU8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_UU16)(expected), (_U_SINT)(_UU16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_UU32)(expected), (_U_SINT)(_UU32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_US8 )(expected), (_U_SINT)(_US8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_US16)(expected), (_U_SINT)(_US16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_US32)(expected), (_U_SINT)(_US32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((_U_SINT)(mask), (_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU8 )(delta), (_U_SINT)(_US8 )(expected), (_U_SINT)(_US8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU16)(delta), (_U_SINT)(_US16)(expected), (_U_SINT)(_US16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_INT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU32)(delta), (_U_SINT)(_US32)(expected), (_U_SINT)(_US32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_UINT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_UINT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU8 )(delta), (_U_SINT)(_U_UINT)(_UU8 )(expected), (_U_SINT)(_U_UINT)(_UU8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_UINT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU16)(delta), (_U_SINT)(_U_UINT)(_UU16)(expected), (_U_SINT)(_U_UINT)(_UU16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_UINT32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU32)(delta), (_U_SINT)(_U_UINT)(_UU32)(expected), (_U_SINT)(_U_UINT)(_UU32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_HEX8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU8 )(delta), (_U_SINT)(_U_UINT)(_UU8 )(expected), (_U_SINT)(_U_UINT)(_UU8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_HEX16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU16)(delta), (_U_SINT)(_U_UINT)(_UU16)(expected), (_U_SINT)(_U_UINT)(_UU16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_HEX32_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((_UU32)(delta), (_U_SINT)(_U_UINT)(_UU32)(expected), (_U_SINT)(_U_UINT)(_UU32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_EQUAL_PTR(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(_UP)(expected), (_U_SINT)(_UP)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING(expected, actual, line, message) UnityAssertEqualString((const char*)(expected), (const char*)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len, line, message) UnityAssertEqualStringLen((const char*)(expected), (const char*)(actual), (_UU32)(len), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY(expected, actual, len, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(len), 1, (message), (UNITY_LINE_TYPE)(line)) + +#define UNITY_TEST_ASSERT_EQUAL_INT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_EQUAL_INT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_EQUAL_INT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_EQUAL_UINT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_EQUAL_UINT16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_EQUAL_UINT32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_EQUAL_HEX16_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_EQUAL_HEX32_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) +#define UNITY_TEST_ASSERT_EQUAL_PTR_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(_UP*)(expected), (UNITY_INTERNAL_PTR)(_UP*)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_POINTER) +#define UNITY_TEST_ASSERT_EQUAL_STRING_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualStringArray((const char**)(expected), (const char**)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_MEMORY_ARRAY(expected, actual, len, num_elements, line, message) UnityAssertEqualMemory((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(len), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line)) + +#ifdef UNITY_SUPPORT_64 +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UnityAssertEqualNumber((_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (_U_SINT)(expected), (_U_SINT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#else +#define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#endif + +#ifdef UNITY_EXCLUDE_FLOAT +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrFloat) +#else +#define UNITY_TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual, line, message) UnityAssertFloatsWithin((_UF)(delta), (_UF)(expected), (_UF)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT(expected, actual, line, message) UNITY_TEST_ASSERT_FLOAT_WITHIN((_UF)(expected) * (_UF)UNITY_FLOAT_PRECISION, (_UF)(expected), (_UF)(actual), (UNITY_LINE_TYPE)(line), (message)) +#define UNITY_TEST_ASSERT_EQUAL_FLOAT_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualFloatArray((_UF*)(expected), (_UF*)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_FLOAT_IS_INF(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NEG_INF(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NAN(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_INF(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NEG_INF(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_NAN(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_FLOAT_IS_NOT_DETERMINATE(actual, line, message) UnityAssertFloatSpecial((_UF)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +#ifdef UNITY_EXCLUDE_DOUBLE +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErrDouble) +#else +#define UNITY_TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual, line, message) UnityAssertDoublesWithin((_UD)(delta), (_UD)(expected), (_UD)(actual), (message), (UNITY_LINE_TYPE)line) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE(expected, actual, line, message) UNITY_TEST_ASSERT_DOUBLE_WITHIN((_UD)(expected) * (_UD)UNITY_DOUBLE_PRECISION, (_UD)expected, (_UD)actual, (UNITY_LINE_TYPE)(line), message) +#define UNITY_TEST_ASSERT_EQUAL_DOUBLE_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualDoubleArray((_UD*)(expected), (_UD*)(actual), (_UU32)(num_elements), (message), (UNITY_LINE_TYPE)line) +#define UNITY_TEST_ASSERT_DOUBLE_IS_INF(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NAN(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_DET) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_INF(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NEG_INF(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NEG_INF) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_NAN) +#define UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE(actual, line, message) UnityAssertDoubleSpecial((_UD)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_FLOAT_IS_NOT_DET) +#endif + +/* End of UNITY_INTERNALS_H */ +#endif diff --git a/tools/unit-test-app/components/unity/license.txt b/tools/unit-test-app/components/unity/license.txt new file mode 100644 index 0000000000..d66fba53e5 --- /dev/null +++ b/tools/unit-test-app/components/unity/license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + +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. diff --git a/tools/unit-test-app/components/unity/unity.c b/tools/unit-test-app/components/unity/unity.c new file mode 100644 index 0000000000..4a99208d8a --- /dev/null +++ b/tools/unit-test-app/components/unity/unity.c @@ -0,0 +1,1306 @@ +/* ========================================================================= + Unity Project - A Test Framework for C + Copyright (c) 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams + [Released under MIT License. Please refer to license.txt for details] +============================================================================ */ + +#include "unity.h" +#include + +/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +#ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION +int UNITY_OUTPUT_CHAR(int); +#endif +#ifdef UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION +int UNITY_OUTPUT_FLUSH(void); +#endif + +/* Helpful macros for us to use here */ +#define UNITY_FAIL_AND_BAIL { Unity.CurrentTestFailed = 1; longjmp(Unity.AbortFrame, 1); } +#define UNITY_IGNORE_AND_BAIL { Unity.CurrentTestIgnored = 1; longjmp(Unity.AbortFrame, 1); } + +/* return prematurely if we are already in failure or ignore state */ +#define UNITY_SKIP_EXECUTION { if ((Unity.CurrentTestFailed != 0) || (Unity.CurrentTestIgnored != 0)) {return;} } + +struct _Unity Unity; + +static const char UnityStrOk[] = "OK"; +static const char UnityStrPass[] = "PASS"; +static const char UnityStrFail[] = "FAIL"; +static const char UnityStrIgnore[] = "IGNORE"; +static const char UnityStrNull[] = "NULL"; +static const char UnityStrSpacer[] = ". "; +static const char UnityStrExpected[] = " Expected "; +static const char UnityStrWas[] = " Was "; +static const char UnityStrElement[] = " Element "; +static const char UnityStrByte[] = " Byte "; +static const char UnityStrMemory[] = " Memory Mismatch."; +static const char UnityStrDelta[] = " Values Not Within Delta "; +static const char UnityStrPointless[] = " You Asked Me To Compare Nothing, Which Was Pointless."; +static const char UnityStrNullPointerForExpected[] = " Expected pointer to be NULL"; +static const char UnityStrNullPointerForActual[] = " Actual pointer was NULL"; +static const char UnityStrNot[] = "Not "; +static const char UnityStrInf[] = "Infinity"; +static const char UnityStrNegInf[] = "Negative Infinity"; +static const char UnityStrNaN[] = "NaN"; +static const char UnityStrDet[] = "Determinate"; +static const char UnityStrInvalidFloatTrait[] = "Invalid Float Trait"; +const char UnityStrErrFloat[] = "Unity Floating Point Disabled"; +const char UnityStrErrDouble[] = "Unity Double Precision Disabled"; +const char UnityStrErr64[] = "Unity 64-bit Support Disabled"; +static const char UnityStrBreaker[] = "-----------------------"; +static const char UnityStrResultsTests[] = " Tests "; +static const char UnityStrResultsFailures[] = " Failures "; +static const char UnityStrResultsIgnored[] = " Ignored "; +static const char UnityStrDetail1Name[] = UNITY_DETAIL1_NAME " "; +static const char UnityStrDetail2Name[] = " " UNITY_DETAIL2_NAME " "; + +#ifdef UNITY_FLOAT_NEEDS_ZERO +/* Dividing by these constants produces +/- infinity. + * The rationale is given in UnityAssertFloatIsInf's body. */ +static const _UF f_zero = 0.0f; +#endif + +/* compiler-generic print formatting masks */ +static const _U_UINT UnitySizeMask[] = +{ + 255u, /* 0xFF */ + 65535u, /* 0xFFFF */ + 65535u, + 4294967295u, /* 0xFFFFFFFF */ + 4294967295u, + 4294967295u, + 4294967295u +#ifdef UNITY_SUPPORT_64 + ,0xFFFFFFFFFFFFFFFF +#endif +}; + +/*----------------------------------------------- + * Pretty Printers & Test Result Output Handlers + *-----------------------------------------------*/ + +void UnityPrint(const char* string) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UnityPrintNumberHex((_U_UINT)*pch, 2); + } + pch++; + } + } +} + +void UnityPrintLen(const char* string, const _UU32 length); +void UnityPrintLen(const char* string, const _UU32 length) +{ + const char* pch = string; + + if (pch != NULL) + { + while (*pch && (_UU32)(pch - string) < length) + { + /* printable characters plus CR & LF are printed */ + if ((*pch <= 126) && (*pch >= 32)) + { + UNITY_OUTPUT_CHAR(*pch); + } + /* write escaped carriage returns */ + else if (*pch == 13) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('r'); + } + /* write escaped line feeds */ + else if (*pch == 10) + { + UNITY_OUTPUT_CHAR('\\'); + UNITY_OUTPUT_CHAR('n'); + } + /* unprintable characters are shown as codes */ + else + { + UNITY_OUTPUT_CHAR('\\'); + UnityPrintNumberHex((_U_UINT)*pch, 2); + } + pch++; + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumberByStyle(const _U_SINT number, const UNITY_DISPLAY_STYLE_T style) +{ + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + UnityPrintNumber(number); + } + else if ((style & UNITY_DISPLAY_RANGE_UINT) == UNITY_DISPLAY_RANGE_UINT) + { + UnityPrintNumberUnsigned( (_U_UINT)number & UnitySizeMask[((_U_UINT)style & (_U_UINT)0x0F) - 1] ); + } + else + { + UnityPrintNumberHex((_U_UINT)number, (char)((style & 0x000F) << 1)); + } +} + +/*-----------------------------------------------*/ +void UnityPrintNumber(const _U_SINT number_to_print) +{ + _U_UINT number = (_U_UINT)number_to_print; + + if (number_to_print < 0) + { + /* A negative number, including MIN negative */ + UNITY_OUTPUT_CHAR('-'); + number = (_U_UINT)(-number_to_print); + } + UnityPrintNumberUnsigned(number); +} + +/*----------------------------------------------- + * basically do an itoa using as little ram as possible */ +void UnityPrintNumberUnsigned(const _U_UINT number) +{ + _U_UINT divisor = 1; + + /* figure out initial divisor */ + while (number / divisor > 9) + { + divisor *= 10; + } + + /* now mod and print, then divide divisor */ + do + { + UNITY_OUTPUT_CHAR((char)('0' + (number / divisor % 10))); + divisor /= 10; + } + while (divisor > 0); +} + +/*-----------------------------------------------*/ +void UnityPrintNumberHex(const _U_UINT number, const char nibbles_to_print) +{ + _U_UINT nibble; + char nibbles = nibbles_to_print; + UNITY_OUTPUT_CHAR('0'); + UNITY_OUTPUT_CHAR('x'); + + while (nibbles > 0) + { + nibble = (number >> (--nibbles << 2)) & 0x0000000F; + if (nibble <= 9) + { + UNITY_OUTPUT_CHAR((char)('0' + nibble)); + } + else + { + UNITY_OUTPUT_CHAR((char)('A' - 10 + nibble)); + } + } +} + +/*-----------------------------------------------*/ +void UnityPrintMask(const _U_UINT mask, const _U_UINT number) +{ + _U_UINT current_bit = (_U_UINT)1 << (UNITY_INT_WIDTH - 1); + _US32 i; + + for (i = 0; i < UNITY_INT_WIDTH; i++) + { + if (current_bit & mask) + { + if (current_bit & number) + { + UNITY_OUTPUT_CHAR('1'); + } + else + { + UNITY_OUTPUT_CHAR('0'); + } + } + else + { + UNITY_OUTPUT_CHAR('X'); + } + current_bit = current_bit >> 1; + } +} + +/*-----------------------------------------------*/ +#ifdef UNITY_FLOAT_VERBOSE +#include + +#ifndef UNITY_VERBOSE_NUMBER_MAX_LENGTH +# ifdef UNITY_DOUBLE_VERBOSE +# define UNITY_VERBOSE_NUMBER_MAX_LENGTH 317 +# else +# define UNITY_VERBOSE_NUMBER_MAX_LENGTH 47 +# endif +#endif + +void UnityPrintFloat(_UF number) +{ + char TempBuffer[UNITY_VERBOSE_NUMBER_MAX_LENGTH + 1]; + snprintf(TempBuffer, sizeof(TempBuffer), "%.6f", number); + UnityPrint(TempBuffer); +} +#endif + +/*-----------------------------------------------*/ + +void UnityPrintFail(void); +void UnityPrintFail(void) +{ + UnityPrint(UnityStrFail); +} + +void UnityPrintOk(void); +void UnityPrintOk(void) +{ + UnityPrint(UnityStrOk); +} + +/*-----------------------------------------------*/ +static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line); +static void UnityTestResultsBegin(const char* file, const UNITY_LINE_TYPE line) +{ +#ifndef UNITY_FIXTURES + UnityPrint(file); + UNITY_OUTPUT_CHAR(':'); + UnityPrintNumber((_U_SINT)line); + UNITY_OUTPUT_CHAR(':'); + UnityPrint(Unity.CurrentTestName); + UNITY_OUTPUT_CHAR(':'); +#else + UNITY_UNUSED(file); + UNITY_UNUSED(line); +#endif +} + +/*-----------------------------------------------*/ +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line); +static void UnityTestResultsFailBegin(const UNITY_LINE_TYPE line) +{ +#ifndef UNITY_FIXTURES + UnityTestResultsBegin(Unity.TestFile, line); +#else + UNITY_UNUSED(line); +#endif + UnityPrint(UnityStrFail); + UNITY_OUTPUT_CHAR(':'); +} + +/*-----------------------------------------------*/ +void UnityConcludeTest(void) +{ + if (Unity.CurrentTestIgnored) + { + Unity.TestIgnores++; + } + else if (!Unity.CurrentTestFailed) + { + UnityTestResultsBegin(Unity.TestFile, Unity.CurrentTestLineNumber); + UnityPrint(UnityStrPass); + } + else + { + Unity.TestFailures++; + } + + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + UNITY_PRINT_EOL(); + UNITY_OUTPUT_FLUSH(); +} + +/*-----------------------------------------------*/ +static void UnityAddMsgIfSpecified(const char* msg); +static void UnityAddMsgIfSpecified(const char* msg) +{ + if (msg) + { + UnityPrint(UnityStrSpacer); +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + UnityPrint(msg); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual); +static void UnityPrintExpectedAndActualStrings(const char* expected, const char* actual) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(expected); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrint(actual); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + +/*-----------------------------------------------*/ +static void UnityPrintExpectedAndActualStringsLen(const char* expected, const char* actual, const _UU32 length) +{ + UnityPrint(UnityStrExpected); + if (expected != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(expected, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } + UnityPrint(UnityStrWas); + if (actual != NULL) + { + UNITY_OUTPUT_CHAR('\''); + UnityPrintLen(actual, length); + UNITY_OUTPUT_CHAR('\''); + } + else + { + UnityPrint(UnityStrNull); + } +} + + + +/*----------------------------------------------- + * Assertion & Control Helpers + *-----------------------------------------------*/ + +static int UnityCheckArraysForNull(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_PTR actual, const UNITY_LINE_TYPE lineNumber, const char* msg) +{ + /* return true if they are both NULL */ + if ((expected == NULL) && (actual == NULL)) + return 1; + + /* throw error if just expected is NULL */ + if (expected == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForExpected); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + + /* throw error if just actual is NULL */ + if (actual == NULL) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrNullPointerForActual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + + /* return false if neither is NULL */ + return 0; +} + +/*----------------------------------------------- + * Assertion Functions + *-----------------------------------------------*/ + +void UnityAssertBits(const _U_SINT mask, + const _U_SINT expected, + const _U_SINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_SKIP_EXECUTION; + + if ((mask & expected) != (mask & actual)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintMask((_U_UINT)mask, (_U_UINT)expected); + UnityPrint(UnityStrWas); + UnityPrintMask((_U_UINT)mask, (_U_UINT)actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualNumber(const _U_SINT expected, + const _U_SINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + UNITY_SKIP_EXECUTION; + + if (expected != actual) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#define UnityPrintPointlessAndBail() \ +{ \ + UnityTestResultsFailBegin(lineNumber); \ + UnityPrint(UnityStrPointless); \ + UnityAddMsgIfSpecified(msg); \ + UNITY_FAIL_AND_BAIL; } + +/*-----------------------------------------------*/ +void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + _UU32 elements = num_elements; + UNITY_INTERNAL_PTR ptr_exp = (UNITY_INTERNAL_PTR)expected; + UNITY_INTERNAL_PTR ptr_act = (UNITY_INTERNAL_PTR)actual; + + UNITY_SKIP_EXECUTION; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (UnityCheckArraysForNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg) == 1) + return; + + /* If style is UNITY_DISPLAY_STYLE_INT, we'll fall into the default case rather than the INT16 or INT32 (etc) case + * as UNITY_DISPLAY_STYLE_INT includes a flag for UNITY_DISPLAY_RANGE_AUTO, which the width-specific + * variants do not. Therefore remove this flag. */ + switch(style & (UNITY_DISPLAY_STYLE_T)(~UNITY_DISPLAY_RANGE_AUTO)) + { + case UNITY_DISPLAY_STYLE_HEX8: + case UNITY_DISPLAY_STYLE_INT8: + case UNITY_DISPLAY_STYLE_UINT8: + while (elements--) + { + if (*(UNITY_PTR_ATTRIBUTE const _US8*)ptr_exp != *(UNITY_PTR_ATTRIBUTE const _US8*)ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US8*)ptr_exp, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US8*)ptr_act, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 1); + ptr_act = (UNITY_INTERNAL_PTR)((_UP)ptr_act + 1); + } + break; + case UNITY_DISPLAY_STYLE_HEX16: + case UNITY_DISPLAY_STYLE_INT16: + case UNITY_DISPLAY_STYLE_UINT16: + while (elements--) + { + if (*(UNITY_PTR_ATTRIBUTE const _US16*)ptr_exp != *(UNITY_PTR_ATTRIBUTE const _US16*)ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US16*)ptr_exp, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US16*)ptr_act, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 2); + ptr_act = (UNITY_INTERNAL_PTR)((_UP)ptr_act + 2); + } + break; +#ifdef UNITY_SUPPORT_64 + case UNITY_DISPLAY_STYLE_HEX64: + case UNITY_DISPLAY_STYLE_INT64: + case UNITY_DISPLAY_STYLE_UINT64: + while (elements--) + { + if (*(UNITY_PTR_ATTRIBUTE const _US64*)ptr_exp != *(UNITY_PTR_ATTRIBUTE const _US64*)ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US64*)ptr_exp, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US64*)ptr_act, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 8); + ptr_act = (UNITY_INTERNAL_PTR)((_UP)ptr_act + 8); + } + break; +#endif + default: + while (elements--) + { + if (*(UNITY_PTR_ATTRIBUTE const _US32*)ptr_exp != *(UNITY_PTR_ATTRIBUTE const _US32*)ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US32*)ptr_exp, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*(UNITY_PTR_ATTRIBUTE const _US32*)ptr_act, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 4); + ptr_act = (UNITY_INTERNAL_PTR)((_UP)ptr_act + 4); + } + break; + } +} + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_FLOAT +void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const _UF* expected, + UNITY_PTR_ATTRIBUTE const _UF* actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + _UU32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const _UF* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const _UF* ptr_actual = actual; + _UF diff, tol; + + UNITY_SKIP_EXECUTION; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (UnityCheckArraysForNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg) == 1) + return; + + while (elements--) + { + diff = *ptr_expected - *ptr_actual; + if (diff < 0.0f) + diff = 0.0f - diff; + tol = UNITY_FLOAT_PRECISION * *ptr_expected; + if (tol < 0.0f) + tol = 0.0f - tol; + + /* This first part of this condition will catch any NaN or Infinite values */ + if (isnan(diff) || isinf(diff) || (diff > tol)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); +#ifdef UNITY_FLOAT_VERBOSE + UnityPrint(UnityStrExpected); + UnityPrintFloat(*ptr_expected); + UnityPrint(UnityStrWas); + UnityPrintFloat(*ptr_actual); +#else + UnityPrint(UnityStrDelta); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_expected++; + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatsWithin(const _UF delta, + const _UF expected, + const _UF actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + _UF diff = actual - expected; + _UF pos_delta = delta; + + UNITY_SKIP_EXECUTION; + + if (diff < 0.0f) + { + diff = 0.0f - diff; + } + if (pos_delta < 0.0f) + { + pos_delta = 0.0f - pos_delta; + } + + /* This first part of this condition will catch any NaN or Infinite values */ + if (isnan(diff) || isinf(diff) || (pos_delta < diff)) + { + UnityTestResultsFailBegin(lineNumber); +#ifdef UNITY_FLOAT_VERBOSE + UnityPrint(UnityStrExpected); + UnityPrintFloat(expected); + UnityPrint(UnityStrWas); + UnityPrintFloat(actual); +#else + UnityPrint(UnityStrDelta); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertFloatSpecial(const _UF actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = { UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet }; + _U_SINT should_be_trait = ((_U_SINT)style & 1); + _U_SINT is_trait = !should_be_trait; + _U_SINT trait_index = (_U_SINT)(style >> 1); + + UNITY_SKIP_EXECUTION; + + switch(style) + { + /* To determine Inf / Neg Inf, we compare to an Inf / Neg Inf value we create on the fly + * We are using a variable to hold the zero value because some compilers complain about dividing by zero otherwise */ + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) & ispos(actual); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) & isneg(actual); + break; + + /* NaN is the only floating point value that does NOT equal itself. Therefore if Actual == Actual, then it is NOT NaN. */ + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual); + break; + + /* A determinate number is non infinite and not NaN. (therefore the opposite of the two above) */ + case UNITY_FLOAT_IS_DET: + case UNITY_FLOAT_IS_NOT_DET: + if (isinf(actual) | isnan(actual)) + is_trait = 0; + else + is_trait = 1; + break; + + default: + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifdef UNITY_FLOAT_VERBOSE + UnityPrintFloat(actual); +#else + if (should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +#endif /* not UNITY_EXCLUDE_FLOAT */ + +/*-----------------------------------------------*/ +#ifndef UNITY_EXCLUDE_DOUBLE +void UnityAssertEqualDoubleArray(UNITY_PTR_ATTRIBUTE const _UD* expected, + UNITY_PTR_ATTRIBUTE const _UD* actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + _UU32 elements = num_elements; + UNITY_PTR_ATTRIBUTE const _UD* ptr_expected = expected; + UNITY_PTR_ATTRIBUTE const _UD* ptr_actual = actual; + _UD diff, tol; + + UNITY_SKIP_EXECUTION; + + if (elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (UnityCheckArraysForNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg) == 1) + return; + + while (elements--) + { + diff = *ptr_expected - *ptr_actual; + if (diff < 0.0) + diff = 0.0 - diff; + tol = UNITY_DOUBLE_PRECISION * *ptr_expected; + if (tol < 0.0) + tol = 0.0 - tol; + + /* This first part of this condition will catch any NaN or Infinite values */ + if (isnan(diff) || isinf(diff) || (diff > tol)) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); +#ifdef UNITY_DOUBLE_VERBOSE + UnityPrint(UnityStrExpected); + UnityPrintFloat((float)(*ptr_expected)); + UnityPrint(UnityStrWas); + UnityPrintFloat((float)(*ptr_actual)); +#else + UnityPrint(UnityStrDelta); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_expected++; + ptr_actual++; + } +} + +/*-----------------------------------------------*/ +void UnityAssertDoublesWithin(const _UD delta, + const _UD expected, + const _UD actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + _UD diff = actual - expected; + _UD pos_delta = delta; + + UNITY_SKIP_EXECUTION; + + if (diff < 0.0) + { + diff = 0.0 - diff; + } + if (pos_delta < 0.0) + { + pos_delta = 0.0 - pos_delta; + } + + /* This first part of this condition will catch any NaN or Infinite values */ + if (isnan(diff) || isinf(diff) || (pos_delta < diff)) + { + UnityTestResultsFailBegin(lineNumber); +#ifdef UNITY_DOUBLE_VERBOSE + UnityPrint(UnityStrExpected); + UnityPrintFloat((float)expected); + UnityPrint(UnityStrWas); + UnityPrintFloat((float)actual); +#else + UnityPrint(UnityStrDelta); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ + +void UnityAssertDoubleSpecial(const _UD actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_FLOAT_TRAIT_T style) +{ + const char* trait_names[] = { UnityStrInf, UnityStrNegInf, UnityStrNaN, UnityStrDet }; + _U_SINT should_be_trait = ((_U_SINT)style & 1); + _U_SINT is_trait = !should_be_trait; + _U_SINT trait_index = (_U_SINT)(style >> 1); + + UNITY_SKIP_EXECUTION; + + switch(style) + { + /* To determine Inf / Neg Inf, we compare to an Inf / Neg Inf value we create on the fly + * We are using a variable to hold the zero value because some compilers complain about dividing by zero otherwise */ + case UNITY_FLOAT_IS_INF: + case UNITY_FLOAT_IS_NOT_INF: + is_trait = isinf(actual) & ispos(actual); + break; + case UNITY_FLOAT_IS_NEG_INF: + case UNITY_FLOAT_IS_NOT_NEG_INF: + is_trait = isinf(actual) & isneg(actual); + break; + + /* NaN is the only floating point value that does NOT equal itself. Therefore if Actual == Actual, then it is NOT NaN. */ + case UNITY_FLOAT_IS_NAN: + case UNITY_FLOAT_IS_NOT_NAN: + is_trait = isnan(actual); + break; + + /* A determinate number is non infinite and not NaN. (therefore the opposite of the two above) */ + case UNITY_FLOAT_IS_DET: + case UNITY_FLOAT_IS_NOT_DET: + if (isinf(actual) | isnan(actual)) + is_trait = 0; + else + is_trait = 1; + break; + + default: + trait_index = 0; + trait_names[0] = UnityStrInvalidFloatTrait; + break; + } + + if (is_trait != should_be_trait) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + if (!should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); + UnityPrint(UnityStrWas); +#ifdef UNITY_DOUBLE_VERBOSE + UnityPrintFloat(actual); +#else + if (should_be_trait) + UnityPrint(UnityStrNot); + UnityPrint(trait_names[trait_index]); +#endif + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + + +#endif /* not UNITY_EXCLUDE_DOUBLE */ + +/*-----------------------------------------------*/ +void UnityAssertNumbersWithin( const _U_UINT delta, + const _U_SINT expected, + const _U_SINT actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + UNITY_SKIP_EXECUTION; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > expected) + Unity.CurrentTestFailed = ((_U_UINT)(actual - expected) > delta); + else + Unity.CurrentTestFailed = ((_U_UINT)(expected - actual) > delta); + } + else + { + if ((_U_UINT)actual > (_U_UINT)expected) + Unity.CurrentTestFailed = ((_U_UINT)(actual - expected) > delta); + else + Unity.CurrentTestFailed = ((_U_UINT)(expected - actual) > delta); + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrDelta); + UnityPrintNumberByStyle((_U_SINT)delta, style); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(expected, style); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(actual, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualString(const char* expected, + const char* actual, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + _UU32 i; + + UNITY_SKIP_EXECUTION; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; expected[i] || actual[i]; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStrings(expected, actual); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + +/*-----------------------------------------------*/ +void UnityAssertEqualStringLen(const char* expected, + const char* actual, + const _UU32 length, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + _UU32 i; + + UNITY_SKIP_EXECUTION; + + /* if both pointers not null compare the strings */ + if (expected && actual) + { + for (i = 0; (expected[i] || actual[i]) && i < length; i++) + { + if (expected[i] != actual[i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected != actual) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrintExpectedAndActualStringsLen(expected, actual, length); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + + +/*-----------------------------------------------*/ +void UnityAssertEqualStringArray( const char** expected, + const char** actual, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + _UU32 i, j = 0; + + UNITY_SKIP_EXECUTION; + + /* if no elements, it's an error */ + if (num_elements == 0) + { + UnityPrintPointlessAndBail(); + } + + if (UnityCheckArraysForNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg) == 1) + return; + + do + { + /* if both pointers not null compare the strings */ + if (expected[j] && actual[j]) + { + for (i = 0; expected[j][i] || actual[j][i]; i++) + { + if (expected[j][i] != actual[j][i]) + { + Unity.CurrentTestFailed = 1; + break; + } + } + } + else + { /* handle case of one pointers being null (if both null, test should pass) */ + if (expected[j] != actual[j]) + { + Unity.CurrentTestFailed = 1; + } + } + + if (Unity.CurrentTestFailed) + { + UnityTestResultsFailBegin(lineNumber); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(j); + } + UnityPrintExpectedAndActualStrings((const char*)(expected[j]), (const char*)(actual[j])); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + } while (++j < num_elements); +} + +/*-----------------------------------------------*/ +void UnityAssertEqualMemory( UNITY_INTERNAL_PTR expected, + UNITY_INTERNAL_PTR actual, + const _UU32 length, + const _UU32 num_elements, + const char* msg, + const UNITY_LINE_TYPE lineNumber) +{ + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_exp = (UNITY_PTR_ATTRIBUTE const unsigned char*)expected; + UNITY_PTR_ATTRIBUTE const unsigned char* ptr_act = (UNITY_PTR_ATTRIBUTE const unsigned char*)actual; + _UU32 elements = num_elements; + _UU32 bytes; + + UNITY_SKIP_EXECUTION; + + if ((elements == 0) || (length == 0)) + { + UnityPrintPointlessAndBail(); + } + + if (UnityCheckArraysForNull((UNITY_INTERNAL_PTR)expected, (UNITY_INTERNAL_PTR)actual, lineNumber, msg) == 1) + return; + + while (elements--) + { + /* /////////////////////////////////// */ + bytes = length; + while (bytes--) + { + if (*ptr_exp != *ptr_act) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrMemory); + if (num_elements > 1) + { + UnityPrint(UnityStrElement); + UnityPrintNumberUnsigned(num_elements - elements - 1); + } + UnityPrint(UnityStrByte); + UnityPrintNumberUnsigned(length - bytes - 1); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(*ptr_exp, UNITY_DISPLAY_STYLE_HEX8); + UnityPrint(UnityStrWas); + UnityPrintNumberByStyle(*ptr_act, UNITY_DISPLAY_STYLE_HEX8); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } + ptr_exp = (UNITY_INTERNAL_PTR)((_UP)ptr_exp + 1); + ptr_act = (UNITY_INTERNAL_PTR)((_UP)ptr_act + 1); + } + /* /////////////////////////////////// */ + + } +} + +/*----------------------------------------------- + * Control Functions + *-----------------------------------------------*/ + +void UnityFail(const char* msg, const UNITY_LINE_TYPE line) +{ + UNITY_SKIP_EXECUTION; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrintFail(); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + +#ifndef UNITY_EXCLUDE_DETAILS + if (Unity.CurrentDetail1) + { + UnityPrint(UnityStrDetail1Name); + UnityPrint(Unity.CurrentDetail1); + if (Unity.CurrentDetail2) + { + UnityPrint(UnityStrDetail2Name); + UnityPrint(Unity.CurrentDetail2); + } + UnityPrint(UnityStrSpacer); + } +#endif + if (msg[0] != ' ') + { + UNITY_OUTPUT_CHAR(' '); + } + UnityPrint(msg); + } + + UNITY_FAIL_AND_BAIL; +} + +/*-----------------------------------------------*/ +void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) +{ + UNITY_SKIP_EXECUTION; + + UnityTestResultsBegin(Unity.TestFile, line); + UnityPrint(UnityStrIgnore); + if (msg != NULL) + { + UNITY_OUTPUT_CHAR(':'); + UNITY_OUTPUT_CHAR(' '); + UnityPrint(msg); + } + UNITY_IGNORE_AND_BAIL; +} + +/*-----------------------------------------------*/ +#if defined(UNITY_WEAK_ATTRIBUTE) + UNITY_WEAK_ATTRIBUTE void setUp(void) { } + UNITY_WEAK_ATTRIBUTE void tearDown(void) { } +#elif defined(UNITY_WEAK_PRAGMA) +# pragma weak setUp + void setUp(void) { } +# pragma weak tearDown + void tearDown(void) { } +#endif +/*-----------------------------------------------*/ +void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) +{ + Unity.CurrentTestName = FuncName; + Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; + Unity.NumberOfTests++; + UNITY_CLR_DETAILS(); + if (TEST_PROTECT()) + { + setUp(); + Func(); + } + if (TEST_PROTECT() && !(Unity.CurrentTestIgnored)) + { + tearDown(); + } + UnityConcludeTest(); +} + +/*-----------------------------------------------*/ +void UnityBegin(const char* filename) +{ + Unity.TestFile = filename; + Unity.CurrentTestName = NULL; + Unity.CurrentTestLineNumber = 0; + Unity.NumberOfTests = 0; + Unity.TestFailures = 0; + Unity.TestIgnores = 0; + Unity.CurrentTestFailed = 0; + Unity.CurrentTestIgnored = 0; + + UNITY_CLR_DETAILS(); + UNITY_OUTPUT_START(); +} + +/*-----------------------------------------------*/ +int UnityEnd(void) +{ + UNITY_PRINT_EOL(); + UnityPrint(UnityStrBreaker); + UNITY_PRINT_EOL(); + UnityPrintNumber((_U_SINT)(Unity.NumberOfTests)); + UnityPrint(UnityStrResultsTests); + UnityPrintNumber((_U_SINT)(Unity.TestFailures)); + UnityPrint(UnityStrResultsFailures); + UnityPrintNumber((_U_SINT)(Unity.TestIgnores)); + UnityPrint(UnityStrResultsIgnored); + UNITY_PRINT_EOL(); + if (Unity.TestFailures == 0U) + { + UnityPrintOk(); + } + else + { + UnityPrintFail(); +#ifdef UNITY_DIFFERENTIATE_FINAL_FAIL + UNITY_OUTPUT_CHAR('E'); UNITY_OUTPUT_CHAR('D'); +#endif + } + UNITY_PRINT_EOL(); + UNITY_OUTPUT_FLUSH(); + UNITY_OUTPUT_COMPLETE(); + return (int)(Unity.TestFailures); +} + +/*-----------------------------------------------*/ diff --git a/tools/unit-test-app/components/unity/unity_platform.c b/tools/unit-test-app/components/unity/unity_platform.c new file mode 100644 index 0000000000..4e1c0b8e07 --- /dev/null +++ b/tools/unit-test-app/components/unity/unity_platform.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include "unity.h" +#include "rom/ets_sys.h" + +#define unity_printf ets_printf + +// Functions which are defined in ROM, linker script provides addresses for these: +int uart_tx_one_char(uint8_t c); +void uart_tx_wait_idle(uint8_t uart_no); +int UartRxString(uint8_t* dst, uint8_t max_length); + +// Pointers to the head and tail of linked list of test description structs: +static struct test_desc_t* s_unity_tests_first = NULL; +static struct test_desc_t* s_unity_tests_last = NULL; + + +void unity_putc(int c) +{ + if (c == '\n') + { + uart_tx_one_char('\n'); + uart_tx_one_char('\r'); + } + else if (c == '\r') + { + } + else + { + uart_tx_one_char(c); + } +} + +void unity_flush() +{ + uart_tx_wait_idle(0); // assume that output goes to UART0 +} + +void unity_testcase_register(struct test_desc_t* desc) +{ + if (!s_unity_tests_first) + { + s_unity_tests_first = desc; + } + else + { + s_unity_tests_last->next = desc; + } + s_unity_tests_last = desc; + desc->next = NULL; +} + +static void unity_run_single_test(const struct test_desc_t* test) +{ + Unity.TestFile = test->file; + Unity.CurrentDetail1 = test->desc; + UnityDefaultTestRun(test->fn, test->name, test->line); +} + +static void unity_run_single_test_by_index(int index) +{ + const struct test_desc_t* test; + for (test = s_unity_tests_first; test != NULL && index != 0; test = test->next, --index) + { + } + if (test != NULL) + { + unity_run_single_test(test); + } + +} + +static void unity_run_single_test_by_name(const char* filter) +{ + char tmp[256]; + strncpy(tmp, filter + 1, sizeof(tmp) - 1); + tmp[strlen(filter) - 2] = 0; + for (const struct test_desc_t* test = s_unity_tests_first; test != NULL; test = test->next) + { + if (strstr(test->name, tmp) != NULL) + { + unity_run_single_test(test); + } + } +} + +void unity_run_all_tests() +{ + for (const struct test_desc_t* test = s_unity_tests_first; test != NULL; test = test->next) + { + unity_run_single_test(test); + } +} + +void unity_run_tests_with_filter(const char* filter) +{ + for (const struct test_desc_t* test = s_unity_tests_first; test != NULL; test = test->next) + { + if (strstr(test->desc, filter) != NULL) + { + unity_run_single_test(test); + } + } +} + +static void trim_trailing_space(char* str) +{ + char* end = str + strlen(str) - 1; + while (end >= str && isspace((int) *end)) + { + *end = 0; + --end; + } +} + +void unity_run_menu() +{ + while (true) + { + int test_counter = 0; + unity_printf("\n\nHere's the test menu, pick your combo:\n"); + for (const struct test_desc_t* test = s_unity_tests_first; + test != NULL; + test = test->next, ++test_counter) + { + unity_printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc); + } + + char cmdline[256]; + UartRxString((uint8_t*) cmdline, sizeof(cmdline) - 1); + trim_trailing_space(cmdline); + + if (strlen(cmdline) == 0) + { + continue; + } + + UNITY_BEGIN(); + + if (cmdline[0] == '*') + { + unity_run_all_tests(); + } + else if (cmdline[0] =='[') + { + unity_run_tests_with_filter(cmdline); + } + else if (cmdline[0] =='"') + { + unity_run_single_test_by_name(cmdline); + } + else + { + int test_index = strtol(cmdline, NULL, 10); + if (test_index >= 1 && test_index <= test_counter) + { + unity_run_single_test_by_index(test_index - 1); + } + } + + UNITY_END(); + } +} + diff --git a/tools/unit-test-app/main/app_main.c b/tools/unit-test-app/main/app_main.c new file mode 100644 index 0000000000..b00034adf1 --- /dev/null +++ b/tools/unit-test-app/main/app_main.c @@ -0,0 +1,18 @@ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" + + +void unityTask(void *pvParameters) +{ + vTaskDelay(1000 / portTICK_PERIOD_MS); + unity_run_menu(); + while(1); +} + +void app_main() +{ + xTaskCreatePinnedToCore(unityTask, "unityTask", 4096, NULL, 5, NULL, 0); +} + diff --git a/tools/unit-test-app/main/component.mk b/tools/unit-test-app/main/component.mk new file mode 100644 index 0000000000..fd2dbe7b84 --- /dev/null +++ b/tools/unit-test-app/main/component.mk @@ -0,0 +1,5 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# + +include $(IDF_PATH)/make/component_common.mk diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig new file mode 100644 index 0000000000..cde49f3c8d --- /dev/null +++ b/tools/unit-test-app/sdkconfig @@ -0,0 +1,125 @@ +# +# Automatically generated file; DO NOT EDIT. +# Espressif IoT Development Framework Configuration +# + +# +# SDK tool configuration +# +CONFIG_TOOLPREFIX="xtensa-esp32-elf-" +CONFIG_PYTHON="python" + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0" +# CONFIG_ESPTOOLPY_BAUD_115200B is not set +# CONFIG_ESPTOOLPY_BAUD_230400B is not set +CONFIG_ESPTOOLPY_BAUD_921600B=y +# CONFIG_ESPTOOLPY_BAUD_2MB is not set +# CONFIG_ESPTOOLPY_BAUD_OTHER is not set +CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 +CONFIG_ESPTOOLPY_BAUD=921600 +CONFIG_ESPTOOLPY_COMPRESSED=y +# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE="dio" +# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="40m" + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 +CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_APP_OFFSET=0x10000 + +# +# Component config +# + +# +# ESP32-specific config +# +# CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set +# CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 +# CONFIG_ESP32_ENABLE_STACK_WIFI is not set +# CONFIG_ESP32_ENABLE_STACK_BT is not set +CONFIG_ESP32_ENABLE_STACK_NONE=y +CONFIG_MEMMAP_SMP=y +# CONFIG_MEMMAP_TRACEMEM is not set +# CONFIG_MEMMAP_SPISRAM is not set +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 +CONFIG_MAIN_TASK_STACK_SIZE=4096 +CONFIG_NEWLIB_STDOUT_ADDCR=y + +# +# FreeRTOS +# +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_CORETIMER_0=y +# CONFIG_FREERTOS_CORETIMER_1 is not set +# CONFIG_FREERTOS_CORETIMER_2 is not set +CONFIG_FREERTOS_HZ=1000 +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 +# CONFIG_FREERTOS_PANIC_PRINT_HALT is not set +CONFIG_FREERTOS_PANIC_PRINT_REBOOT=y +# CONFIG_FREERTOS_PANIC_SILENT_REBOOT is not set +# CONFIG_FREERTOS_PANIC_GDBSTUB is not set +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y +# CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set +# CONFIG_FREERTOS_ASSERT_DISABLE is not set +CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG=y +# CONFIG_ENABLE_MEMORY_DEBUG is not set +# CONFIG_FREERTOS_DEBUG_INTERNALS is not set + +# +# Log output +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_COLORS=y + +# +# LWIP +# +CONFIG_LWIP_MAX_SOCKETS=4 +CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX=0 +# CONFIG_LWIP_SO_REUSE is not set + +# +# mbedTLS +# +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +# CONFIG_MBEDTLS_DEBUG is not set + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set + +# +# TESTS +# +CONFIG_FP_TEST_ENABLE=y From 2a73783bc656f208c4277f837593acb200a203c1 Mon Sep 17 00:00:00 2001 From: Daniel Campora Date: Tue, 15 Nov 2016 00:46:53 +0100 Subject: [PATCH 279/285] StaticQueue_t needs to have the same size as xQUEUE. Without this change, building FreeRTOS with static allocation enabled succeeds, but trying to create a queue from a static buffer causes an assert because the size of static and dynamic queues differ. --- components/freertos/include/freertos/FreeRTOS.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index f6c9aa497d..67bdc6db12 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -927,7 +927,6 @@ typedef struct xSTATIC_QUEUE StaticList_t xDummy3[ 2 ]; UBaseType_t uxDummy4[ 3 ]; - BaseType_t ucDummy5[ 2 ]; #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) uint8_t ucDummy6; @@ -943,12 +942,12 @@ typedef struct xSTATIC_QUEUE #endif struct { - volatile uint32_t mux; + volatile uint32_t ucDummy10; #ifdef CONFIG_FREERTOS_PORTMUX_DEBUG - const char *lastLockedFn; - int lastLockedLine; + void *pvDummy8; + UBaseType_t uxDummy11; #endif - } mux; + } sDummy12; } StaticQueue_t; typedef StaticQueue_t StaticSemaphore_t; From 5ac7810480c77af66963c047dee3997d8101bc6e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 23 Nov 2016 08:49:15 +1100 Subject: [PATCH 280/285] build system: Fix null-terminating of text files for embedding on OS X Ref github #112 --- make/component_wrapper.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index 3018c18b55..48bf0ecace 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -198,7 +198,7 @@ embed_bin/$$(notdir $(1)): $(call resolvepath,$(1),$(COMPONENT_PATH)) | embed_bi embed_txt/$$(notdir $(1)): $(call resolvepath,$(1),$(COMPONENT_PATH)) | embed_txt cp $$< $$@ - echo -ne '\0' >> $$@ # null-terminate text files + printf '\0' >> $$@ # null-terminate text files # messing about with the embed_X subdirectory then using 'cd' for objcopy is because the # full path passed to OBJCOPY makes it into the name of the symbols in the .o file From 180f4d092917183fb27508f1ca7fb1eabf1b7d23 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 23 Nov 2016 09:42:35 +1100 Subject: [PATCH 281/285] FreeRTOS: Use C11 _Static_assert to verify static "dummy" structs at compile time Includes a tweak to make Static_task_t equal size to TCB_t when using MPU_WRAPPERS . Matches tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE macro in tasks.c. This isn't actually a bug (if static task allocation is off, there is no use for Static_task_t), but it allows us to make consistent compile-time checks that Static_task_t == TCB_t. --- components/freertos/include/freertos/FreeRTOS.h | 3 ++- components/freertos/include/freertos/list.h | 13 +++++++++++++ components/freertos/queue.c | 5 +++++ components/freertos/tasks.c | 4 ++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/components/freertos/include/freertos/FreeRTOS.h b/components/freertos/include/freertos/FreeRTOS.h index 67bdc6db12..4c60308f78 100644 --- a/components/freertos/include/freertos/FreeRTOS.h +++ b/components/freertos/include/freertos/FreeRTOS.h @@ -895,7 +895,8 @@ typedef struct xSTATIC_TCB uint32_t ulDummy18; uint32_t ucDummy19; #endif - #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + #if( ( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) \ + || ( portUSING_MPU_WRAPPERS == 1 ) ) uint8_t uxDummy20; #endif diff --git a/components/freertos/include/freertos/list.h b/components/freertos/include/freertos/list.h index b63df7f724..8606deba5a 100644 --- a/components/freertos/include/freertos/list.h +++ b/components/freertos/include/freertos/list.h @@ -190,6 +190,10 @@ struct xLIST_ITEM }; typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ +#if __GNUC_PREREQ(4, 6) +_Static_assert(sizeof(StaticListItem_t) == sizeof(ListItem_t), "StaticListItem_t != ListItem_t"); +#endif + struct xMINI_LIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ @@ -199,6 +203,11 @@ struct xMINI_LIST_ITEM }; typedef struct xMINI_LIST_ITEM MiniListItem_t; +#if __GNUC_PREREQ(4, 6) +_Static_assert(sizeof(StaticMiniListItem_t) == sizeof(MiniListItem_t), "StaticMiniListItem_t != MiniListItem_t"); +#endif + + /* * Definition of the type of queue used by the scheduler. */ @@ -211,6 +220,10 @@ typedef struct xLIST listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ } List_t; +#if __GNUC_PREREQ(4, 6) +_Static_assert(sizeof(StaticList_t) == sizeof(List_t), "StaticList_t != List_t"); +#endif + /* * Access macro to set the owner of a list item. The owner of a list item * is the object (usually a TCB) that contains the list item. diff --git a/components/freertos/queue.c b/components/freertos/queue.c index f404a243e6..7c491f6952 100644 --- a/components/freertos/queue.c +++ b/components/freertos/queue.c @@ -179,6 +179,11 @@ typedef struct QueueDefinition name below to enable the use of older kernel aware debuggers. */ typedef xQUEUE Queue_t; +#if __GNUC_PREREQ(4, 6) +_Static_assert(sizeof(StaticQueue_t) == sizeof(Queue_t), "StaticQueue_t != Queue_t"); +#endif + + /*-----------------------------------------------------------*/ /* diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 26103ee236..16cce3b967 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -242,6 +242,10 @@ typedef struct tskTaskControlBlock below to enable the use of older kernel aware debuggers. */ typedef tskTCB TCB_t; +#if __GNUC_PREREQ(4, 6) +_Static_assert(sizeof(StaticTask_t) == sizeof(TCB_t), "StaticTask_t != TCB_t"); +#endif + /* * Some kernel aware debuggers require the data the debugger needs access to to * be global, rather than file scope. From 1d05f41a70583a4631d6a7520a8772b216df05f6 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 23 Nov 2016 10:47:40 +1100 Subject: [PATCH 282/285] UART driver: Fix crash in ISR due to "UART" static array moved to flash Ref: http://esp32.com/viewtopic.php?f=13&t=546&sid=76ff371ae2b259441a2cf355e96d74b9#p2275 This is a really subtle bug, gcc noticed the UART array elements are read-only so implicitly moved the elements to .rodata as if it was const. However this array is accessed from the UART ISR, so has to be in IRAM or DRAM. --- components/driver/uart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index 02610e7b49..59b4904dcd 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -84,7 +84,8 @@ typedef struct { static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0}; -static uart_dev_t* UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; +/* DRAM_ATTR is required to avoid UART array placed in flash, due to accessed from ISR */ +static DRAM_ATTR uart_dev_t* const UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2}; static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit) From 83a3ce882f568692400914b410863b45877f4562 Mon Sep 17 00:00:00 2001 From: antti Date: Wed, 23 Nov 2016 11:29:36 +0800 Subject: [PATCH 283/285] add updated nvs flash test --- components/nvs_flash/test/test_nvs.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/components/nvs_flash/test/test_nvs.c b/components/nvs_flash/test/test_nvs.c index c523026432..db97879bc2 100644 --- a/components/nvs_flash/test/test_nvs.c +++ b/components/nvs_flash/test/test_nvs.c @@ -9,32 +9,26 @@ #include "esp_spi_flash.h" #include -#define NVS_FLASH_SECTOR 6 -#define NVS_FLASH_SECTOR_COUNT_MIN 3 -#define NVS_FLASH_SECTOR_COUNT_MAX 10 TEST_CASE("various nvs tests", "[nvs]") { nvs_handle handle_1; - TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED); - for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) { - spi_flash_erase_sector(i); - } - TEST_ESP_OK(nvs_flash_init_custom(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(nvs_flash_init()); + TEST_ESP_ERR(nvs_open("test_namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); - TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND); + TEST_ESP_ERR(nvs_set_i32(handle_1, "foo", 0x12345678), ESP_ERR_NVS_INVALID_HANDLE); + nvs_close(handle_1); - // TEST_ESP_ERR(nvs_set_i32(handle_1, "foo", 0x12345678), ESP_ERR_NVS_READ_ONLY); - // nvs_close(handle_1); - - TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1)); + TEST_ESP_OK(nvs_open("test_namespace2", NVS_READWRITE, &handle_1)); + TEST_ESP_OK(nvs_erase_all(handle_1)); TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678)); TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789)); nvs_handle handle_2; - TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2)); + TEST_ESP_OK(nvs_open("test_namespace3", NVS_READWRITE, &handle_2)); + TEST_ESP_OK(nvs_erase_all(handle_2)); TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a)); - const char *str = "value 0123456789abcdef0123456789abcdef"; + const char* str = "value 0123456789abcdef0123456789abcdef"; TEST_ESP_OK(nvs_set_str(handle_2, "key", str)); int32_t v1; @@ -51,4 +45,7 @@ TEST_CASE("various nvs tests", "[nvs]") TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len)); TEST_ASSERT_EQUAL_INT32(0, strcmp(buf, str)); + + nvs_close(handle_1); + nvs_close(handle_2); } From 15d96eccdba963bb98e6c7efbac04c009a8c0652 Mon Sep 17 00:00:00 2001 From: antti Date: Wed, 23 Nov 2016 11:42:12 +0800 Subject: [PATCH 284/285] add build all unit tests in project.mk and update doc --- make/project.mk | 12 +++++++++--- tools/unit-test-app/README.md | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/make/project.mk b/make/project.mk index 0dfce4368a..181152f118 100644 --- a/make/project.mk +++ b/make/project.mk @@ -94,10 +94,16 @@ COMPONENT_PATHS += $(abspath $(SRCDIRS)) # A component is buildable if it has a component.mk makefile in it COMPONENT_PATHS_BUILDABLE := $(foreach cp,$(COMPONENT_PATHS),$(if $(wildcard $(cp)/component.mk),$(cp))) -# If TEST_COMPONENTS is set on the command line, create variables for building unit tests +# If TESTS_ALL set to 1, set TEST_COMPONENTS to all components +ifeq ($(TESTS_ALL),1) +TEST_COMPONENTS := $(COMPONENTS) +endif + +# If TEST_COMPONENTS is set, create variables for building unit tests ifdef TEST_COMPONENTS -TEST_COMPONENT_PATHS := $(foreach comp,$(TEST_COMPONENTS),$(IDF_PATH)/components/$(comp)/test) -TEST_COMPONENT_NAMES := $(foreach comp,$(TEST_COMPONENTS),$(comp)_test) +override TEST_COMPONENTS := $(foreach comp,$(TEST_COMPONENTS),$(wildcard $(IDF_PATH)/components/$(comp)/test)) +TEST_COMPONENT_PATHS := $(TEST_COMPONENTS) +TEST_COMPONENT_NAMES := $(foreach comp,$(TEST_COMPONENTS),$(lastword $(subst /, ,$(dir $(comp))))_test) endif # Initialise project-wide variables which can be added to by diff --git a/tools/unit-test-app/README.md b/tools/unit-test-app/README.md index cf45ce287b..eff3e12f4a 100644 --- a/tools/unit-test-app/README.md +++ b/tools/unit-test-app/README.md @@ -8,5 +8,5 @@ ESP-IDF unit tests are run using Unit Test App. The app can be built with the un * Set IDF_PATH environment variable to point to the path to the esp-idf top-level directory. * Change into `tools/unit-test-app` directory * `make menuconfig` to configure the Unit Test App. -* `make TEST_COMPONENTS=` with `TEST_COMPONENTS` set to names of the components to be included in the test app. +* `make TEST_COMPONENTS=` with `TEST_COMPONENTS` set to names of the components to be included in the test app. Or `make TESTS_ALL=1` to build the test app with all the tests for components having `test` subdirectory. * Follow the printed instructions to flash, or run `make flash`. From 79646f41b596f56cc26aa1c3df9697c71867df4a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 24 Nov 2016 08:08:09 +1100 Subject: [PATCH 285/285] Fixes for stdlib.h inclusion Refs: http://esp32.com/viewtopic.php?f=13&t=550 http://esp32.com/viewtopic.php?f=13&t=551 rmt.c should include stdlib.h for malloc, esp_bignum,c & https_request_main.c for abort(). FreeRTOSConfig.h is only including stdlib if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION is set. However, it is included for abort() so needs to be included whenever CONFIG_FREERTOS_ASSERT_FAIL_ABORT is set. This change includes unconditionally in FreeRTOSConfig.h. This is to avoid this kind of bug where compiler errors are dependent on config. I suggest we don't change this to be more selective until we have 'make randomconfig' style tests in CI. --- components/driver/rmt.c | 1 + components/freertos/include/freertos/FreeRTOSConfig.h | 3 +-- components/mbedtls/port/esp_bignum.c | 1 + examples/04_https_request/main/https_request_main.c | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/driver/rmt.c b/components/driver/rmt.c index 21bc8696b0..9fc36d49c2 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -13,6 +13,7 @@ // limitations under the License. #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/xtensa_api.h" diff --git a/components/freertos/include/freertos/FreeRTOSConfig.h b/components/freertos/include/freertos/FreeRTOSConfig.h index 13ce73e0a8..4f1012657a 100644 --- a/components/freertos/include/freertos/FreeRTOSConfig.h +++ b/components/freertos/include/freertos/FreeRTOSConfig.h @@ -108,6 +108,7 @@ /* configASSERT behaviour */ #ifndef __ASSEMBLER__ +#include /* for abort() */ #include "rom/ets_sys.h" #if defined(CONFIG_FREERTOS_ASSERT_DISABLE) @@ -126,8 +127,6 @@ #endif #if CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION -#include -#include "rom/ets_sys.h" #define UNTESTED_FUNCTION() { ets_printf("Untested FreeRTOS function %s\r\n", __FUNCTION__); configASSERT(false); } while(0) #else #define UNTESTED_FUNCTION() diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 7570820e3b..ec2e420398 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "mbedtls/bignum.h" #include "rom/bigint.h" #include "soc/hwcrypto_reg.h" diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index 2ad56681d0..1f7112bc86 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -22,6 +22,7 @@ * limitations under the License. */ #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h"