diff --git a/drivers/tildagon_power/fusb302b/fusb302b.c b/drivers/tildagon_power/fusb302b/fusb302b.c index e4977fea..04b54d02 100644 --- a/drivers/tildagon_power/fusb302b/fusb302b.c +++ b/drivers/tildagon_power/fusb302b/fusb302b.c @@ -19,6 +19,7 @@ typedef struct #define READ ( MP_MACHINE_I2C_FLAG_WRITE1 | MP_MACHINE_I2C_FLAG_READ | MP_MACHINE_I2C_FLAG_STOP ) #define WRITE MP_MACHINE_I2C_FLAG_STOP static const fusb_register_t select_cc = { 0x02U, 0x0CU, 2U }; +static const fusb_register_t select_vcon = { 0x02U, 0x30U, 4U }; static const fusb_register_t enable_bmc = { 0x03U, 0x03U, 0U }; static const fusb_register_t auto_crc = { 0x03U, 0x04U, 2U }; static const fusb_register_t tx_flush = { 0x06U, 0x40U, 6U }; @@ -54,7 +55,6 @@ void fusb_setup_device( fusb_state_t* state ) mp_machine_i2c_buf_t buffer = { .len = 13, .buf = write_buffer }; tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); state->host = 0U; - state->input_current_limit = 500U; state->cc_select = 0U; } @@ -85,6 +85,16 @@ void fusb_set_cc( fusb_state_t* state, uint8_t cc_select ) write_bits( state, select_cc, cc_select ); } +/** + * @brief turn on Vcon + * @param state the port object + * @param cc_vcon which cc line to use as Vcon + */ +void fusb_set_vcon(fusb_state_t* state, uint8_t cc_vcon ) +{ + write_bits( state, select_vcon, cc_vcon ); +} + /** * @brief disable toggle * @param state the port object @@ -267,22 +277,11 @@ void fusb_setup_pd( fusb_state_t* state ) tildagon_mux_i2c_transaction( state->mux_port, ADDRESS, 1, &buffer, WRITE ); write_bits( state, rx_flush, 1 ); - uint8_t data = 0x20; - if ( state->cc_select == 1 ) - { - data |= 0x01; - } - else - { - data |= 0x02; - } - if ( state->host == 0 ) - { - data |= 0x84; - } - else + uint8_t data = 0x24; /* pd rev 2 and auto crc bits set */ + if ( state->cc_select < 3 ) { - data |= 0x04; + data |= state->cc_select; /* enable transceiver for the CC pin selected */ + data |= state->host << 7 | state->host << 4; /* set power and data roles for auto good crc */ } write_buffer[0] = enable_bmc.regaddr; write_buffer[1] = data; diff --git a/drivers/tildagon_power/fusb302b/fusb302b.h b/drivers/tildagon_power/fusb302b/fusb302b.h index dd74df6a..180f4d88 100644 --- a/drivers/tildagon_power/fusb302b/fusb302b.h +++ b/drivers/tildagon_power/fusb302b/fusb302b.h @@ -22,7 +22,6 @@ typedef struct { tildagon_mux_i2c_obj_t* mux_port; uint8_t cc_select; - uint16_t input_current_limit; uint16_t status; uint16_t statusa; uint8_t host; @@ -48,6 +47,12 @@ extern void fusb_setup_host( fusb_state_t* state ); * @param cc_select the pin to measure on */ extern void fusb_set_cc( fusb_state_t* state, uint8_t cc_select ); +/** + * @brief turn on Vcon + * @param state the port object + * @param cc_vcon which cc line to use as Vcon + */ +extern void fusb_set_vcon(fusb_state_t* state, uint8_t cc_vcon ); /** * @brief disable toggle * @param state the port object diff --git a/drivers/tildagon_power/fusb302b/fusb302b_pd.c b/drivers/tildagon_power/fusb302b/fusb302b_pd.c index 094f81f9..ece4aa26 100644 --- a/drivers/tildagon_power/fusb302b/fusb302b_pd.c +++ b/drivers/tildagon_power/fusb302b/fusb302b_pd.c @@ -21,64 +21,84 @@ /** * @brief parse and decode the receive buffer - * @param state the comms state onject + * @param state the comms state object * @param fusb the object for the fusb to use */ void fusbpd_decode( pd_state_t* state, fusb_state_t* fusb ) { - uint8_t rx_buffer[32]; /* parse rx buffer */ while ( !fusb_rx_empty( fusb ) ) { - fusb_get_fifo( fusb, rx_buffer, 1 ); - if ( rx_buffer[0] == RX_SOP ) + uint8_t buffer = 0; + fusb_get_fifo( fusb, &buffer, 1U ); + if ( buffer == RX_SOP ) { - fusb_get_fifo( fusb, state->last_rx_header.raw, 2 ); + pd_header_union_t header; + fusb_get_fifo( fusb, header.raw, 2U ); - if ( state->last_rx_header.sop.number_objects == 0 ) + if ( header.sop.number_objects == 0U ) { /* handle control messages */ - state->last_rx_control_msg_type = state->last_rx_header.sop.message_type; + state->last_rx_control_msg_type = header.sop.message_type; } else { - uint8_t buffer_index = 0U; - if ( state->last_rx_header.sop.message_type == PD_DATA_SOURCE_CAPABILITIES ) + /* handle data messages */ + if ( ( header.sop.message_type == PD_DATA_SOURCE_CAPABILITIES ) + || ( header.sop.message_type == PD_DATA_REQUEST ) ) { for ( uint8_t i = 8U; i; i-- ) { state->pdos[i].raw32 = 0U; } - fusb_get_fifo( fusb, rx_buffer, ( state->last_rx_header.sop.number_objects * 4) + 4 ); - for ( uint8_t i = 0U; i < state->last_rx_header.sop.number_objects; i++ ) - { - state->pdos[i].raw32 = *((uint32_t*)&rx_buffer[ buffer_index ]); - buffer_index += 4U; - } - state->number_of_pdos = state->last_rx_header.sop.number_objects; + fusb_get_fifo( fusb, (uint8_t*)&state->pdos[0].raw32, ( header.sop.number_objects * 4U ) ); + state->number_of_pdos = header.sop.number_objects; + /* discard crc and end of packet */ + uint8_t temp[4]; + fusb_get_fifo( fusb, temp, 4U ); } - else if ( state->last_rx_header.sop.message_type == PD_DATA_VENDOR_DEFINED ) + else if ( header.sop.message_type == PD_DATA_VENDOR_DEFINED ) { - - fusb_get_fifo( fusb, rx_buffer, ( state->last_rx_header.sop.number_objects * 4) + 4 ); - buffer_index += 2U; - uint16_t vendor_id = rx_buffer[ buffer_index ] + ( rx_buffer[ buffer_index + 1 ] << 8 ); - buffer_index += 2U; - if ( vendor_id == PD_VENDOR_ID ) - { - *((uint32_t*)&state->rx_badge_id[0]) = *((uint32_t*)&rx_buffer[buffer_index]); - *((uint32_t*)&state->rx_badge_id[4]) = *((uint32_t*)&rx_buffer[buffer_index + 4]); - buffer_index += 8U; - } - else - { - //todo save this to pass to micropython? - } + fusb_get_fifo( fusb, state->vendor.vendor_data, ( header.sop.number_objects * 4U ) ); + state->vendor.no_objects = header.sop.number_objects; + /* discard crc and end of packet */ + uint8_t temp[4]; + fusb_get_fifo( fusb, temp, 4U ); } - else + else { + /* let outer loop consume the message */ } - state->last_rx_data_msg_type = state->last_rx_header.sop.message_type; + + state->last_rx_data_msg_type = header.sop.message_type; + } + } + else if ( buffer == RX_SOP1 ) + { + if ( state->extra != NULL ) + { + /* store for python */ + fusb_get_fifo( fusb, &state->extra->prime.data[0], 2U ); + pd_header_union_t header; + header.raw[0] = state->extra->prime.data[0]; + header.raw[1] = state->extra->prime.data[1]; + fusb_get_fifo( fusb, &state->extra->prime.data[2], header.sop_prime.number_objects * 4 ); + state->extra->prime.data_size = 2 + ( header.sop_prime.number_objects * 4 ); + state->extra->prime.new_msg = true; + } + } + else if ( buffer == RX_SOP2 ) + { + if ( state->extra != NULL ) + { + /* store for python */ + fusb_get_fifo( fusb, &state->extra->dbl_prime.data[0], 2U ); + pd_header_union_t header; + header.raw[0] = state->extra->dbl_prime.data[0]; + header.raw[1] = state->extra->dbl_prime.data[1]; + fusb_get_fifo( fusb, &state->extra->dbl_prime.data[2], header.sop_prime.number_objects * 4 ); + state->extra->dbl_prime.data_size = 2 + ( header.sop_prime.number_objects * 4 ); + state->extra->dbl_prime.new_msg = true; } } } @@ -86,7 +106,7 @@ void fusbpd_decode( pd_state_t* state, fusb_state_t* fusb ) /** * @brief creat a request power message - * @param state the comms state onject + * @param state the comms state object * @param num the index of the pdo list sent from the source * @param current the current required to run the device * @param max_current the maximum current required by the device @@ -124,7 +144,7 @@ void fusbpd_request_power( pd_state_t* state, uint8_t num, uint16_t current, uin /** * @brief create a request source capabilities message - * @param state the comms state onject + * @param state the comms state object */ void fusbpd_request_capability( pd_state_t* state ) { @@ -145,42 +165,171 @@ void fusbpd_request_capability( pd_state_t* state ) } /** - * @brief create a vendor specific pdo message with the esp32 unique id - * @param state the comms state onject + * @brief create a vendor specific pdo message. + * @param state the comms state object. + * @param data buffer of data to transmit, multiple of 4. first 4 bytes must be the vendor header. + * @param no_objects number of VDOs to transmit. max 7 objects including vendor header + */ +void fusbpd_vendor_specific( pd_state_t* state, uint8_t* data, uint8_t no_objects ) +{ + if( no_objects < 8 ) + { + state->tx_buffer[0] = TX_FIFO_ADDRESS; + state->tx_buffer[1] = TX_SOP1; + state->tx_buffer[2] = TX_SOP1; + state->tx_buffer[3] = TX_SOP1; + state->tx_buffer[4] = TX_SOP2; + state->tx_buffer[5] = TX_PACKSYM | (0x02 + ( no_objects * 4 )) ; + pd_header_union_t header = { 0U }; + header.sop.number_objects = no_objects; + header.sop.message_id = state->msg_id; + header.sop.message_type = 0x0FU; + header.sop.revision = 0x01U; + header.sop.data_role = state->data_role; + header.sop.power_role = state->power_role; + state->tx_buffer[6] = header.raw[0]; + state->tx_buffer[7] = header.raw[1]; + state->msg_id++; + uint8_t index = 0U; + while( index < ( no_objects * 4 ) ) + { + state->tx_buffer[ 8 + index ] = data[index]; + index++; + } + + state->tx_buffer[ 8 + index ] = TX_JAM_CRC; + state->tx_buffer[ 9 + index ] = TX_EOP; + state->tx_buffer[ 10 + index ] = TX_OFF; + state->tx_buffer[ 11 + index ] = TX_ON; + state->message_length = 12 + index ; + } + else + { + /* todo return some error codes */ + } +} + +/** + * @brief create a prime message. + * @param state the comms state object. + * @param header standard message header. + * @param data buffer of data to transmit. + * @param length data length. fusb302 tx buffer is 48 bytes, 16 are needed for tx commands, length 32 max. */ -void fusbpd_vendor_specific( pd_state_t* state ) +void fusbpd_prime( pd_state_t* state, uint8_t* data, uint8_t length ) { + uint8_t len = length - 2U; + /* fill header with protocol layer state */ + pd_header_union_t msgheader = { 0U }; + msgheader.raw[0] = data[0]; + msgheader.raw[1] = data[1]; + msgheader.sop.message_id = state->msg_id; + msgheader.sop.revision = 0x01U; + msgheader.sop.data_role = state->data_role; + msgheader.sop.power_role = state->power_role; + state->msg_id++; + if( state->msg_id > 7 ) + { + state->msg_id = 0; + } + state->tx_buffer[0] = TX_FIFO_ADDRESS; state->tx_buffer[1] = TX_SOP1; state->tx_buffer[2] = TX_SOP1; - state->tx_buffer[3] = TX_SOP1; - state->tx_buffer[4] = TX_SOP2; - state->tx_buffer[5] = TX_PACKSYM | 0x0E; - pd_header_union_t header = { 0U }; - header.sop.number_objects = 0x03U; - header.sop.message_id = state->msg_id; - header.sop.message_type = 0x0FU; - header.sop.revision = 0x01U; - header.sop.data_role = state->data_role; - header.sop.power_role = state->power_role; - state->tx_buffer[6] = header.raw[0]; - state->tx_buffer[7] = header.raw[1]; + state->tx_buffer[3] = TX_SOP3; + state->tx_buffer[4] = TX_SOP3; + uint8_t padding = 0; + if( len % 4 ) + { + padding = 4 - ( len % 4 ); + } + + state->tx_buffer[5] = TX_PACKSYM | (length + padding ); + state->tx_buffer[6] = msgheader.raw[0]; + state->tx_buffer[7] = msgheader.raw[1]; + + uint8_t index = 0U; + while( index < len ) + { + state->tx_buffer[ 8 + index ] = (&data[2])[index]; + index++; + } + + if( padding ) + { + while( index < ( len + padding ) ) + { + state->tx_buffer[ 8 + index ] = 0; + index++; + } + } + + state->tx_buffer[ 8 + index ] = TX_JAM_CRC; + state->tx_buffer[ 9 + index ] = TX_EOP; + state->tx_buffer[ 10 + index ] = TX_OFF; + state->tx_buffer[ 11 + index ] = TX_ON; + state->message_length = 12 + index ; +} + +/** + * @brief create a double prime message. + * @param state the comms state object. + * @param header standard message header. + * @param data buffer of data to transmit. + * @param length data length. fusb302 tx buffer is 48 bytes, 16 are needed for tx commands, length 32 max. + */ +void fusbpd_dbl_prime( pd_state_t* state, uint8_t* data, uint8_t length ) +{ + uint8_t len = length - 2U; + /* fill header with protocol layer state */ + pd_header_union_t msgheader = { 0U }; + msgheader.raw[0] = data[0]; + msgheader.raw[1] = data[1]; + msgheader.sop.message_id = state->msg_id; + msgheader.sop.revision = 0x01U; + msgheader.sop.data_role = state->data_role; + msgheader.sop.power_role = state->power_role; state->msg_id++; - state->tx_buffer[8] = 0x00U; - state->tx_buffer[9] = 0x00U; - state->tx_buffer[10] = PD_VENDOR_ID >> 8; - state->tx_buffer[11] = PD_VENDOR_ID & 0XFF; - state->tx_buffer[12] = state->badge_id[0]; - state->tx_buffer[13] = state->badge_id[1]; - state->tx_buffer[14] = state->badge_id[2]; - state->tx_buffer[15] = state->badge_id[3]; - state->tx_buffer[16] = state->badge_id[4]; - state->tx_buffer[17] = state->badge_id[5]; - state->tx_buffer[18] = state->badge_id[6]; - state->tx_buffer[19] = state->badge_id[7]; - state->tx_buffer[20] = TX_JAM_CRC; - state->tx_buffer[21] = TX_EOP; - state->tx_buffer[22] = TX_OFF; - state->tx_buffer[23] = TX_ON; - state->message_length = 24; + if( state->msg_id > 7 ) + { + state->msg_id = 0; + } + + state->tx_buffer[0] = TX_FIFO_ADDRESS; + state->tx_buffer[1] = TX_SOP1; + state->tx_buffer[2] = TX_SOP3; + state->tx_buffer[3] = TX_SOP1; + state->tx_buffer[4] = TX_SOP3; + uint8_t padding = 0; + if( len % 4 ) + { + padding = 4 - ( len % 4 ); + } + + state->tx_buffer[5] = TX_PACKSYM | (length + padding ); + state->tx_buffer[6] = msgheader.raw[0]; + state->tx_buffer[7] = msgheader.raw[1]; + + uint8_t index = 0U; + while( index < len ) + { + state->tx_buffer[ 8 + index ] = (&data[2])[index]; + index++; + } + + if( padding ) + { + while( index < ( len + padding ) ) + { + state->tx_buffer[ 8 + index ] = 0; + index++; + } + } + + state->tx_buffer[ 8 + index ] = TX_JAM_CRC; + state->tx_buffer[ 9 + index ] = TX_EOP; + state->tx_buffer[ 10 + index ] = TX_OFF; + state->tx_buffer[ 11 + index ] = TX_ON; + state->message_length = 12 + index ; } + diff --git a/drivers/tildagon_power/fusb302b/fusb302b_pd.h b/drivers/tildagon_power/fusb302b/fusb302b_pd.h index ee53b552..7eba5136 100644 --- a/drivers/tildagon_power/fusb302b/fusb302b_pd.h +++ b/drivers/tildagon_power/fusb302b/fusb302b_pd.h @@ -1,12 +1,16 @@ - #ifndef FUSB302B_PD_H #define FUSB302B_PD_H #include #include "fusb302b.h" + +#define PD_FIXED_SUPPLY 0U +#define PD_BATTERY 1U +#define PD_VARIABLE_SUPPLY 2U +#define PD_MAX_TX_MSG_SIZE 50 /* assume TXon command doesn't go into the fifo and one for the device address*/ /* - PD data structures +PD data structures */ typedef enum { @@ -71,6 +75,7 @@ typedef struct typedef union { uint8_t raw[2]; + uint16_t all; pd_sop_header_t sop; pd_sop_prime_header_t sop_prime; } pd_header_union_t; @@ -116,46 +121,39 @@ typedef union pd_variable_pdo_t variable; } pd_source_pdo_union_t; -typedef struct +typedef struct { - uint32_t min_current : 10; - uint32_t current : 10; - uint32_t reserved : 2; - uint32_t epr_cap : 1; - uint32_t uem_suspend : 1; - uint32_t no_suspend : 1; - uint32_t usb_comms : 1; - uint32_t capability_mismatch : 1; - uint32_t give_back : 1; - uint32_t position : 1; -} pd_request_pdo_t; - -typedef union + bool new_msg; + uint8_t vendor_data[28]; + uint8_t no_objects; +} pd_vendor_t; + +typedef struct { - uint32_t raw; - pd_request_pdo_t bits; -} pd_request_pdo_union_t; + bool new_msg; + uint8_t data_size; + uint8_t data[80]; +} pd_prime_t; -#define PD_FIXED_SUPPLY 0U -#define PD_BATTERY 1U -#define PD_VARIABLE_SUPPLY 2U -#define PD_MAX_TX_MSG_SIZE 50 -#define PD_VENDOR_ID 0xCDCD +typedef struct +{ + pd_prime_t prime; + pd_prime_t dbl_prime; +} pd_extras_t; typedef struct { - uint8_t tx_buffer[PD_MAX_TX_MSG_SIZE]; - uint8_t message_length; - uint8_t msg_id; - uint8_t power_role; - uint8_t data_role; - pd_header_union_t last_rx_header; + uint8_t tx_buffer[PD_MAX_TX_MSG_SIZE]; + uint8_t message_length; /* length of tx data */ + uint8_t msg_id; /* id of message, increments each transmit */ + uint8_t power_role; /* 0 sink, 1 source */ + uint8_t data_role; /* 0 UFP, 1 DFP */ pd_source_pdo_union_t pdos[8]; uint8_t number_of_pdos; pd_control_message_type_t last_rx_control_msg_type; - pd_data_message_types_t last_rx_data_msg_type; - uint8_t badge_id[8]; - uint8_t rx_badge_id[8]; + pd_data_message_types_t last_rx_data_msg_type; + pd_vendor_t vendor; + pd_extras_t* extra; } pd_state_t; /** @@ -178,9 +176,35 @@ extern void fusbpd_request_power( pd_state_t* state, uint8_t num, uint16_t curre */ extern void fusbpd_request_capability( pd_state_t* state ); /** - * @brief create a vendor specific pdo message with the esp32 unique id - * @param state the comms state onject + * @brief create a vendor specific pdo message. + * @param state the comms state object. + * @param data buffer of data to transmit, multiple of 4. first 4 bytes must be the vendor header. + * @param no_objects number of VDOs to transmit. max 8 objects including vendor header + */ +extern void fusbpd_vendor_specific( pd_state_t* state, uint8_t* data, uint8_t no_objects ); +/** + * @brief create an extended message. + * @param state the comms state object. + * @param header pd header. + * @param ext_header extended header. data size used for length of message. + * @param data buffer of data to transmit. fusb302 tx buffer is 48 bytes, 16 are needed for tx commands, length 32 max. + */ +extern void fusbpd_extended( pd_state_t* state, uint16_t header, uint16_t ext_header, uint8_t* data ); +/** + * @brief create a unchunked extended message. + * @param state the comms state onject. + * @param header standard message header. + * @param data buffer of data to transmit, must start with the appropriate extended header. + * @param length data length. fusb302 tx buffer is 48 bytes, 16 are needed for tx commands, length 32 max. + */ +extern void fusbpd_prime( pd_state_t* state, uint8_t* data, uint8_t length ); +/** + * @brief create a unchunked extended message. + * @param state the comms state onject. + * @param header standard message header. + * @param data buffer of data to transmit. + * @param length data length. fusb302 tx buffer is 48 bytes, 16 are needed for tx commands, length 32 max. */ -extern void fusbpd_vendor_specific( pd_state_t* state ); +extern void fusbpd_dbl_prime( pd_state_t* state, uint8_t* data, uint8_t length ); #endif /* FUSB302B_PD_H */ diff --git a/drivers/tildagon_power/mp_pd.c b/drivers/tildagon_power/mp_pd.c new file mode 100644 index 00000000..1848c236 --- /dev/null +++ b/drivers/tildagon_power/mp_pd.c @@ -0,0 +1,271 @@ +#include "py/obj.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include +#include "tildagon_power.h" + + +typedef struct _device_obj_t { + mp_obj_base_t base; +} _device_obj_t; + +typedef struct _host_obj_t { + mp_obj_base_t base; +} _host_obj_t; + +static mp_obj_t device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) +{ + _device_obj_t *self = mp_obj_malloc(_device_obj_t, type); + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t host_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) +{ + _host_obj_t *self = mp_obj_malloc(_host_obj_t, type); + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t device_pd_enabled( mp_obj_t self_in ) +{ + return mp_obj_new_bool( device_pd_state == WAITING ); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(device_pd_enabled_obj, device_pd_enabled); + +static mp_obj_t host_pd_enabled( mp_obj_t self_in ) +{ + return mp_obj_new_bool( host_pd_state == WAITING ); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(host_pd_enabled_obj, host_pd_enabled); + +static mp_obj_t device_connected( mp_obj_t self_in ) +{ + return mp_obj_new_bool( device_attach_state == ATTACHED ); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(device_connected_obj, device_connected); + +static mp_obj_t host_connected( mp_obj_t self_in ) +{ + return mp_obj_new_bool( host_attach_state == ATTACHED ); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(host_connected_obj, host_connected); + +static mp_obj_t device_badge_connected( mp_obj_t self_in ) +{ + return mp_obj_new_bool( badge_as_device ); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(device_badge_connected_obj, device_badge_connected); + +static mp_obj_t host_badge_connected( mp_obj_t self_in ) +{ + return mp_obj_new_bool( badge_as_host ); +} + +static MP_DEFINE_CONST_FUN_OBJ_1(host_badge_connected_obj, host_badge_connected); + +static mp_obj_t device_get_vendor_msg( mp_obj_t self_in ) +{ + mp_obj_t buffer = mp_obj_new_list( 0, NULL ); + for ( uint8_t i = 0U; i < ( usb_in.pd.vendor.no_objects * 4); i++ ) + { + mp_obj_list_append( buffer, mp_obj_new_int( usb_in.pd.vendor.vendor_data[i] ) ); + } + + usb_in.pd.vendor.no_objects = 0; + return buffer; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(device_get_vendor_msg_obj, device_get_vendor_msg); + +static mp_obj_t host_get_vendor_msg( mp_obj_t self_in ) +{ + mp_obj_t buffer = mp_obj_new_list( 0, NULL ); + for ( uint8_t i = 0U; i < ( usb_out.pd.vendor.no_objects * 4 ); i++ ) + { + mp_obj_list_append( buffer, mp_obj_new_int( usb_out.pd.vendor.vendor_data[i] ) ); + } + + usb_out.pd.vendor.no_objects = 0; + return buffer; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(host_get_vendor_msg_obj, host_get_vendor_msg); + +static mp_obj_t host_get_prime_msg( mp_obj_t self_in ) +{ + mp_obj_t buffer = mp_obj_new_list(0, NULL); + for ( uint8_t i = 0U; i < usb_out.pd.extra->prime.data_size; i++) + { + mp_obj_list_append(buffer, mp_obj_new_int(usb_out.pd.extra->prime.data[i])); + } + + usb_out.pd.extra->prime.data_size = 0; + + return buffer; +} + +static MP_DEFINE_CONST_FUN_OBJ_1( host_get_prime_msg_obj, host_get_prime_msg); + +static mp_obj_t host_get_dbl_prime_msg( mp_obj_t self_in ) +{ + mp_obj_t buffer = mp_obj_new_list(0, NULL); + for ( uint8_t i = 0U; i < usb_out.pd.extra->dbl_prime.data_size; i++) + { + mp_obj_list_append(buffer, mp_obj_new_int(usb_out.pd.extra->dbl_prime.data[i])); + } + + usb_out.pd.extra->dbl_prime.data_size = 0; + + return buffer; +} + +static MP_DEFINE_CONST_FUN_OBJ_1( host_get_dbl_prime_msg_obj, host_get_dbl_prime_msg); + +static mp_obj_t device_send_vendor_msg( mp_obj_t self_in, mp_obj_t mp_data ) +{ + mp_buffer_info_t data; + mp_get_buffer_raise( mp_data, &data, MP_BUFFER_READ ); + if( data.len > 28 ) + { + return mp_obj_new_int(-ESP_ERR_INVALID_SIZE); + } + if( device_pd_state != WAITING ) + { + return mp_obj_new_int(-ESP_ERR_INVALID_STATE); + } + if (data.len % 4) + { + /* pad to 4 byte boundry */ + uint8_t buffer[28] = { 0 }; + for (uint8_t i = 0; i < data.len; i++) + { + buffer[i] = ((uint8_t*)data.buf)[i]; + } + fusbpd_vendor_specific( &usb_in.pd, buffer, ( data.len / 4 ) + 1 ); + } + else + { + fusbpd_vendor_specific( &usb_in.pd, data.buf, data.len / 4 ); + } + + fusb_send( &usb_in.fusb, usb_in.pd.tx_buffer, usb_in.pd.message_length ); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_2(device_send_vendor_msg_obj, device_send_vendor_msg); + +static mp_obj_t host_send_vendor_msg( mp_obj_t self_in, mp_obj_t mp_data ) +{ + mp_buffer_info_t data; + mp_get_buffer_raise( mp_data, &data, MP_BUFFER_READ ); + if( data.len > 28 ) + { + return mp_obj_new_int(-ESP_ERR_INVALID_SIZE); + } + if( host_pd_state != WAITING ) + { + return mp_obj_new_int(ESP_ERR_INVALID_STATE); + } + if (data.len % 4) + { + /* pad to 4 byte boundry */ + uint8_t buffer[28] = { 0 }; + for (uint8_t i = 0; i < data.len; i++) + { + buffer[i] = ((uint8_t*)data.buf)[i]; + } + fusbpd_vendor_specific( &usb_out.pd, buffer, ( data.len / 4 ) + 1 ); + } + else + { + fusbpd_vendor_specific( &usb_out.pd, data.buf, data.len / 4 ); + } + + fusb_send( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_2(host_send_vendor_msg_obj, host_send_vendor_msg); + +static mp_obj_t host_send_prime_msg( mp_obj_t self_in, mp_obj_t mp_data ) +{ + mp_buffer_info_t data; + mp_get_buffer_raise(mp_data, &data, MP_BUFFER_READ); + fusbpd_prime( &usb_out.pd, data.buf, data.len ); + fusb_send( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_2( host_send_prime_msg_obj, host_send_prime_msg); + +static mp_obj_t host_send_dbl_prime_msg( mp_obj_t self_in, mp_obj_t mp_data ) +{ + mp_buffer_info_t data; + mp_get_buffer_raise(mp_data, &data, MP_BUFFER_READ); + fusbpd_dbl_prime( &usb_out.pd, data.buf, data.len ); + fusb_send( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_2( host_send_dbl_prime_msg_obj, host_send_dbl_prime_msg); + + +static const mp_rom_map_elem_t device_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&device_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_pd_enabled), MP_ROM_PTR(&device_pd_enabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_badge_connected), MP_ROM_PTR(&device_badge_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_vendor_msg), MP_ROM_PTR(&device_get_vendor_msg_obj) }, + { MP_ROM_QSTR(MP_QSTR_send_vendor_msg), MP_ROM_PTR(&device_send_vendor_msg_obj) }, +}; + +static MP_DEFINE_CONST_DICT(device_locals_dict, device_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + class_type_Device, + MP_QSTR_Device, + MP_TYPE_FLAG_NONE, + make_new, device_make_new, + locals_dict, &device_locals_dict + ); + +static const mp_rom_map_elem_t host_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_connected), MP_ROM_PTR(&host_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_pd_enabled), MP_ROM_PTR(&host_pd_enabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_badge_connected), MP_ROM_PTR(&host_badge_connected_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_vendor_msg), MP_ROM_PTR(&host_get_vendor_msg_obj) }, + { MP_ROM_QSTR(MP_QSTR_send_vendor_msg), MP_ROM_PTR(&host_send_vendor_msg_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_prime_msg), MP_ROM_PTR(&host_get_prime_msg_obj ) }, + { MP_ROM_QSTR(MP_QSTR_get_dbl_prime_msg), MP_ROM_PTR(&host_get_dbl_prime_msg_obj ) }, + { MP_ROM_QSTR(MP_QSTR_send_prime_msg), MP_ROM_PTR(&host_send_prime_msg_obj ) }, + { MP_ROM_QSTR(MP_QSTR_send_dbl_prime_msg), MP_ROM_PTR(&host_send_dbl_prime_msg_obj ) }, +}; + +static MP_DEFINE_CONST_DICT(host_locals_dict, host_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + class_type_Host, + MP_QSTR_Host, + MP_TYPE_FLAG_NONE, + make_new, host_make_new, + locals_dict, &host_locals_dict + ); + +static const mp_rom_map_elem_t pd_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_pd) }, + { MP_ROM_QSTR(MP_QSTR_Device), MP_ROM_PTR(&class_type_Device) }, + { MP_ROM_QSTR(MP_QSTR_Host), MP_ROM_PTR(&class_type_Host) }, +}; + +static MP_DEFINE_CONST_DICT(pd_module_globals, pd_module_globals_table); + +const mp_obj_module_t pd_user_cmodule = +{ + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&pd_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_pd, pd_user_cmodule); diff --git a/drivers/tildagon_power/mp_power.c b/drivers/tildagon_power/mp_power.c index ee377d9d..774e5b96 100644 --- a/drivers/tildagon_power/mp_power.c +++ b/drivers/tildagon_power/mp_power.c @@ -80,13 +80,13 @@ static mp_obj_t power_BatteryLevel( void ) float max_current = 1.536F; float vbat_cv_percent = 20.0F; float vbat_ci_percent = 100.0F - vbat_cv_percent; - if ( usb_in.fusb.input_current_limit == 1500U ) + if ( input_current_limit == 1500U ) { max_current = 1.3F; vbat_cv_percent = 17.0F; vbat_ci_percent = 100.0F - vbat_cv_percent; } - else if ( usb_in.fusb.input_current_limit == 500 ) + else if ( input_current_limit == 500 ) { max_current = 0.4F; vbat_cv_percent = 2.0F; @@ -199,7 +199,7 @@ static mp_obj_t power_SupplyCapabilities( void ) bq_update_state( &pmic ); mp_obj_t tuple[3]; tuple[0] = mp_obj_new_str( "non-pd", 6 ); - tuple[1] = mp_obj_new_int( usb_in.fusb.input_current_limit ); + tuple[1] = mp_obj_new_int( input_current_limit ); tuple[2] = mp_obj_new_float( pmic.vbus ); mp_obj_t capability = mp_obj_new_tuple(3, tuple); mp_obj_list_append(capabilities, capability); diff --git a/drivers/tildagon_power/mp_power_event.c b/drivers/tildagon_power/mp_power_event.c index b36903cc..368a88f0 100644 --- a/drivers/tildagon_power/mp_power_event.c +++ b/drivers/tildagon_power/mp_power_event.c @@ -77,6 +77,70 @@ static mp_obj_t mp_power_lanyard_detach_set_cb(mp_obj_t cb) static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_lanyard_detach_set_cb_obj, mp_power_lanyard_detach_set_cb); +static mp_obj_t mp_power_badge_as_device_attach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_BADGE_AS_DEVICE_ATTACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_badge_as_device_attach_set_cb_obj, mp_power_badge_as_device_attach_set_cb); + +static mp_obj_t mp_power_badge_as_device_detach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_BADGE_AS_DEVICE_DETACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_badge_as_device_detach_set_cb_obj, mp_power_badge_as_device_detach_set_cb); + +static mp_obj_t mp_power_badge_as_host_attach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_BADGE_AS_HOST_ATTACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_badge_as_host_attach_set_cb_obj, mp_power_badge_as_host_attach_set_cb); + +static mp_obj_t mp_power_badge_as_host_detach_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_BADGE_AS_HOST_DETACH] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_badge_as_host_detach_set_cb_obj, mp_power_badge_as_host_detach_set_cb); + +static mp_obj_t mp_power_device_vendor_msg_rx_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_DEVICE_VENDOR_MSG_RX] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_device_vendor_msg_rx_set_cb_obj, mp_power_device_vendor_msg_rx_set_cb); + +static mp_obj_t mp_power_host_vendor_msg_rx_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_HOST_VENDOR_MSG_RX] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_event_host_vendor_msg_rx_set_cb_obj, mp_power_host_vendor_msg_rx_set_cb); + +static mp_obj_t mp_power_host_prime_msg_rx_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_HOST_PRIME_MSG_RX] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_host_prime_msg_rx_set_cb_obj, mp_power_host_prime_msg_rx_set_cb); + +static mp_obj_t mp_power_host_dbl_prime_msg_rx_set_cb(mp_obj_t cb) +{ + callbacks[MP_POWER_EVENT_HOST_DBL_PRIME_MSG_RX] = cb; + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mp_power_host_dbl_prime_msg_rx_set_cb_obj, mp_power_host_dbl_prime_msg_rx_set_cb); + static const mp_rom_map_elem_t power_event_globals_table[] = { @@ -89,6 +153,14 @@ static const mp_rom_map_elem_t power_event_globals_table[] = { MP_ROM_QSTR(MP_QSTR_set_device_detach_cb), MP_ROM_PTR(&mp_power_event_device_detach_set_cb_obj) }, { MP_ROM_QSTR(MP_QSTR_set_lanyard_attach_cb), MP_ROM_PTR(&mp_power_event_lanyard_attach_set_cb_obj) }, { MP_ROM_QSTR(MP_QSTR_set_lanyard_detach_cb), MP_ROM_PTR(&mp_power_event_lanyard_detach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_badge_as_device_attach_cb), MP_ROM_PTR(&mp_power_badge_as_device_attach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_badge_as_device_detach_cb), MP_ROM_PTR(&mp_power_badge_as_device_detach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_badge_as_host_attach_cb), MP_ROM_PTR(&mp_power_badge_as_host_attach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_badge_as_host_detach_cb), MP_ROM_PTR(&mp_power_badge_as_host_detach_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_device_vendor_msg_rx_cb), MP_ROM_PTR(&mp_power_event_device_vendor_msg_rx_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_host_vendor_msg_rx_cb), MP_ROM_PTR(&mp_power_event_host_vendor_msg_rx_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_host_prime_msg_rx_cb), MP_ROM_PTR(&mp_power_host_prime_msg_rx_set_cb_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_host_dbl_prime_msg_rx_cb), MP_ROM_PTR(&mp_power_host_dbl_prime_msg_rx_set_cb_obj) }, }; static MP_DEFINE_CONST_DICT(power_event_globals, power_event_globals_table); diff --git a/drivers/tildagon_power/mp_power_event.h b/drivers/tildagon_power/mp_power_event.h index b7bfd167..6b926fef 100644 --- a/drivers/tildagon_power/mp_power_event.h +++ b/drivers/tildagon_power/mp_power_event.h @@ -14,6 +14,14 @@ typedef enum MP_POWER_EVENT_DEVICE_DETACH, MP_POWER_EVENT_LANYARD_ATTACH, MP_POWER_EVENT_LANYARD_DETACH, + MP_POWER_EVENT_BADGE_AS_HOST_ATTACH, + MP_POWER_EVENT_BADGE_AS_HOST_DETACH, + MP_POWER_EVENT_BADGE_AS_DEVICE_ATTACH, + MP_POWER_EVENT_BADGE_AS_DEVICE_DETACH, + MP_POWER_EVENT_DEVICE_VENDOR_MSG_RX, + MP_POWER_EVENT_HOST_VENDOR_MSG_RX, + MP_POWER_EVENT_HOST_PRIME_MSG_RX, + MP_POWER_EVENT_HOST_DBL_PRIME_MSG_RX, MP_POWER_EVENT_MAX } mp_power_event_t; diff --git a/drivers/tildagon_power/tildagon_power.c b/drivers/tildagon_power/tildagon_power.c index 719669c7..20ca34c9 100644 --- a/drivers/tildagon_power/tildagon_power.c +++ b/drivers/tildagon_power/tildagon_power.c @@ -12,26 +12,7 @@ #include "tildagon_power.h" #include "mp_power_event.h" -typedef enum -{ - DISABLED, - UNATTACHED, - ATTACHED, - MAX_STATES -} attach_machine_state_t; - -typedef enum -{ - NOT_STARTED = 0x00, - WAITING = 0x01, - POWER_REQUESTED = 0x02, - PSU_READY_RECEIVED = 0x03, - BADGE_TO_BADGE = 0x04, - LANYARD = 0x05, - REQUEST_RETRY_FAIL = 0x06, - VENDOR_SENT = 0x07, - VENDOR_RETRY_FAIL = 0x08, -} pd_machine_state_t; +#define PD_VENDOR_ID CONFIG_TINYUSB_DESC_CUSTOM_VID typedef enum { @@ -55,17 +36,17 @@ typedef void (*funptr_t)( event_t ); void host_disabled_handler( event_t event ); void host_unattached_handler( event_t event ); -void host_attached_handler ( event_t event ); -void device_disabled_handler ( event_t event ); +void host_attached_handler( event_t event ); +void device_disabled_handler( event_t event ); void device_unattached_handler( event_t event ); void device_attached_handler( event_t event ); -void device_pd_machine ( event_t event ); +void device_pd( event_t event ); +void host_pd( event_t event ); void generate_events( void ); -void determine_input_current_limit ( usb_state_t* state ); +void determine_input_current_limit( usb_state_t* state ); void clean_in( void ); void clean_out( void ); - funptr_t host_attach_machine[MAX_STATES] = { host_disabled_handler, @@ -83,15 +64,23 @@ funptr_t device_attach_machine[MAX_STATES] = bq_state_t pmic = { 0 }; usb_state_t usb_in = { 0 }; usb_state_t usb_out = { 0 }; - -static attach_machine_state_t host_attach_state = DISABLED; -static attach_machine_state_t device_attach_state = DISABLED; -static pd_machine_state_t host_pd_state = NOT_STARTED; -static pd_machine_state_t device_pd_state = NOT_STARTED; +uint16_t input_current_limit = 500; +bool badge_as_device = false; +bool badge_as_host = false; +attach_machine_state_t host_attach_state = DISABLED; +attach_machine_state_t device_attach_state = DISABLED; +pd_machine_state_t host_pd_state = NOT_STARTED; +pd_machine_state_t device_pd_state = NOT_STARTED; + +static uint8_t tildagon_message[20] = { 0x00, 0x00, PD_VENDOR_ID & 0xFF, PD_VENDOR_ID >> 8, + 0x54, 0x69, 0x6C, 0x64, + 0x61, 0x67, 0x6F, 0x6E, + 0x42, 0x65, 0x73, 0x74, + 0x61, 0x67, 0x6F, 0x6E }; static TaskHandle_t tildagon_power_task_handle = NULL; static QueueHandle_t event_queue; -bool lanyard_mode = false; - +static pd_extras_t host_pd_extras; +static bool lanyard_mode = false; /** * @brief fast rate task to handle the interrupt generated events */ @@ -101,6 +90,9 @@ void tildagon_power_fast_task(void *param __attribute__((__unused__))) usb_in.fusb.mux_port = tildagon_get_mux_obj( 7 ); usb_out.fusb.mux_port = tildagon_get_mux_obj( 0 ); pmic.mux_port = tildagon_get_mux_obj( 7 ); + usb_out.pd.power_role = 1; + usb_out.pd.data_role = 1; + usb_out.pd.extra = &host_pd_extras; // turn off 5V switch before setting up PMIC as the reset will enable the boost. aw9523b_pin_set_output( &ext_pin[2], 4, false); bq_init( &pmic ); @@ -241,13 +233,18 @@ void host_unattached_handler( event_t event ) { host_attach_state = ATTACHED; fusb_mask_interrupt_bclevel( &usb_out.fusb, 1 ); - tildagon_power_enable_5v(true); - fusb_setup_pd(&usb_out.fusb ); - fusb_mask_interrupt_retryfail( &usb_out.fusb, 0 ); - fusb_mask_interrupt_txsent( &usb_out.fusb, 0 ); - fusbpd_vendor_specific( &usb_out.pd ); - fusb_send ( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); - host_pd_state = VENDOR_SENT; + tildagon_power_enable_5v(true); + /* don't set up comms when both CC pins have Ra attached */ + if ( usb_out.fusb.cc_select < 3 ) + { + fusb_setup_pd( &usb_out.fusb ); + fusb_set_vcon( &usb_out.fusb, ( usb_out.fusb.cc_select ^ 3 ) & 0x03 ); + fusb_mask_interrupt_retryfail( &usb_out.fusb, 0 ); + fusb_mask_interrupt_txsent( &usb_out.fusb, 0 ); + fusbpd_vendor_specific( &usb_out.pd, tildagon_message, 5 ); + fusb_send ( &usb_out.fusb, usb_out.pd.tx_buffer, usb_out.pd.message_length ); + host_pd_state = WAITING; + } push_event( MP_POWER_EVENT_HOST_ATTACH ); } } @@ -265,15 +262,13 @@ void host_attached_handler( event_t event ) } else { - //todo host pd state machine for badge to badge - if ( host_pd_state > NOT_STARTED ) + if ( host_pd_state >= WAITING ) { - //host_pd_machine( event ); + host_pd( event ); } } } - /** * @brief handler for device events possible when nothing is attached */ @@ -300,6 +295,7 @@ void device_disabled_handler( event_t event ) */ void device_unattached_handler( event_t event ) { + if ( event == DEVICE_ATTACH ) { device_attach_state = ATTACHED; @@ -308,13 +304,17 @@ void device_unattached_handler( event_t event ) else if ( event == DEVICE_BC_LEVEL ) { determine_input_current_limit( &usb_in ); - if ( ( usb_in.fusb.input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) + if ( ( input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) { fusb_setup_pd( &usb_in.fusb ); device_pd_state = WAITING; } fusb_mask_interrupt_bclevel( &usb_in.fusb, 1 ); } + else if ( event == DEVICE_GOODCRCSENT ) + { + device_pd( event ); + } } /** @@ -322,6 +322,7 @@ void device_unattached_handler( event_t event ) */ void device_attached_handler( event_t event ) { + if ( event == DEVICE_DETACH) { device_attach_state = DISABLED; @@ -331,88 +332,172 @@ void device_attached_handler( event_t event ) else if ( event == DEVICE_BC_LEVEL ) { determine_input_current_limit( &usb_in ); - if ( ( usb_in.fusb.input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) + if ( ( input_current_limit >= 1500 ) && ( device_pd_state == NOT_STARTED ) ) { fusb_setup_pd( &usb_in.fusb ); device_pd_state = WAITING; } fusb_mask_interrupt_bclevel( &usb_in.fusb, 1 ); } - else + else if ( event == DEVICE_GOODCRCSENT ) { - if ( device_pd_state > NOT_STARTED ) - { - device_pd_machine( event ); - } + device_pd( event ); } } - /** - * @brief state machine for the device pd comms + * @brief state machine for the host pd comms */ -void device_pd_machine ( event_t event ) +void host_pd ( event_t event ) { - switch ( device_pd_state ) + if ( host_pd_state == WAITING ) { - case WAITING: + if ( event == HOST_GOODCRCSENT ) { - if ( event == DEVICE_GOODCRCSENT ) - { - fusbpd_decode( &usb_in.pd, &usb_in.fusb ); - if ( usb_in.pd.last_rx_data_msg_type == PD_DATA_SOURCE_CAPABILITIES ) + fusbpd_decode( &usb_out.pd, &usb_out.fusb ); + switch( usb_out.pd.last_rx_data_msg_type ) + { + case PD_DATA_VENDOR_DEFINED: { - /* - We only need 5V so can use the first object, from the usb 3 standard: - The vSafe5V Fixed Supply Object Shall always be the first object. - A Source Shall Not offer multiple Power Data Objects of the same - type (fixed, variable, Battery) and the same Voltage but Shall - instead offer one Power Data Object with the highest available - current for that Source capability and Voltage. - - */ - uint32_t current = usb_in.pd.pdos[0].fixed.max_current * 10; - /* limit current to the maximum current of a non active cable */ - if ( current > 3000 ) + + if ( + ( usb_out.pd.vendor.no_objects == 5 ) + && ( usb_out.pd.vendor.vendor_data[4] == tildagon_message[4] ) + && ( usb_out.pd.vendor.vendor_data[5] == tildagon_message[5] ) + && ( usb_out.pd.vendor.vendor_data[6] == tildagon_message[6] ) + && ( usb_out.pd.vendor.vendor_data[7] == tildagon_message[7] ) + ) { - current = 3000; + push_event(MP_POWER_EVENT_BADGE_AS_HOST_ATTACH); + badge_as_host = true; } - fusbpd_request_power( &usb_in.pd, 0, current, current ); - fusb_send( &usb_in.fusb, usb_in.pd.tx_buffer, usb_in.pd.message_length ); - usb_in.pd.last_rx_data_msg_type = PD_DATA_DO_NOT_USE; - device_pd_state = POWER_REQUESTED; - } - else if( usb_in.pd.last_rx_data_msg_type == PD_DATA_VENDOR_DEFINED ) + else + { + push_event(MP_POWER_EVENT_HOST_VENDOR_MSG_RX); + } + break; + } + case PD_DATA_REQUEST: { - /* ToDo: if vendor pdo received decide on badge to badge and callback? */ + /* + don't respond to this? We don't send capabilities message on attach due + to this being event driven and needing to make multiple attempts to send + and not being able to control the current output, which the sink can + determine from the Rd value. + response would be PD_CONTROL_ACCEPT, PD_CONTROL_PS_RDY + */ + break; + } + default: + { + break; } } - break; - } - case POWER_REQUESTED: - { - if ( event == DEVICE_GOODCRCSENT ) + usb_out.pd.last_rx_data_msg_type = PD_DATA_DO_NOT_USE; + + + switch ( usb_out.pd.last_rx_control_msg_type ) { - fusb_auto_good_crc( &usb_in.fusb ); - fusbpd_decode( &usb_in.pd, &usb_in.fusb ); - /* if psu ready move state */ - if ( usb_in.pd.last_rx_control_msg_type == PD_CONTROL_PS_RDY ) + case PD_CONTROL_SOFT_RESET: + { + usb_out.pd.msg_id = 0; + break; + } + case PD_CONTROL_GET_SOURCE_CAP: { - device_pd_state = PSU_READY_RECEIVED; + /* respond with 1 PDO 5V, 1500mA */ + break; } + default: + { + break; + } } - break; + usb_out.pd.last_rx_control_msg_type = PD_CONTROL_DO_NOT_USE; + /* look for prime and double prime messages */ + if ( usb_out.pd.extra != NULL ) + { + if ( usb_out.pd.extra->prime.new_msg ) + { + push_event(MP_POWER_EVENT_HOST_PRIME_MSG_RX); + usb_out.pd.extra->prime.new_msg = false; + } + if ( usb_out.pd.extra->dbl_prime.new_msg ) + { + push_event(MP_POWER_EVENT_HOST_DBL_PRIME_MSG_RX); + usb_out.pd.extra->dbl_prime.new_msg = false; + } + } + } + } +} + +/** + * @brief state machine for the device pd comms + */ +void device_pd ( event_t event ) +{ + if ( device_pd_state == WAITING ) + { + fusbpd_decode( &usb_in.pd, &usb_in.fusb ); + if ( usb_in.pd.last_rx_data_msg_type == PD_DATA_SOURCE_CAPABILITIES ) + { + /* + We only need 5V so can use the first object, from the usb 3 standard: + The vSafe5V Fixed Supply Object Shall always be the first object. + A Source Shall Not offer multiple Power Data Objects of the same + type (fixed, variable, Battery) and the same Voltage but Shall + instead offer one Power Data Object with the highest available + current for that Source capability and Voltage. + */ + uint32_t current = usb_in.pd.pdos[0].fixed.max_current * 10; + /* limit current to the maximum current of a non active cable */ + if ( current > 3000 ) + { + current = 3000; + } + fusbpd_request_power( &usb_in.pd, 0, current, current ); + fusb_send( &usb_in.fusb, usb_in.pd.tx_buffer, usb_in.pd.message_length ); + } + else if ( usb_in.pd.last_rx_data_msg_type == PD_DATA_VENDOR_DEFINED ) + { + if ( + ( usb_in.pd.vendor.no_objects == 5 ) + && ( usb_in.pd.vendor.vendor_data[4] == tildagon_message[4] ) + && ( usb_in.pd.vendor.vendor_data[5] == tildagon_message[5] ) + && ( usb_in.pd.vendor.vendor_data[6] == tildagon_message[6] ) + && ( usb_in.pd.vendor.vendor_data[7] == tildagon_message[7] ) + ) + { + push_event( MP_POWER_EVENT_BADGE_AS_DEVICE_ATTACH ); + badge_as_device = true; + } + else + { + push_event( MP_POWER_EVENT_DEVICE_VENDOR_MSG_RX ); + } + } + else if ( usb_in.pd.last_rx_data_msg_type == PD_DATA_SINK_CAPABILITIES ) + { + /* reply with what we need as a sink */ + } + usb_in.pd.last_rx_data_msg_type = PD_DATA_DO_NOT_USE; + + if ( usb_in.pd.last_rx_control_msg_type == PD_CONTROL_SOFT_RESET ) + { + usb_in.pd.msg_id = 0U; + } + else if ( usb_in.pd.last_rx_control_msg_type == PD_CONTROL_PS_RDY ) + { + /* negotiating is complete */ } - case PSU_READY_RECEIVED: - case BADGE_TO_BADGE: - default: + else { - /* stay in this state until detach */ + /* ignore all other control messages */ } } } - /** * @brief Determines if the interrupt was a USB event and which one. */ @@ -426,6 +511,8 @@ void generate_events( void ) { bq_enable_HiZ_input( &pmic, 1 ); lanyard_mode = true; + host_pd_state = LANYARD; + device_pd_state = LANYARD; push_event(MP_POWER_EVENT_LANYARD_ATTACH); } if ( prev_status == pmic.status ) @@ -527,20 +614,20 @@ void generate_events( void ) */ void determine_input_current_limit ( usb_state_t* state ) { - state->fusb.input_current_limit = 500; + input_current_limit = 500; uint16_t bc_level = state->fusb.status & FUSB_STATUS_BCLVL_MASK; if ( bc_level > 0 ) { if ( bc_level == 2 ) { - state->fusb.input_current_limit = 1500U; + input_current_limit = 1500U; } else if ( ( bc_level == 3 ) && ( ( state->fusb.status & FUSB_STATUS_COMP_MASK ) == 0 ) ) { - state->fusb.input_current_limit = 3000U; + input_current_limit = 3000U; } } - bq_set_input_current_limit( &pmic, (float) state->fusb.input_current_limit ); + bq_set_input_current_limit( &pmic, (float)input_current_limit ); } /** @@ -548,18 +635,20 @@ void determine_input_current_limit ( usb_state_t* state ) */ void clean_in( void ) { - usb_in.fusb.input_current_limit = 500; - bq_set_input_current_limit( &pmic, (float) usb_in.fusb.input_current_limit ); + if ( badge_as_device ) + { + badge_as_device = false; + push_event(MP_POWER_EVENT_BADGE_AS_DEVICE_DETACH); + } + input_current_limit = 500; + bq_set_input_current_limit( &pmic, (float)input_current_limit ); device_pd_state = NOT_STARTED; usb_in.fusb.cc_select = 0U; fusb_setup_device( &usb_in.fusb ); - usb_in.pd.last_rx_control_msg_type = 0U; - usb_in.pd.last_rx_data_msg_type = 0U; + usb_in.pd.last_rx_control_msg_type = PD_CONTROL_DO_NOT_USE; + usb_in.pd.last_rx_data_msg_type = PD_DATA_DO_NOT_USE; usb_in.pd.number_of_pdos = 0U; - *((uint16_t*)&usb_in.pd.last_rx_header.raw[0]) = 0U; usb_in.pd.msg_id = 0U; - *((uint32_t*)&usb_in.pd.rx_badge_id[0]) = 0U; - *((uint32_t*)&usb_in.pd.rx_badge_id[4]) = 0U; } /** @@ -567,25 +656,20 @@ void clean_in( void ) */ void clean_out( void ) { + if ( badge_as_host ) + { + badge_as_host = false; + push_event(MP_POWER_EVENT_BADGE_AS_HOST_DETACH); + } tildagon_power_enable_5v(false); host_pd_state = NOT_STARTED; usb_in.fusb.cc_select = 0; fusb_setup_host( &usb_out.fusb ); - usb_out.pd.last_rx_control_msg_type = 0U; - usb_out.pd.last_rx_data_msg_type = 0U; + fusb_set_vcon( &usb_out.fusb, 0 ); + usb_out.pd.last_rx_control_msg_type = PD_CONTROL_DO_NOT_USE; + usb_out.pd.last_rx_data_msg_type = PD_DATA_DO_NOT_USE; usb_out.pd.number_of_pdos = 0U; usb_out.pd.msg_id = 0U; - - *((uint16_t*)&usb_out.pd.last_rx_header.raw[0]) = 0U; - *((uint32_t*)&usb_out.pd.rx_badge_id[0]) = 0U; - *((uint32_t*)&usb_out.pd.rx_badge_id[4]) = 0U; - - /* setup lanyard and badge to badge numbers */ - esp_fill_random( usb_in.pd.badge_id, 8 ); - for ( uint8_t i = 0; i < 8; i++) - { - usb_out.pd.badge_id[i] = usb_in.pd.badge_id[i]; - } if ( lanyard_mode ) { diff --git a/drivers/tildagon_power/tildagon_power.cmake b/drivers/tildagon_power/tildagon_power.cmake index 6a93a8a5..1d5ebad5 100644 --- a/drivers/tildagon_power/tildagon_power.cmake +++ b/drivers/tildagon_power/tildagon_power.cmake @@ -10,6 +10,7 @@ target_sources(usermod_tildagon_power INTERFACE ${CMAKE_CURRENT_LIST_DIR}/fusb302b/fusb302b_pd.c ${CMAKE_CURRENT_LIST_DIR}/mp_power.c ${CMAKE_CURRENT_LIST_DIR}/mp_power_event.c + ${CMAKE_CURRENT_LIST_DIR}/mp_pd.c ) # Add the current directory as an include directory. diff --git a/drivers/tildagon_power/tildagon_power.h b/drivers/tildagon_power/tildagon_power.h index 9d65eb61..d574c8e5 100644 --- a/drivers/tildagon_power/tildagon_power.h +++ b/drivers/tildagon_power/tildagon_power.h @@ -15,10 +15,31 @@ typedef struct pd_state_t pd; } usb_state_t; +typedef enum +{ + DISABLED, + UNATTACHED, + ATTACHED, + MAX_STATES +} attach_machine_state_t; + +typedef enum +{ + NOT_STARTED = 0x00, + WAITING = 0x01, + LANYARD = 0x02, +} pd_machine_state_t; + extern bq_state_t pmic; extern usb_state_t usb_in; extern usb_state_t usb_out; - +extern uint16_t input_current_limit; +extern attach_machine_state_t device_attach_state; +extern attach_machine_state_t host_attach_state; +extern pd_machine_state_t device_pd_state; +extern pd_machine_state_t host_pd_state; +extern bool badge_as_device; +extern bool badge_as_host; /** * @brief initialise the badge power management task */ diff --git a/modules/system/power/events.py b/modules/system/power/events.py index b2a95964..f33d40ff 100644 --- a/modules/system/power/events.py +++ b/modules/system/power/events.py @@ -1,3 +1,6 @@ +from events import Event + + class PowerEvent: def __init__(self, EventName): self.__str__ = EventName @@ -34,3 +37,27 @@ class RequestLanyardAttachEvent(PowerEvent): ... class RequestLanyardDetachEvent(PowerEvent): ... + + +class BadgeAsDeviceAttachEvent(Event): ... + + +class BadgeAsDeviceDetachEvent(Event): ... + + +class BadgeAsHostAttachEvent(Event): ... + + +class BadgeAsHostDetachEvent(Event): ... + + +class VendorMsgDevRxEvent(Event): ... + + +class VendorMsgHostRxEvent(Event): ... + + +class PrimeMsgHostRxEvent(Event): ... + + +class DblPrimeMsgHostRxEvent(Event): ... diff --git a/modules/system/power/handler.py b/modules/system/power/handler.py index d93871c7..72775f22 100644 --- a/modules/system/power/handler.py +++ b/modules/system/power/handler.py @@ -1,6 +1,7 @@ from system.eventbus import eventbus from system.power import events - +from system.power.pd_helper import pdHelper +from system.notification.events import ShowNotificationEvent import power_event as pe @@ -14,6 +15,14 @@ def RegisterDefaultCallbacks(self): pe.set_host_detach_cb(self.HostDetachHandler) pe.set_lanyard_attach_cb(self.LanyardAttachHandler) pe.set_lanyard_detach_cb(self.LanyardDetachHandler) + pe.set_badge_as_device_attach_cb(self.BadgeAsDeviceAttachHandler) + pe.set_badge_as_device_detach_cb(self.BadgeAsDeviceDetachHandler) + pe.set_badge_as_host_attach_cb(self.BadgeAsHostAttachHandler) + pe.set_badge_as_host_detach_cb(self.BadgeAsHostDetachHandler) + pe.set_device_vendor_msg_rx_cb(self.VendorMsgDevRxHandler) + pe.set_host_vendor_msg_rx_cb(self.VendorMsgHostRxHandler) + pe.set_host_prime_msg_rx_cb(self.PrimeMsgHostRxHandler) + pe.set_host_dbl_prime_msg_rx_cb(self.DoublePrimeMsgHostRxHandler) def ChargeEventHandler(self): eventbus.emit( @@ -52,3 +61,33 @@ def LanyardDetachHandler(self): eventbus.emit( events.RequestLanyardDetachEvent(events.PowerEvent("Lanyard Detatched")) ) + + def BadgeAsDeviceAttachHandler(self): + eventbus.emit(ShowNotificationEvent("Badge Connected as Device")) + eventbus.emit(events.BadgeAsDeviceAttachEvent()) + pdh = pdHelper() + pdh.device_send_badge_id() + + def BadgeAsDeviceDetachHandler(self): + eventbus.emit(ShowNotificationEvent("Badge Device disconnected")) + eventbus.emit(events.BadgeAsDeviceDetachEvent()) + + def BadgeAsHostAttachHandler(self): + eventbus.emit(ShowNotificationEvent("Badge Connected as Host")) + eventbus.emit(events.BadgeAsHostAttachEvent()) + + def BadgeAsHostDetachHandler(self): + eventbus.emit(ShowNotificationEvent("Badge Host disconnected")) + eventbus.emit(events.BadgeAsHostDetachEvent()) + + def VendorMsgDevRxHandler(self): + eventbus.emit(events.VendorMsgDevRxEvent()) + + def VendorMsgHostRxHandler(self): + eventbus.emit(events.VendorMsgHostRxEvent()) + + def PrimeMsgHostRxHandler(self): + eventbus.emit(events.PrimeMsgHostRxEvent()) + + def DoublePrimeMsgHostRxHandler(self): + eventbus.emit(events.DblPrimeMsgHostRxEvent()) diff --git a/modules/system/power/pd_helper.py b/modules/system/power/pd_helper.py new file mode 100644 index 00000000..7e6ffe60 --- /dev/null +++ b/modules/system/power/pd_helper.py @@ -0,0 +1,127 @@ +from pd import Host, Device + + +class vdmCmd: + DISCOVER_IDENTITY = 1 + DISCOVER_SVIDS = 2 + DISCOVER_MODES = 3 + ENTER_MODE = 4 + EXIT_MODE = 5 + ATTENTION = 6 + + +class dataType: + BIST = 3 + VENDOR_DEFINED = 15 + + +class cmdType: + ACCEPT = 3 + REJECT = 4 + SOFT_RESET = 13 + + +class pdHelper: + def pd_header(self, message_type, no_objects=0): + # the majority of the header is filled in by the badge + header = (no_objects << 12) | message_type + return header + + def vdm_structured_header(self, command, SVID=0xFF00, obj_pos=0, Version=0): + vdmh = (SVID << 16) | (1 << 15) | (Version << 13) | (obj_pos << 8) | command + return vdmh + + def vdm_unstructured_header(self, SVID=0xFF00, data=0): + vdmh = (SVID << 16) | (0x7FFF & data) + return vdmh + + def vdm_header_extract(self, vendor_header): + header = { + "SVID": vendor_header >> 16, + "structured": (vendor_header >> 15) & 0x01, + "data": None, + "Version": None, + "obj_pos": None, + "cmd_type": None, + "cmd": None, + } + if header["structured"] == 1: + header["Version"] = (vendor_header >> 13) & 0x03 + header["obj_pos"] = (vendor_header >> 8) & 0x07 + header["cmd_type"] = (vendor_header >> 6) & 0x03 + header["cmd"] = vendor_header & 0x3F + else: + header["data"] = vendor_header & 0x7FFF + return header + + def host_disc_id_prime(self): + header = self.pd_header(dataType.VENDOR_DEFINED, 1) + data = self.vdm_structured_header(vdmCmd.DISCOVER_IDENTITY) + usb_out = Host() + usb_out.send_prime_msg( + header.to_bytes(2, "little") + data.to_bytes(4, "little") + ) + + def host_disc_id_dbl_prime(self): + header = self.pd_header(dataType.VENDOR_DEFINED, 1) + data = self.vdm_structured_header(vdmCmd.DISCOVER_IDENTITY) + usb_out = Host() + usb_out.send_dbl_prime_msg( + header.to_bytes(2, "little") + data.to_bytes(4, "little") + ) + + def host_send_badge_id(self): + tildagon_message = bytearray( + [ + 0x00, + 0x00, + 0x00, + 0xFF, + 0x54, + 0x69, + 0x6C, + 0x64, + 0x61, + 0x67, + 0x6F, + 0x6E, + 0x42, + 0x65, + 0x73, + 0x74, + 0x61, + 0x67, + 0x6F, + 0x6E, + ] + ) + usb_out = Host() + usb_out.send_vendor_msg(tildagon_message) + + def device_send_badge_id(self): + tildagon_message = bytearray( + [ + 0x00, + 0x00, + 0x00, + 0xFF, + 0x54, + 0x69, + 0x6C, + 0x64, + 0x61, + 0x67, + 0x6F, + 0x6E, + 0x42, + 0x65, + 0x73, + 0x74, + 0x61, + 0x67, + 0x6F, + 0x6E, + ] + ) + usb_in = Device() + usb_in.send_vendor_msg(tildagon_message)