./workspace/TS100 -> ./source/
This commit is contained in:
305
source/Core/Drivers/FUSB302/fusb302b.h
Normal file
305
source/Core/Drivers/FUSB302/fusb302b.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_FUSB302B_H
|
||||
#define PDB_FUSB302B_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pd.h"
|
||||
#include <pdb_msg.h>
|
||||
|
||||
/* I2C addresses of the FUSB302B chips */
|
||||
#define FUSB302B_ADDR (0x22<<1)
|
||||
#define FUSB302B01_ADDR (0x23<<1)
|
||||
#define FUSB302B10_ADDR (0x24<<1)
|
||||
#define FUSB302B11_ADDR (0x25<<1)
|
||||
|
||||
/* Device ID register */
|
||||
#define FUSB_DEVICE_ID 0x01
|
||||
#define FUSB_DEVICE_ID_VERSION_ID_SHIFT 4
|
||||
#define FUSB_DEVICE_ID_VERSION_ID (0xF << FUSB_DEVICE_ID_VERSION_ID_SHIFT)
|
||||
#define FUSB_DEVICE_ID_PRODUCT_ID_SHIFT 2
|
||||
#define FUSB_DEVICE_ID_PRODUCT_ID (0x3 << FUSB_DEVICE_ID_PRODUCT_ID_SHIFT)
|
||||
#define FUSB_DEVICE_ID_REVISION_ID_SHIFT 0
|
||||
#define FUSB_DEVICE_ID_REVISION_ID (0x3 << FUSB_DEVICE_ID_REVISION_ID_SHIFT)
|
||||
|
||||
/* Switches0 register */
|
||||
#define FUSB_SWITCHES0 0x02
|
||||
#define FUSB_SWITCHES0_PU_EN2 (1 << 7)
|
||||
#define FUSB_SWITCHES0_PU_EN1 (1 << 6)
|
||||
#define FUSB_SWITCHES0_VCONN_CC2 (1 << 5)
|
||||
#define FUSB_SWITCHES0_VCONN_CC1 (1 << 4)
|
||||
#define FUSB_SWITCHES0_MEAS_CC2 (1 << 3)
|
||||
#define FUSB_SWITCHES0_MEAS_CC1 (1 << 2)
|
||||
#define FUSB_SWITCHES0_PDWN_2 (1 << 1)
|
||||
#define FUSB_SWITCHES0_PDWN_1 1
|
||||
|
||||
/* Switches1 register */
|
||||
#define FUSB_SWITCHES1 0x03
|
||||
#define FUSB_SWITCHES1_POWERROLE (1 << 7)
|
||||
#define FUSB_SWITCHES1_SPECREV_SHIFT 5
|
||||
#define FUSB_SWITCHES1_SPECREV (0x3 << FUSB_SWITCHES1_SPECREV_SHIFT)
|
||||
#define FUSB_SWITCHES1_DATAROLE (1 << 4)
|
||||
#define FUSB_SWITCHES1_AUTO_CRC (1 << 2)
|
||||
#define FUSB_SWITCHES1_TXCC2 (1 << 1)
|
||||
#define FUSB_SWITCHES1_TXCC1 1
|
||||
|
||||
/* Measure register */
|
||||
#define FUSB_MEASURE 0x04
|
||||
#define FUSB_MEASURE_MEAS_VBUS (1 << 6)
|
||||
#define FUSB_MEASURE_MDAC_SHIFT 0
|
||||
#define FUSB_MEASURE_MDAC (0x3F << FUSB_MEASURE_MDAC_SHIFT)
|
||||
|
||||
/* Slice register */
|
||||
#define FUSB_SLICE 0x05
|
||||
#define FUSB_SLICE_SDAC_HYS_SHIFT 6
|
||||
#define FUSB_SLICE_SDAC_HYS (0x3 << FUSB_SLICE_SDAC_HYS_SHIFT)
|
||||
#define FUSB_SLICE_SDAC_SHIFT 0
|
||||
#define FUSB_SLICE_SDAC (0x3F << FUSB_SLICE_SDAC_SHIFT)
|
||||
|
||||
/* Control0 register */
|
||||
#define FUSB_CONTROL0 0x06
|
||||
#define FUSB_CONTROL0_TX_FLUSH (1 << 6)
|
||||
#define FUSB_CONTROL0_INT_MASK (1 << 5)
|
||||
#define FUSB_CONTROL0_HOST_CUR_SHIFT 2
|
||||
#define FUSB_CONTROL0_HOST_CUR (0x3 << FUSB_CONTROL0_HOST_CUR_SHIFT)
|
||||
#define FUSB_CONTROL0_AUTO_PRE (1 << 1)
|
||||
#define FUSB_CONTROL0_TX_START 1
|
||||
|
||||
/* Control1 register */
|
||||
#define FUSB_CONTROL1 0x07
|
||||
#define FUSB_CONTROL1_ENSOP2DB (1 << 6)
|
||||
#define FUSB_CONTROL1_ENSOP1DB (1 << 5)
|
||||
#define FUSB_CONTROL1_BIST_MODE2 (1 << 4)
|
||||
#define FUSB_CONTROL1_RX_FLUSH (1 << 2)
|
||||
#define FUSB_CONTROL1_ENSOP2 (1 << 1)
|
||||
#define FUSB_CONTROL1_ENSOP1 1
|
||||
|
||||
/* Control2 register */
|
||||
#define FUSB_CONTROL2 0x08
|
||||
#define FUSB_CONTROL2_TOG_SAVE_PWR_SHIFT 6
|
||||
#define FUSB_CONTROL2_TOG_SAVE_PWR (0x3 << FUSB_CONTROL2_TOG_SAVE_PWR)
|
||||
#define FUSB_CONTROL2_TOG_RD_ONLY (1 << 5)
|
||||
#define FUSB_CONTROL2_WAKE_EN (1 << 3)
|
||||
#define FUSB_CONTROL2_MODE_SHIFT 1
|
||||
#define FUSB_CONTROL2_MODE (0x3 << FUSB_CONTROL2_MODE_SHIFT)
|
||||
#define FUSB_CONTROL2_TOGGLE 1
|
||||
|
||||
/* Control3 register */
|
||||
#define FUSB_CONTROL3 0x09
|
||||
#define FUSB_CONTROL3_SEND_HARD_RESET (1 << 6)
|
||||
#define FUSB_CONTROL3_BIST_TMODE (1 << 5)
|
||||
#define FUSB_CONTROL3_AUTO_HARDRESET (1 << 4)
|
||||
#define FUSB_CONTROL3_AUTO_SOFTRESET (1 << 3)
|
||||
#define FUSB_CONTROL3_N_RETRIES_SHIFT 1
|
||||
#define FUSB_CONTROL3_N_RETRIES (0x3 << FUSB_CONTROL3_N_RETRIES_SHIFT)
|
||||
#define FUSB_CONTROL3_AUTO_RETRY 1
|
||||
|
||||
/* Mask1 register */
|
||||
#define FUSB_MASK1 0x0A
|
||||
#define FUSB_MASK1_M_VBUSOK (1 << 7)
|
||||
#define FUSB_MASK1_M_ACTIVITY (1 << 6)
|
||||
#define FUSB_MASK1_M_COMP_CHNG (1 << 5)
|
||||
#define FUSB_MASK1_M_CRC_CHK (1 << 4)
|
||||
#define FUSB_MASK1_M_ALERT (1 << 3)
|
||||
#define FUSB_MASK1_M_WAKE (1 << 2)
|
||||
#define FUSB_MASK1_M_COLLISION (1 << 1)
|
||||
#define FUSB_MASK1_M_BC_LVL (1 << 0)
|
||||
|
||||
/* Power register */
|
||||
#define FUSB_POWER 0x0B
|
||||
#define FUSB_POWER_PWR3 (1 << 3)
|
||||
#define FUSB_POWER_PWR2 (1 << 2)
|
||||
#define FUSB_POWER_PWR1 (1 << 1)
|
||||
#define FUSB_POWER_PWR0 1
|
||||
|
||||
/* Reset register */
|
||||
#define FUSB_RESET 0x0C
|
||||
#define FUSB_RESET_PD_RESET (1 << 1)
|
||||
#define FUSB_RESET_SW_RES 1
|
||||
|
||||
/* OCPreg register */
|
||||
#define FUSB_OCPREG 0x0D
|
||||
#define FUSB_OCPREG_OCP_RANGE (1 << 3)
|
||||
#define FUSB_OCPREG_OCP_CUR_SHIFT 0
|
||||
#define FUSB_OCPREG_OCP_CUR (0x7 << FUSB_OCPREG_OCP_CUR_SHIFT)
|
||||
|
||||
/* Maska register */
|
||||
#define FUSB_MASKA 0x0E
|
||||
#define FUSB_MASKA_M_OCP_TEMP (1 << 7)
|
||||
#define FUSB_MASKA_M_TOGDONE (1 << 6)
|
||||
#define FUSB_MASKA_M_SOFTFAIL (1 << 5)
|
||||
#define FUSB_MASKA_M_RETRYFAIL (1 << 4)
|
||||
#define FUSB_MASKA_M_HARDSENT (1 << 3)
|
||||
#define FUSB_MASKA_M_TXSENT (1 << 2)
|
||||
#define FUSB_MASKA_M_SOFTRST (1 << 1)
|
||||
#define FUSB_MASKA_M_HARDRST 1
|
||||
|
||||
/* Maskb register */
|
||||
#define FUSB_MASKB 0x0F
|
||||
#define FUSB_MASKB_M_GCRCSENT 1
|
||||
|
||||
/* Control4 register */
|
||||
#define FUSB_CONTROL4 0x10
|
||||
#define FUSB_CONTROL4_TOG_EXIT_AUD 1
|
||||
|
||||
/* Status0a register */
|
||||
#define FUSB_STATUS0A 0x3C
|
||||
#define FUSB_STATUS0A_SOFTFAIL (1 << 5)
|
||||
#define FUSB_STATUS0A_RETRYFAIL (1 << 4)
|
||||
#define FUSB_STATUS0A_POWER3 (1 << 3)
|
||||
#define FUSB_STATUS0A_POWER2 (1 << 2)
|
||||
#define FUSB_STATUS0A_SOFTRST (1 << 1)
|
||||
#define FUSB_STATUS0A_HARDRST 1
|
||||
|
||||
/* Status1a register */
|
||||
#define FUSB_STATUS1A 0x3D
|
||||
#define FUSB_STATUS1A_TOGSS_SHIFT 3
|
||||
#define FUSB_STATUS1A_TOGSS (0x7 << FUSB_STATUS1A_TOGSS_SHIFT)
|
||||
#define FUSB_STATUS1A_RXSOP2DB (1 << 2)
|
||||
#define FUSB_STATUS1A_RXSOP1DB (1 << 1)
|
||||
#define FUSB_STATUS1A_RXSOP 1
|
||||
|
||||
/* Interrupta register */
|
||||
#define FUSB_INTERRUPTA 0x3E
|
||||
#define FUSB_INTERRUPTA_I_OCP_TEMP (1 << 7)
|
||||
#define FUSB_INTERRUPTA_I_TOGDONE (1 << 6)
|
||||
#define FUSB_INTERRUPTA_I_SOFTFAIL (1 << 5)
|
||||
#define FUSB_INTERRUPTA_I_RETRYFAIL (1 << 4)
|
||||
#define FUSB_INTERRUPTA_I_HARDSENT (1 << 3)
|
||||
#define FUSB_INTERRUPTA_I_TXSENT (1 << 2)
|
||||
#define FUSB_INTERRUPTA_I_SOFTRST (1 << 1)
|
||||
#define FUSB_INTERRUPTA_I_HARDRST 1
|
||||
|
||||
/* Interruptb register */
|
||||
#define FUSB_INTERRUPTB 0x3F
|
||||
#define FUSB_INTERRUPTB_I_GCRCSENT 1
|
||||
|
||||
/* Status0 register */
|
||||
#define FUSB_STATUS0 0x40
|
||||
#define FUSB_STATUS0_VBUSOK (1 << 7)
|
||||
#define FUSB_STATUS0_ACTIVITY (1 << 6)
|
||||
#define FUSB_STATUS0_COMP (1 << 5)
|
||||
#define FUSB_STATUS0_CRC_CHK (1 << 4)
|
||||
#define FUSB_STATUS0_ALERT (1 << 3)
|
||||
#define FUSB_STATUS0_WAKE (1 << 2)
|
||||
#define FUSB_STATUS0_BC_LVL_SHIFT 0
|
||||
#define FUSB_STATUS0_BC_LVL (0x3 << FUSB_STATUS0_BC_LVL_SHIFT)
|
||||
|
||||
/* Status1 register */
|
||||
#define FUSB_STATUS1 0x41
|
||||
#define FUSB_STATUS1_RXSOP2 (1 << 7)
|
||||
#define FUSB_STATUS1_RXSOP1 (1 << 6)
|
||||
#define FUSB_STATUS1_RX_EMPTY (1 << 5)
|
||||
#define FUSB_STATUS1_RX_FULL (1 << 4)
|
||||
#define FUSB_STATUS1_TX_EMPTY (1 << 3)
|
||||
#define FUSB_STATUS1_TX_FULL (1 << 2)
|
||||
#define FUSB_STATUS1_OVRTEMP (1 << 1)
|
||||
#define FUSB_STATUS1_OCP 1
|
||||
|
||||
/* Interrupt register */
|
||||
#define FUSB_INTERRUPT 0x42
|
||||
#define FUSB_INTERRUPT_I_VBUSOK (1 << 7)
|
||||
#define FUSB_INTERRUPT_I_ACTIVITY (1 << 6)
|
||||
#define FUSB_INTERRUPT_I_COMP_CHNG (1 << 5)
|
||||
#define FUSB_INTERRUPT_I_CRC_CHK (1 << 4)
|
||||
#define FUSB_INTERRUPT_I_ALERT (1 << 3)
|
||||
#define FUSB_INTERRUPT_I_WAKE (1 << 2)
|
||||
#define FUSB_INTERRUPT_I_COLLISION (1 << 1)
|
||||
#define FUSB_INTERRUPT_I_BC_LVL 1
|
||||
|
||||
/* FIFOs register */
|
||||
#define FUSB_FIFOS 0x43
|
||||
|
||||
#define FUSB_FIFO_TX_TXON 0xA1
|
||||
#define FUSB_FIFO_TX_SOP1 0x12
|
||||
#define FUSB_FIFO_TX_SOP2 0x13
|
||||
#define FUSB_FIFO_TX_SOP3 0x1B
|
||||
#define FUSB_FIFO_TX_RESET1 0x15
|
||||
#define FUSB_FIFO_TX_RESET2 0x16
|
||||
#define FUSB_FIFO_TX_PACKSYM 0x80
|
||||
#define FUSB_FIFO_TX_JAM_CRC 0xFF
|
||||
#define FUSB_FIFO_TX_EOP 0x14
|
||||
#define FUSB_FIFO_TX_TXOFF 0xFE
|
||||
|
||||
#define FUSB_FIFO_RX_TOKEN_BITS 0xE0
|
||||
#define FUSB_FIFO_RX_SOP 0xE0
|
||||
#define FUSB_FIFO_RX_SOP1 0xC0
|
||||
#define FUSB_FIFO_RX_SOP2 0xA0
|
||||
#define FUSB_FIFO_RX_SOP1DB 0x80
|
||||
#define FUSB_FIFO_RX_SOP2DB 0x60
|
||||
|
||||
/*
|
||||
* FUSB status union
|
||||
*
|
||||
* Provides a nicer structure than just an array of uint8_t for working with
|
||||
* the FUSB302B status and interrupt flags.
|
||||
*/
|
||||
union fusb_status {
|
||||
uint8_t bytes[7];
|
||||
struct {
|
||||
uint8_t status0a;
|
||||
uint8_t status1a;
|
||||
uint8_t interrupta;
|
||||
uint8_t interruptb;
|
||||
uint8_t status0;
|
||||
uint8_t status1;
|
||||
uint8_t interrupt;
|
||||
};
|
||||
};
|
||||
|
||||
/* FUSB functions */
|
||||
|
||||
/*
|
||||
* Send a USB Power Delivery message to the FUSB302B
|
||||
*/
|
||||
void fusb_send_message(const union pd_msg *msg);
|
||||
|
||||
/*
|
||||
* Read a USB Power Delivery message from the FUSB302B
|
||||
*/
|
||||
uint8_t fusb_read_message(union pd_msg *msg);
|
||||
|
||||
/*
|
||||
* Tell the FUSB302B to send a hard reset signal
|
||||
*/
|
||||
void fusb_send_hardrst();
|
||||
|
||||
/*
|
||||
* Read the FUSB302B status and interrupt flags into *status
|
||||
*/
|
||||
void fusb_get_status(union fusb_status *status);
|
||||
|
||||
/*
|
||||
* Read the FUSB302B BC_LVL as an enum fusb_typec_current
|
||||
*/
|
||||
enum fusb_typec_current fusb_get_typec_current();
|
||||
|
||||
/*
|
||||
* Initialization routine for the FUSB302B
|
||||
*/
|
||||
bool fusb_setup();
|
||||
|
||||
/*
|
||||
* Reset the FUSB302B
|
||||
*/
|
||||
void fusb_reset();
|
||||
|
||||
bool fusb_read_id();
|
||||
|
||||
#endif /* PDB_FUSB302B_H */
|
||||
28
source/Core/Drivers/FUSB302/fusbpd.cpp
Normal file
28
source/Core/Drivers/FUSB302/fusbpd.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* fusbpd.cpp
|
||||
*
|
||||
* Created on: 13 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
#include "Model_Config.h"
|
||||
#ifdef POW_PD
|
||||
#include <fusbpd.h>
|
||||
#include <pd.h>
|
||||
#include "BSP.h"
|
||||
#include "I2CBB.hpp"
|
||||
#include "fusb302b.h"
|
||||
#include "policy_engine.h"
|
||||
#include "protocol_rx.h"
|
||||
#include "protocol_tx.h"
|
||||
#include "int_n.h"
|
||||
|
||||
void fusb302_start_processing() {
|
||||
/* Initialize the FUSB302B */
|
||||
if (fusb_setup()) {
|
||||
PolicyEngine::init();
|
||||
ProtocolTransmit::init();
|
||||
ProtocolReceive::init();
|
||||
InterruptHandler::init();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
18
source/Core/Drivers/FUSB302/fusbpd.h
Normal file
18
source/Core/Drivers/FUSB302/fusbpd.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* fusbpd.h
|
||||
*
|
||||
* Created on: 13 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
|
||||
#ifndef DRIVERS_FUSB302_FUSBPD_H_
|
||||
#define DRIVERS_FUSB302_FUSBPD_H_
|
||||
//Wrapper for all of the FUSB302 PD work
|
||||
extern struct pdb_config pdb_config_data;
|
||||
#include <stdint.h>
|
||||
|
||||
//returns 1 if the FUSB302 is on the I2C bus
|
||||
uint8_t fusb302_detect();
|
||||
|
||||
void fusb302_start_processing();
|
||||
#endif /* DRIVERS_FUSB302_FUSBPD_H_ */
|
||||
80
source/Core/Drivers/FUSB302/int_n.cpp
Normal file
80
source/Core/Drivers/FUSB302/int_n.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 "int_n.h"
|
||||
#include "fusbpd.h"
|
||||
#include <pd.h>
|
||||
#include "fusb302b.h"
|
||||
#include "protocol_rx.h"
|
||||
#include "protocol_tx.h"
|
||||
#include "policy_engine.h"
|
||||
#include "protocol_rx.h"
|
||||
#include "protocol_tx.h"
|
||||
#include "task.h"
|
||||
#include "BSP.h"
|
||||
|
||||
osThreadId InterruptHandler::TaskHandle = NULL;
|
||||
uint32_t InterruptHandler::TaskBuffer[InterruptHandler::TaskStackSize];
|
||||
osStaticThreadDef_t InterruptHandler::TaskControlBlock;
|
||||
|
||||
void InterruptHandler::init() {
|
||||
osThreadStaticDef(intTask, Thread, PDB_PRIO_PRL_INT_N, 0, TaskStackSize, TaskBuffer, &TaskControlBlock);
|
||||
TaskHandle = osThreadCreate(osThread(intTask), NULL);
|
||||
}
|
||||
|
||||
void InterruptHandler::Thread(const void *arg) {
|
||||
(void) arg;
|
||||
union fusb_status status;
|
||||
while (true) {
|
||||
/* If the INT_N line is low */
|
||||
if (xTaskNotifyWait(0x00, 0x0F, NULL, PolicyEngine::setupCompleteOrTimedOut() ? 1000 : 10) == pdPASS) {
|
||||
//delay slightly so we catch the crc with better timing
|
||||
osDelay(1);
|
||||
}
|
||||
/* Read the FUSB302B status and interrupt registers */
|
||||
fusb_get_status(&status);
|
||||
/* If the I_TXSENT or I_RETRYFAIL flag is set, tell the Protocol TX
|
||||
* thread */
|
||||
if (status.interrupta & FUSB_INTERRUPTA_I_TXSENT) {
|
||||
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_I_TXSENT);
|
||||
}
|
||||
if (status.interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) {
|
||||
ProtocolTransmit::notify(ProtocolTransmit::Notifications::PDB_EVT_PRLTX_I_RETRYFAIL);
|
||||
}
|
||||
|
||||
/* If the I_GCRCSENT flag is set, tell the Protocol RX thread */
|
||||
//This means a message was recieved with a good CRC
|
||||
if (status.interruptb & FUSB_INTERRUPTB_I_GCRCSENT) {
|
||||
ProtocolReceive::notify(PDB_EVT_PRLRX_I_GCRCSENT);
|
||||
}
|
||||
|
||||
/* If the I_OCP_TEMP and OVRTEMP flags are set, tell the Policy
|
||||
* Engine thread */
|
||||
if ((status.interrupta & FUSB_INTERRUPTA_I_OCP_TEMP) && (status.status1 & FUSB_STATUS1_OVRTEMP)) {
|
||||
PolicyEngine::notify(PDB_EVT_PE_I_OVRTEMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
void InterruptHandler::irqCallback() {
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
||||
if (TaskHandle != NULL) {
|
||||
BaseType_t taskWoke = pdFALSE;
|
||||
xTaskNotifyFromISR(TaskHandle, 0x01, eNotifyAction::eSetBits, &taskWoke);
|
||||
portYIELD_FROM_ISR(taskWoke);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
source/Core/Drivers/FUSB302/int_n.h
Normal file
57
source/Core/Drivers/FUSB302/int_n.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_INT_N_OLD_H
|
||||
#define PDB_INT_N_OLD_H
|
||||
|
||||
#include <pd.h>
|
||||
|
||||
class InterruptHandler {
|
||||
public:
|
||||
//Creates the thread to handle the Interrupt pin
|
||||
static void init();
|
||||
|
||||
|
||||
static void irqCallback();
|
||||
private:
|
||||
static void Thread(const void *arg);
|
||||
static osThreadId TaskHandle;
|
||||
static const size_t TaskStackSize = 1536 / 3;
|
||||
static uint32_t TaskBuffer[TaskStackSize];
|
||||
static osStaticThreadDef_t TaskControlBlock;
|
||||
/*
|
||||
* Hard Reset machine states
|
||||
*/
|
||||
enum hardrst_state {
|
||||
PRLHRResetLayer,
|
||||
PRLHRIndicateHardReset,
|
||||
PRLHRRequestHardReset,
|
||||
PRLHRWaitPHY,
|
||||
PRLHRHardResetRequested,
|
||||
PRLHRWaitPE,
|
||||
PRLHRComplete
|
||||
};
|
||||
static enum hardrst_state hardrst_reset_layer();
|
||||
static enum hardrst_state hardrst_indicate_hard_reset();
|
||||
static enum hardrst_state hardrst_request_hard_reset();
|
||||
static enum hardrst_state hardrst_wait_phy();
|
||||
static enum hardrst_state hardrst_hard_reset_requested();
|
||||
static enum hardrst_state hardrst_wait_pe();
|
||||
static enum hardrst_state hardrst_complete();
|
||||
};
|
||||
|
||||
#endif /* PDB_INT_N_OLD_H */
|
||||
400
source/Core/Drivers/FUSB302/pd.h
Normal file
400
source/Core/Drivers/FUSB302/pd.h
Normal file
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_PD_H
|
||||
#define PDB_PD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "pdb_msg.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "pdb_conf.h"
|
||||
/*
|
||||
* Macros for working with USB Power Delivery messages.
|
||||
*
|
||||
* This file is mostly written from the PD Rev. 2.0 spec, but the header is
|
||||
* written from the Rev. 3.0 spec.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PD Header
|
||||
*/
|
||||
#define PD_HDR_MSGTYPE_SHIFT 0
|
||||
#define PD_HDR_MSGTYPE (0x1F << PD_HDR_MSGTYPE_SHIFT)
|
||||
#define PD_HDR_DATAROLE_SHIFT 5
|
||||
#define PD_HDR_DATAROLE (0x1 << PD_HDR_DATAROLE_SHIFT)
|
||||
#define PD_HDR_SPECREV_SHIFT 6
|
||||
#define PD_HDR_SPECREV (0x3 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_HDR_POWERROLE_SHIFT 8
|
||||
#define PD_HDR_POWERROLE (1 << PD_HDR_POWERROLE_SHIFT)
|
||||
#define PD_HDR_MESSAGEID_SHIFT 9
|
||||
#define PD_HDR_MESSAGEID (0x7 << PD_HDR_MESSAGEID_SHIFT)
|
||||
#define PD_HDR_NUMOBJ_SHIFT 12
|
||||
#define PD_HDR_NUMOBJ (0x7 << PD_HDR_NUMOBJ_SHIFT)
|
||||
#define PD_HDR_EXT (1 << 15)
|
||||
|
||||
/* Message types */
|
||||
#define PD_MSGTYPE_GET(msg) (((msg)->hdr & PD_HDR_MSGTYPE) >> PD_HDR_MSGTYPE_SHIFT)
|
||||
/* Control Message */
|
||||
#define PD_MSGTYPE_GOODCRC 0x01
|
||||
#define PD_MSGTYPE_GOTOMIN 0x02
|
||||
#define PD_MSGTYPE_ACCEPT 0x03
|
||||
#define PD_MSGTYPE_REJECT 0x04
|
||||
#define PD_MSGTYPE_PING 0x05
|
||||
#define PD_MSGTYPE_PS_RDY 0x06
|
||||
#define PD_MSGTYPE_GET_SOURCE_CAP 0x07
|
||||
#define PD_MSGTYPE_GET_SINK_CAP 0x08
|
||||
#define PD_MSGTYPE_DR_SWAP 0x09
|
||||
#define PD_MSGTYPE_PR_SWAP 0x0A
|
||||
#define PD_MSGTYPE_VCONN_SWAP 0x0B
|
||||
#define PD_MSGTYPE_WAIT 0x0C
|
||||
#define PD_MSGTYPE_SOFT_RESET 0x0D
|
||||
#define PD_MSGTYPE_NOT_SUPPORTED 0x10
|
||||
#define PD_MSGTYPE_GET_SOURCE_CAP_EXTENDED 0x11
|
||||
#define PD_MSGTYPE_GET_STATUS 0x12
|
||||
#define PD_MSGTYPE_FR_SWAP 0x13
|
||||
#define PD_MSGTYPE_GET_PPS_STATUS 0x14
|
||||
#define PD_MSGTYPE_GET_COUNTRY_CODES 0x15
|
||||
/* Data Message */
|
||||
#define PD_MSGTYPE_SOURCE_CAPABILITIES 0x01
|
||||
#define PD_MSGTYPE_REQUEST 0x02
|
||||
#define PD_MSGTYPE_BIST 0x03
|
||||
#define PD_MSGTYPE_SINK_CAPABILITIES 0x04
|
||||
#define PD_MSGTYPE_BATTERY_STATUS 0x05
|
||||
#define PD_MSGTYPE_ALERT 0x06
|
||||
#define PD_MSGTYPE_GET_COUNTRY_INFO 0x07
|
||||
#define PD_MSGTYPE_VENDOR_DEFINED 0x0F
|
||||
/* Extended Message */
|
||||
#define PD_MSGTYPE_SOURCE_CAPABILITIES_EXTENDED 0x01
|
||||
#define PD_MSGTYPE_STATUS 0x02
|
||||
#define PD_MSGTYPE_GET_BATTERY_CAP 0x03
|
||||
#define PD_MSGTYPE_GET_BATTERY_STATUS 0x04
|
||||
#define PD_MSGTYPE_BATTERY_CAPABILITIES 0x05
|
||||
#define PD_MSGTYPE_GET_MANUFACTURER_INFO 0x06
|
||||
#define PD_MSGTYPE_MANUFACTURER_INFO 0x07
|
||||
#define PD_MSGTYPE_SECURITY_REQUEST 0x08
|
||||
#define PD_MSGTYPE_SECURITY_RESPONSE 0x09
|
||||
#define PD_MSGTYPE_FIRMWARE_UPDATE_REQUEST 0x0A
|
||||
#define PD_MSGTYPE_FIRMWARE_UPDATE_RESPONSE 0x0B
|
||||
#define PD_MSGTYPE_PPS_STATUS 0x0C
|
||||
#define PD_MSGTYPE_COUNTRY_INFO 0x0D
|
||||
#define PD_MSGTYPE_COUNTRY_CODES 0x0E
|
||||
|
||||
/* Data roles */
|
||||
#define PD_DATAROLE_UFP (0x0 << PD_HDR_DATAROLE_SHIFT)
|
||||
#define PD_DATAROLE_DFP (0x1 << PD_HDR_DATAROLE_SHIFT)
|
||||
|
||||
/* Specification revisions */
|
||||
#define PD_SPECREV_1_0 (0x0 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_SPECREV_2_0 (0x1 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_SPECREV_3_0 (0x2 << PD_HDR_SPECREV_SHIFT)
|
||||
|
||||
/* Port power roles */
|
||||
#define PD_POWERROLE_SINK (0x0 << PD_HDR_POWERROLE_SHIFT)
|
||||
#define PD_POWERROLE_SOURCE (0x1 << PD_HDR_POWERROLE_SHIFT)
|
||||
|
||||
/* Message ID */
|
||||
#define PD_MESSAGEID_GET(msg) (((msg)->hdr & PD_HDR_MESSAGEID) >> PD_HDR_MESSAGEID_SHIFT)
|
||||
|
||||
/* Number of data objects */
|
||||
#define PD_NUMOBJ(n) (((n) << PD_HDR_NUMOBJ_SHIFT) & PD_HDR_NUMOBJ)
|
||||
#define PD_NUMOBJ_GET(msg) (((msg)->hdr & PD_HDR_NUMOBJ) >> PD_HDR_NUMOBJ_SHIFT)
|
||||
|
||||
/*
|
||||
* PD Extended Message Header
|
||||
*/
|
||||
#define PD_EXTHDR_DATA_SIZE_SHIFT 0
|
||||
#define PD_EXTHDR_DATA_SIZE (0x1FF << PD_EXTHDR_DATA_SIZE_SHIFT)
|
||||
#define PD_EXTHDR_REQUEST_CHUNK_SHIFT 10
|
||||
#define PD_EXTHDR_REQUEST_CHUNK (1 << PD_EXTHDR_REQUEST_CHUNK_SHIFT)
|
||||
#define PD_EXTHDR_CHUNK_NUMBER_SHIFT 11
|
||||
#define PD_EXTHDR_CHUNK_NUMBER (0xF << PD_EXTHDR_CHUNK_NUMBER_SHIFT)
|
||||
#define PD_EXTHDR_CHUNKED_SHIFT 15
|
||||
#define PD_EXTHDR_CHUNKED (1 << PD_EXTHDR_CHUNKED_SHIFT)
|
||||
|
||||
/* Data size */
|
||||
#define PD_DATA_SIZE(n) (((n) << PD_EXTHDR_DATA_SIZE_SHIFT) & PD_EXTHDR_DATA_SIZE)
|
||||
#define PD_DATA_SIZE_GET(msg) (((msg)->exthdr & PD_EXTHDR_DATA_SIZE) >> PD_EXTHDR_DATA_SIZE_SHIFT)
|
||||
|
||||
/* Chunk number */
|
||||
#define PD_CHUNK_NUMBER(n) (((n) << PD_EXTHDR_CHUNK_NUMBER_SHIFT) & PD_EXTHDR_CHUNK_NUMBER)
|
||||
#define PD_CHUNK_NUMBER_GET(msg) (((msg)->exthdr & PD_EXTHDR_CHUNK_NUMBER) >> PD_EXTHDR_CHUNK_NUMBER_SHIFT)
|
||||
|
||||
/*
|
||||
* PD Power Data Object
|
||||
*/
|
||||
#define PD_PDO_TYPE_SHIFT 30
|
||||
#define PD_PDO_TYPE (0x3 << PD_PDO_TYPE_SHIFT)
|
||||
|
||||
/* PDO types */
|
||||
#define PD_PDO_TYPE_FIXED ((unsigned) (0x0 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_BATTERY ((unsigned) (0x1 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_VARIABLE ((unsigned) (0x2 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_AUGMENTED ((unsigned) (0x3 << PD_PDO_TYPE_SHIFT))
|
||||
|
||||
#define PD_APDO_TYPE_SHIFT 28
|
||||
#define PD_APDO_TYPE (0x3 << PD_APDO_TYPE_SHIFT)
|
||||
|
||||
/* APDO types */
|
||||
#define PD_APDO_TYPE_PPS (0x0 << PD_APDO_TYPE_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO */
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT 29
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT 28
|
||||
#define PD_PDO_SRC_FIXED_USB_SUSPEND (1 << PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT 27
|
||||
#define PD_PDO_SRC_FIXED_UNCONSTRAINED (1 << PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_USB_COMMS_SHIFT 26
|
||||
#define PD_PDO_SRC_FIXED_USB_COMMS (1 << PD_PDO_SRC_FIXED_USB_COMMS_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT 25
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT 24
|
||||
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG (1 << PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT 20
|
||||
#define PD_PDO_SRC_FIXED_PEAK_CURRENT (0x3 << PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE_SHIFT 10
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE (0x3FF << PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_CURRENT_SHIFT 0
|
||||
#define PD_PDO_SRC_FIXED_CURRENT (0x3FF << PD_PDO_SRC_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO current */
|
||||
#define PD_PDO_SRC_FIXED_CURRENT_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO voltage */
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
|
||||
|
||||
/* PD Programmable Power Supply APDO */
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_SHIFT 17
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE (0xFF << PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_SHIFT 8
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE (0xFF << PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_CURRENT_SHIFT 0
|
||||
#define PD_APDO_PPS_CURRENT (0x7F << PD_APDO_PPS_CURRENT_SHIFT)
|
||||
|
||||
/* PD Programmable Power Supply APDO voltages */
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MAX_VOLTAGE) >> PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MIN_VOLTAGE) >> PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
|
||||
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MAX_VOLTAGE_SHIFT) & PD_APDO_PPS_MAX_VOLTAGE)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MIN_VOLTAGE_SHIFT) & PD_APDO_PPS_MIN_VOLTAGE)
|
||||
|
||||
/* PD Programmable Power Supply APDO current */
|
||||
#define PD_APDO_PPS_CURRENT_GET(pdo) ((uint8_t) (((pdo) & PD_APDO_PPS_CURRENT) >> PD_APDO_PPS_CURRENT_SHIFT))
|
||||
|
||||
#define PD_APDO_PPS_CURRENT_SET(i) (((i) << PD_APDO_PPS_CURRENT_SHIFT) & PD_APDO_PPS_CURRENT)
|
||||
|
||||
|
||||
/* PD Sink Fixed PDO */
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT 29
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT 28
|
||||
#define PD_PDO_SNK_FIXED_HIGHER_CAP (1 << PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT 27
|
||||
#define PD_PDO_SNK_FIXED_UNCONSTRAINED (1 << PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_USB_COMMS_SHIFT 26
|
||||
#define PD_PDO_SNK_FIXED_USB_COMMS (1 << PD_PDO_SNK_FIXED_USB_COMMS_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT 25
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE_SHIFT 10
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE (0x3FF << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_CURRENT_SHIFT 0
|
||||
#define PD_PDO_SNK_FIXED_CURRENT (0x3FF << PD_PDO_SNK_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Sink Fixed PDO current */
|
||||
#define PD_PDO_SNK_FIXED_CURRENT_SET(i) (((i) << PD_PDO_SNK_FIXED_CURRENT_SHIFT) & PD_PDO_SNK_FIXED_CURRENT)
|
||||
|
||||
/* PD Sink Fixed PDO voltage */
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE_SET(v) (((v) << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT) & PD_PDO_SNK_FIXED_VOLTAGE)
|
||||
|
||||
|
||||
/*
|
||||
* PD Request Data Object
|
||||
*/
|
||||
#define PD_RDO_OBJPOS_SHIFT 28
|
||||
#define PD_RDO_OBJPOS (0x7 << PD_RDO_OBJPOS_SHIFT)
|
||||
#define PD_RDO_GIVEBACK_SHIFT 27
|
||||
#define PD_RDO_GIVEBACK (1 << PD_RDO_GIVEBACK_SHIFT)
|
||||
#define PD_RDO_CAP_MISMATCH_SHIFT 26
|
||||
#define PD_RDO_CAP_MISMATCH (1 << PD_RDO_CAP_MISMATCH_SHIFT)
|
||||
#define PD_RDO_USB_COMMS_SHIFT 25
|
||||
#define PD_RDO_USB_COMMS (1 << PD_RDO_USB_COMMS_SHIFT)
|
||||
#define PD_RDO_NO_USB_SUSPEND_SHIFT 24
|
||||
#define PD_RDO_NO_USB_SUSPEND (1 << PD_RDO_NO_USB_SUSPEND_SHIFT)
|
||||
#define PD_RDO_UNCHUNKED_EXT_MSG_SHIFT 23
|
||||
#define PD_RDO_UNCHUNKED_EXT_MSG (1 << PD_RDO_UNCHUNKED_EXT_MSG_SHIFT)
|
||||
|
||||
#define PD_RDO_OBJPOS_SET(i) (((i) << PD_RDO_OBJPOS_SHIFT) & PD_RDO_OBJPOS)
|
||||
#define PD_RDO_OBJPOS_GET(msg) (((msg)->obj[0] & PD_RDO_OBJPOS) >> PD_RDO_OBJPOS_SHIFT)
|
||||
|
||||
/* Fixed and Variable RDO, no GiveBack support */
|
||||
#define PD_RDO_FV_CURRENT_SHIFT 10
|
||||
#define PD_RDO_FV_CURRENT (0x3FF << PD_RDO_FV_CURRENT_SHIFT)
|
||||
#define PD_RDO_FV_MAX_CURRENT_SHIFT 0
|
||||
#define PD_RDO_FV_MAX_CURRENT (0x3FF << PD_RDO_FV_MAX_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_FV_CURRENT_SET(i) (((i) << PD_RDO_FV_CURRENT_SHIFT) & PD_RDO_FV_CURRENT)
|
||||
#define PD_RDO_FV_MAX_CURRENT_SET(i) (((i) << PD_RDO_FV_MAX_CURRENT_SHIFT) & PD_RDO_FV_MAX_CURRENT)
|
||||
|
||||
/* Fixed and Variable RDO with GiveBack support */
|
||||
#define PD_RDO_FV_MIN_CURRENT_SHIFT 0
|
||||
#define PD_RDO_FV_MIN_CURRENT (0x3FF << PD_RDO_FV_MIN_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_FV_MIN_CURRENT_SET(i) (((i) << PD_RDO_FV_MIN_CURRENT_SHIFT) & PD_RDO_FV_MIN_CURRENT)
|
||||
|
||||
/* TODO: Battery RDOs */
|
||||
|
||||
/* Programmable RDO */
|
||||
#define PD_RDO_PROG_VOLTAGE_SHIFT 9
|
||||
#define PD_RDO_PROG_VOLTAGE (0x7FF << PD_RDO_PROG_VOLTAGE_SHIFT)
|
||||
#define PD_RDO_PROG_CURRENT_SHIFT 0
|
||||
#define PD_RDO_PROG_CURRENT (0x7F << PD_RDO_PROG_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_PROG_VOLTAGE_SET(i) (((i) << PD_RDO_PROG_VOLTAGE_SHIFT) & PD_RDO_PROG_VOLTAGE)
|
||||
#define PD_RDO_PROG_CURRENT_SET(i) (((i) << PD_RDO_PROG_CURRENT_SHIFT) & PD_RDO_PROG_CURRENT)
|
||||
|
||||
/*
|
||||
* Time values
|
||||
*
|
||||
* Where a range is specified, the middle of the range (rounded down to the
|
||||
* nearest millisecond) is used.
|
||||
*/
|
||||
#define PD_T_CHUNKING_NOT_SUPPORTED (450)
|
||||
#define PD_T_HARD_RESET_COMPLETE (1000)
|
||||
#define PD_T_PS_TRANSITION (5000)
|
||||
#define PD_T_SENDER_RESPONSE (2700)
|
||||
#define PD_T_SINK_REQUEST (1000)
|
||||
#define PD_T_TYPEC_SINK_WAIT_CAP (1000)
|
||||
#define PD_T_PD_DEBOUNCE (2000)
|
||||
|
||||
/*
|
||||
* Counter maximums
|
||||
*/
|
||||
#define PD_N_HARD_RESET_COUNT 2
|
||||
|
||||
/*
|
||||
* Value parameters
|
||||
*/
|
||||
#define PD_MAX_EXT_MSG_LEN 260
|
||||
#define PD_MAX_EXT_MSG_CHUNK_LEN 26
|
||||
#define PD_MAX_EXT_MSG_LEGACY_LEN 26
|
||||
|
||||
/*
|
||||
* Unit conversions
|
||||
*
|
||||
* V: volt
|
||||
* CV: centivolt
|
||||
* MV: millivolt
|
||||
* PRV: Programmable RDO voltage unit (20 mV)
|
||||
* PDV: Power Delivery voltage unit (50 mV)
|
||||
* PAV: PPS APDO voltage unit (100 mV)
|
||||
*
|
||||
* A: ampere
|
||||
* CA: centiampere
|
||||
* MA: milliampere
|
||||
* PDI: Power Delivery current unit (10 mA)
|
||||
* PAI: PPS APDO current unit (50 mA)
|
||||
*
|
||||
* W: watt
|
||||
* CW: centiwatt
|
||||
* MW: milliwatt
|
||||
*
|
||||
* O: ohm
|
||||
* CO: centiohm
|
||||
* MO: milliohm
|
||||
*/
|
||||
#define PD_MV2PRV(mv) ((mv) / 20)
|
||||
#define PD_MV2PDV(mv) ((mv) / 50)
|
||||
#define PD_MV2PAV(mv) ((mv) / 100)
|
||||
#define PD_PRV2MV(prv) ((prv) * 20)
|
||||
#define PD_PDV2MV(pdv) ((pdv) * 50)
|
||||
#define PD_PAV2MV(pav) ((pav) * 100)
|
||||
|
||||
#define PD_MA2CA(ma) (((ma) + 10 - 1) / 10)
|
||||
#define PD_MA2PDI(ma) (((ma) + 10 - 1) / 10)
|
||||
#define PD_MA2PAI(ma) (((ma) + 50 - 1) / 50)
|
||||
#define PD_CA2PAI(ca) (((ca) + 5 - 1) / 5)
|
||||
#define PD_PDI2MA(pdi) ((pdi) * 10)
|
||||
#define PD_PAI2MA(pai) ((pai) * 50)
|
||||
#define PD_PAI2CA(pai) ((pai) * 5)
|
||||
|
||||
#define PD_MW2CW(mw) ((mw) / 10)
|
||||
|
||||
#define PD_MO2CO(mo) ((mo) / 10)
|
||||
|
||||
/* Get portions of a voltage in more normal units */
|
||||
#define PD_MV_V(mv) ((mv) / 1000)
|
||||
#define PD_MV_MV(mv) ((mv) % 1000)
|
||||
|
||||
#define PD_PDV_V(pdv) ((pdv) / 20)
|
||||
#define PD_PDV_CV(pdv) (5 * ((pdv) % 20))
|
||||
|
||||
#define PD_PAV_V(pav) ((pav) / 10)
|
||||
#define PD_PAV_CV(pav) (10 * ((pav) % 10))
|
||||
|
||||
/* Get portions of a PD current in more normal units */
|
||||
#define PD_PDI_A(pdi) ((pdi) / 100)
|
||||
#define PD_PDI_CA(pdi) ((pdi) % 100)
|
||||
|
||||
#define PD_PAI_A(pai) ((pai) / 20)
|
||||
#define PD_PAI_CA(pai) (5 * ((pai) % 20))
|
||||
|
||||
/* Get portions of a power in more normal units */
|
||||
#define PD_CW_W(cw) ((cw) / 100)
|
||||
#define PD_CW_CW(cw) ((cw) % 100)
|
||||
|
||||
/* Get portions of a resistance in more normal units */
|
||||
#define PD_CO_O(co) ((co) / 100)
|
||||
#define PD_CO_CO(co) ((co) % 100)
|
||||
|
||||
/*
|
||||
* Unit constants
|
||||
*/
|
||||
#define PD_MV_MIN 0
|
||||
#define PD_MV_MAX 21000
|
||||
#define PD_PDV_MIN PD_MV2PDV(PD_MV_MIN)
|
||||
#define PD_PDV_MAX PD_MV2PDV(PD_MV_MAX)
|
||||
|
||||
#define PD_MA_MIN 0
|
||||
#define PD_MA_MAX 5000
|
||||
#define PD_CA_MIN PD_MA2CA(PD_MA_MIN)
|
||||
#define PD_CA_MAX PD_MA2CA(PD_MA_MAX)
|
||||
#define PD_PDI_MIN PD_MA2PDI(PD_MA_MIN)
|
||||
#define PD_PDI_MAX PD_MA2PDI(PD_MA_MAX)
|
||||
|
||||
#define PD_MW_MIN 0
|
||||
#define PD_MW_MAX 100000
|
||||
|
||||
#define PD_MO_MIN 500
|
||||
#define PD_MO_MAX 655350
|
||||
|
||||
|
||||
/*
|
||||
* FUSB Type-C Current level enum
|
||||
*/
|
||||
enum fusb_typec_current {
|
||||
fusb_tcc_none = 0,
|
||||
fusb_tcc_default = 1,
|
||||
fusb_tcc_1_5 = 2,
|
||||
fusb_sink_tx_ng = 2,
|
||||
fusb_tcc_3_0 = 3,
|
||||
fusb_sink_tx_ok = 3
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* PDB_PD_H */
|
||||
32
source/Core/Drivers/FUSB302/pdb_conf.h
Normal file
32
source/Core/Drivers/FUSB302/pdb_conf.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_CONF_H
|
||||
#define PDB_CONF_H
|
||||
|
||||
|
||||
/* Number of messages in the message pool */
|
||||
#define PDB_MSG_POOL_SIZE 4
|
||||
|
||||
#define EVENT_MASK(x) (1<<x)
|
||||
#define eventmask_t uint32_t
|
||||
/* PD Buddy thread priorities */
|
||||
#define PDB_PRIO_PE (osPriorityNormal)
|
||||
#define PDB_PRIO_PRL (osPriorityBelowNormal)
|
||||
#define PDB_PRIO_PRL_INT_N (osPriorityLow)
|
||||
|
||||
#endif /* PDB_CONF_H */
|
||||
55
source/Core/Drivers/FUSB302/pdb_msg.h
Normal file
55
source/Core/Drivers/FUSB302/pdb_msg.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_MSG_H
|
||||
#define PDB_MSG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* PD message union
|
||||
*
|
||||
* This can be safely read from or written to in any form without any
|
||||
* transformations because everything in the system is little-endian.
|
||||
*
|
||||
* Two bytes of padding are required at the start to prevent problems due to
|
||||
* alignment. Specifically, without the padding, &obj[0] != &bytes[2], making
|
||||
* the statement in the previous paragraph invalid.
|
||||
*/
|
||||
union pd_msg {
|
||||
struct {
|
||||
uint8_t _pad1[2];
|
||||
uint8_t bytes[30];
|
||||
} __attribute__((packed));
|
||||
struct {
|
||||
uint8_t _pad2[2];
|
||||
uint16_t hdr;
|
||||
union {
|
||||
uint32_t obj[7];
|
||||
struct {
|
||||
uint16_t exthdr;
|
||||
uint8_t data[26];
|
||||
};
|
||||
};
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* PDB_MSG_H */
|
||||
692
source/Core/Drivers/FUSB302/policy_engine.cpp
Normal file
692
source/Core/Drivers/FUSB302/policy_engine.cpp
Normal file
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 "policy_engine.h"
|
||||
#include <stdbool.h>
|
||||
#include "int_n.h"
|
||||
#include <pd.h>
|
||||
#include "protocol_tx.h"
|
||||
#include "fusb302b.h"
|
||||
bool PolicyEngine::pdNegotiationComplete;
|
||||
int PolicyEngine::current_voltage_mv;
|
||||
int PolicyEngine::_requested_voltage;
|
||||
bool PolicyEngine::_unconstrained_power;
|
||||
union pd_msg PolicyEngine::currentMessage;
|
||||
uint16_t PolicyEngine::hdr_template;
|
||||
bool PolicyEngine::_explicit_contract;
|
||||
int8_t PolicyEngine::_hard_reset_counter;
|
||||
int8_t PolicyEngine::_old_tcc_match;
|
||||
uint8_t PolicyEngine::_pps_index;
|
||||
uint8_t PolicyEngine::_last_pps;
|
||||
osThreadId PolicyEngine::TaskHandle = NULL;
|
||||
uint32_t PolicyEngine::TaskBuffer[PolicyEngine::TaskStackSize];
|
||||
osStaticThreadDef_t PolicyEngine::TaskControlBlock;
|
||||
union pd_msg PolicyEngine::tempMessage;
|
||||
union pd_msg PolicyEngine::_last_dpm_request;
|
||||
PolicyEngine::policy_engine_state PolicyEngine::state = PESinkStartup;
|
||||
StaticQueue_t PolicyEngine::xStaticQueue;
|
||||
uint8_t PolicyEngine::ucQueueStorageArea[PDB_MSG_POOL_SIZE
|
||||
* sizeof(union pd_msg)];
|
||||
QueueHandle_t PolicyEngine::messagesWaiting = NULL;
|
||||
EventGroupHandle_t PolicyEngine::xEventGroupHandle = NULL;
|
||||
StaticEventGroup_t PolicyEngine::xCreatedEventGroup;
|
||||
void PolicyEngine::init() {
|
||||
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE,
|
||||
sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
|
||||
//Create static thread at PDB_PRIO_PE priority
|
||||
osThreadStaticDef(PolEng, pe_task, PDB_PRIO_PE, 0, TaskStackSize,
|
||||
TaskBuffer, &TaskControlBlock);
|
||||
TaskHandle = osThreadCreate(osThread(PolEng), NULL);
|
||||
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
|
||||
}
|
||||
|
||||
void PolicyEngine::notify(uint32_t notification) {
|
||||
if (xEventGroupHandle != NULL) {
|
||||
xEventGroupSetBits(xEventGroupHandle, notification);
|
||||
}
|
||||
}
|
||||
|
||||
void PolicyEngine::pe_task(const void *arg) {
|
||||
(void) arg;
|
||||
//Internal thread loop
|
||||
hdr_template = PD_DATAROLE_UFP | PD_POWERROLE_SINK;
|
||||
/* Initialize the old_tcc_match */
|
||||
_old_tcc_match = -1;
|
||||
/* Initialize the pps_index */
|
||||
_pps_index = 8;
|
||||
/* Initialize the last_pps */
|
||||
_last_pps = 8;
|
||||
|
||||
for (;;) {
|
||||
//Loop based on state
|
||||
switch (state) {
|
||||
|
||||
case PESinkStartup:
|
||||
state = pe_sink_startup();
|
||||
break;
|
||||
case PESinkDiscovery:
|
||||
state = pe_sink_discovery();
|
||||
break;
|
||||
case PESinkWaitCap:
|
||||
state = pe_sink_wait_cap();
|
||||
break;
|
||||
case PESinkEvalCap:
|
||||
state = pe_sink_eval_cap();
|
||||
break;
|
||||
case PESinkSelectCap:
|
||||
state = pe_sink_select_cap();
|
||||
break;
|
||||
case PESinkTransitionSink:
|
||||
state = pe_sink_transition_sink();
|
||||
break;
|
||||
case PESinkReady:
|
||||
state = pe_sink_ready();
|
||||
break;
|
||||
case PESinkGetSourceCap:
|
||||
state = pe_sink_get_source_cap();
|
||||
break;
|
||||
case PESinkGiveSinkCap:
|
||||
state = pe_sink_give_sink_cap();
|
||||
break;
|
||||
case PESinkHardReset:
|
||||
state = pe_sink_hard_reset();
|
||||
break;
|
||||
case PESinkTransitionDefault:
|
||||
state = pe_sink_transition_default();
|
||||
break;
|
||||
case PESinkSoftReset:
|
||||
state = pe_sink_soft_reset();
|
||||
break;
|
||||
case PESinkSendSoftReset:
|
||||
state = pe_sink_send_soft_reset();
|
||||
break;
|
||||
case PESinkSendNotSupported:
|
||||
state = pe_sink_send_not_supported();
|
||||
break;
|
||||
case PESinkChunkReceived:
|
||||
state = pe_sink_chunk_received();
|
||||
break;
|
||||
case PESinkSourceUnresponsive:
|
||||
state = pe_sink_source_unresponsive();
|
||||
break;
|
||||
case PESinkNotSupportedReceived:
|
||||
state = pe_sink_not_supported_received();
|
||||
break;
|
||||
default:
|
||||
state = PESinkStartup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_startup() {
|
||||
/* We don't have an explicit contract currently */
|
||||
_explicit_contract = false;
|
||||
|
||||
//If desired could send an alert that PD is starting
|
||||
|
||||
/* No need to reset the protocol layer here. There are two ways into this
|
||||
* state: startup and exiting hard reset. On startup, the protocol layer
|
||||
* is reset by the startup procedure. When exiting hard reset, the
|
||||
* protocol layer is reset by the hard reset state machine. Since it's
|
||||
* already done somewhere else, there's no need to do it again here. */
|
||||
|
||||
return PESinkDiscovery;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_discovery() {
|
||||
/* Wait for VBUS. Since it's our only power source, we already know that
|
||||
* we have it, so just move on. */
|
||||
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_wait_cap() {
|
||||
/* Fetch a message from the protocol layer */
|
||||
eventmask_t evt = 0;
|
||||
if (readMessage()) {
|
||||
evt = PDB_EVT_PE_MSG_RX_PEND;
|
||||
} else {
|
||||
evt = waitForEvent(
|
||||
PDB_EVT_PE_MSG_RX | PDB_EVT_PE_I_OVRTEMP | PDB_EVT_PE_RESET,
|
||||
//Wait for cap timeout
|
||||
PD_T_TYPEC_SINK_WAIT_CAP);
|
||||
}
|
||||
/* If we timed out waiting for Source_Capabilities, send a hard reset */
|
||||
if (evt == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
/* If we're too hot, we shouldn't negotiate power yet */
|
||||
if (evt & PDB_EVT_PE_I_OVRTEMP) {
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
/* If we got a message */
|
||||
if (evt & (PDB_EVT_PE_MSG_RX | PDB_EVT_PE_MSG_RX_PEND)) {
|
||||
/* Get the message */
|
||||
while ((evt & PDB_EVT_PE_MSG_RX_PEND) || readMessage() == true) {
|
||||
/* If we got a Source_Capabilities message, read it. */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOURCE_CAPABILITIES
|
||||
&& PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
/* First, determine what PD revision we're using */
|
||||
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_1_0) {
|
||||
/* If the other end is using at least version 3.0, we'll
|
||||
* use version 3.0. */
|
||||
if ((tempMessage.hdr & PD_HDR_SPECREV) >= PD_SPECREV_3_0) {
|
||||
hdr_template |= PD_SPECREV_3_0;
|
||||
/* Otherwise, use 2.0. Don't worry about the 1.0 case
|
||||
* because we don't have hardware for PD 1.0 signaling. */
|
||||
} else {
|
||||
hdr_template |= PD_SPECREV_2_0;
|
||||
}
|
||||
}
|
||||
return PESinkEvalCap;
|
||||
/* If the message was a Soft_Reset, do the soft reset procedure */
|
||||
}
|
||||
evt = 0;
|
||||
}
|
||||
return PESinkWaitCap; //wait for more messages?
|
||||
|
||||
}
|
||||
|
||||
/* If we failed to get a message, send a hard reset */
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_eval_cap() {
|
||||
/* If we have a Source_Capabilities message, remember the index of the
|
||||
* first PPS APDO so we can check if the request is for a PPS APDO in
|
||||
* PE_SNK_Select_Cap. */
|
||||
/* Start by assuming we won't find a PPS APDO (set the index greater
|
||||
* than the maximum possible) */
|
||||
_pps_index = 8;
|
||||
/* Search for the first PPS APDO */
|
||||
for (int8_t i = 0; i < PD_NUMOBJ_GET(&tempMessage); i++) {
|
||||
if ((tempMessage.obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED
|
||||
&& (tempMessage.obj[i] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS) {
|
||||
_pps_index = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* New capabilities also means we can't be making a request from the
|
||||
* same PPS APDO */
|
||||
_last_pps = 8;
|
||||
|
||||
/* Ask the DPM what to request */
|
||||
if (pdbs_dpm_evaluate_capability(&tempMessage, &_last_dpm_request)) {
|
||||
|
||||
return PESinkSelectCap;
|
||||
}
|
||||
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_select_cap() {
|
||||
|
||||
/* Transmit the request */
|
||||
waitForEvent(0xFFFF, 0); //clear pending
|
||||
ProtocolTransmit::pushMessage(&_last_dpm_request);
|
||||
//Send indication that there is a message pending
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
|
||||
eventmask_t evt = waitForEvent(
|
||||
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET || evt == 0) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & PDB_EVT_PE_TX_ERR) == PDB_EVT_PE_TX_ERR) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* Wait for a response */
|
||||
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET,
|
||||
PD_T_SENDER_RESPONSE);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If we didn't get a response before the timeout, send a hard reset */
|
||||
if (evt == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* Get the response message */
|
||||
if (messageWaiting()) {
|
||||
readMessage();
|
||||
/* If the source accepted our request, wait for the new power */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_ACCEPT
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkTransitionSink;
|
||||
/* If the message was a Soft_Reset, do the soft reset procedure */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
return PESinkSoftReset;
|
||||
/* If the message was Wait or Reject */
|
||||
} else if ((PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_REJECT
|
||||
|| PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_WAIT)
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
/* If we don't have an explicit contract, wait for capabilities */
|
||||
if (!_explicit_contract) {
|
||||
return PESinkWaitCap;
|
||||
/* If we do have an explicit contract, go to the ready state */
|
||||
} else {
|
||||
return PESinkReady;
|
||||
}
|
||||
} else {
|
||||
return PESinkSendSoftReset;
|
||||
}
|
||||
}
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_sink() {
|
||||
/* Wait for the PS_RDY message */
|
||||
eventmask_t evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET,
|
||||
PD_T_PS_TRANSITION);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If no message was received, send a hard reset */
|
||||
if (evt == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* If we received a message, read it */
|
||||
if (messageWaiting()) {
|
||||
readMessage();
|
||||
/* If we got a PS_RDY, handle it */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PS_RDY
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
/* We just finished negotiating an explicit contract */
|
||||
_explicit_contract = true;
|
||||
|
||||
/* Set the output appropriately */
|
||||
pdbs_dpm_transition_requested();
|
||||
|
||||
return PESinkReady;
|
||||
/* If there was a protocol error, send a hard reset */
|
||||
} else {
|
||||
/* Turn off the power output before this hard reset to make sure we
|
||||
* don't supply an incorrect voltage to the device we're powering.
|
||||
*/
|
||||
pdbs_dpm_transition_default();
|
||||
|
||||
return PESinkHardReset;
|
||||
}
|
||||
}
|
||||
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_ready() {
|
||||
eventmask_t evt;
|
||||
|
||||
/* Wait for an event */
|
||||
evt = waitForEvent(
|
||||
PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET | PDB_EVT_PE_I_OVRTEMP);
|
||||
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
|
||||
/* If we overheated, send a hard reset */
|
||||
if (evt & PDB_EVT_PE_I_OVRTEMP) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* If we received a message */
|
||||
if (evt & PDB_EVT_PE_MSG_RX) {
|
||||
if (messageWaiting()) {
|
||||
readMessage();
|
||||
/* Ignore vendor-defined messages */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_VENDOR_DEFINED
|
||||
&& PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
|
||||
return PESinkReady;
|
||||
/* Ignore Ping messages */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PING
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkReady;
|
||||
/* DR_Swap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_DR_SWAP
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
/* Get_Source_Cap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GET_SOURCE_CAP
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
/* PR_Swap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_PR_SWAP
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
/* VCONN_Swap messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_VCONN_SWAP
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
/* Request messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_REQUEST
|
||||
&& PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
/* Sink_Capabilities messages are not supported */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage)
|
||||
== PD_MSGTYPE_SINK_CAPABILITIES
|
||||
&& PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
/* Handle GotoMin messages */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GOTOMIN
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
/* GiveBack is not supported */
|
||||
return PESinkSendNotSupported;
|
||||
|
||||
/* Evaluate new Source_Capabilities */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage)
|
||||
== PD_MSGTYPE_SOURCE_CAPABILITIES
|
||||
&& PD_NUMOBJ_GET(&tempMessage) > 0) {
|
||||
/* Don't free the message: we need to keep the
|
||||
* Source_Capabilities message so we can evaluate it. */
|
||||
return PESinkEvalCap;
|
||||
/* Give sink capabilities when asked */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_GET_SINK_CAP
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkGiveSinkCap;
|
||||
/* If the message was a Soft_Reset, do the soft reset procedure */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkSoftReset;
|
||||
/* PD 3.0 messges */
|
||||
} else if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
|
||||
/* If the message is a multi-chunk extended message, let it
|
||||
* time out. */
|
||||
if ((tempMessage.hdr & PD_HDR_EXT)
|
||||
&& (PD_DATA_SIZE_GET(&tempMessage)
|
||||
> PD_MAX_EXT_MSG_LEGACY_LEN)) {
|
||||
|
||||
return PESinkChunkReceived;
|
||||
/* Tell the DPM a message we sent got a response of
|
||||
* Not_Supported. */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage)
|
||||
== PD_MSGTYPE_NOT_SUPPORTED
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkNotSupportedReceived;
|
||||
/* If we got an unknown message, send a soft reset */
|
||||
} else {
|
||||
|
||||
return PESinkSendSoftReset;
|
||||
}
|
||||
/* If we got an unknown message, send a soft reset ??? */
|
||||
} else {
|
||||
|
||||
return PESinkSendSoftReset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_get_source_cap() {
|
||||
/* Get a message object */
|
||||
union pd_msg *get_source_cap = &tempMessage;
|
||||
/* Make a Get_Source_Cap message */
|
||||
get_source_cap->hdr = hdr_template | PD_MSGTYPE_GET_SOURCE_CAP
|
||||
| PD_NUMOBJ(0);
|
||||
/* Transmit the Get_Source_Cap */
|
||||
ProtocolTransmit::pushMessage(get_source_cap);
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
|
||||
eventmask_t evt = waitForEvent(
|
||||
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
|
||||
/* Free the sent message */
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_give_sink_cap() {
|
||||
/* Get a message object */
|
||||
union pd_msg *snk_cap = &tempMessage;
|
||||
/* Get our capabilities from the DPM */
|
||||
pdbs_dpm_get_sink_capability(snk_cap);
|
||||
|
||||
/* Transmit our capabilities */
|
||||
ProtocolTransmit::pushMessage(snk_cap);
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
|
||||
eventmask_t evt = waitForEvent(
|
||||
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
|
||||
|
||||
/* Free the Sink_Capabilities message */
|
||||
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_hard_reset() {
|
||||
/* If we've already sent the maximum number of hard resets, assume the
|
||||
* source is unresponsive. */
|
||||
if (_hard_reset_counter > PD_N_HARD_RESET_COUNT) {
|
||||
return PESinkSourceUnresponsive;
|
||||
}
|
||||
//So, we could send a hardreset here; however that will cause a power cycle on the PSU end.. Which will then reset this MCU
|
||||
//So therefore we went get anywhere :)
|
||||
/* Increment HardResetCounter */
|
||||
_hard_reset_counter++;
|
||||
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_transition_default() {
|
||||
_explicit_contract = false;
|
||||
|
||||
/* Tell the DPM to transition to default power */
|
||||
pdbs_dpm_transition_default();
|
||||
|
||||
/* There is no local hardware to reset. */
|
||||
/* Since we never change our data role from UFP, there is no reason to set
|
||||
* it here. */
|
||||
|
||||
return PESinkStartup;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_soft_reset() {
|
||||
/* No need to explicitly reset the protocol layer here. It resets itself
|
||||
* when a Soft_Reset message is received. */
|
||||
|
||||
/* Get a message object */
|
||||
union pd_msg accept;
|
||||
/* Make an Accept message */
|
||||
accept.hdr = hdr_template | PD_MSGTYPE_ACCEPT | PD_NUMOBJ(0);
|
||||
/* Transmit the Accept */
|
||||
ProtocolTransmit::pushMessage(&accept);
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
|
||||
eventmask_t evt = waitForEvent(
|
||||
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
|
||||
/* Free the sent message */
|
||||
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
return PESinkWaitCap;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_soft_reset() {
|
||||
/* No need to explicitly reset the protocol layer here. It resets itself
|
||||
* just before a Soft_Reset message is transmitted. */
|
||||
|
||||
/* Get a message object */
|
||||
union pd_msg *softrst = &tempMessage;
|
||||
/* Make a Soft_Reset message */
|
||||
softrst->hdr = hdr_template | PD_MSGTYPE_SOFT_RESET | PD_NUMOBJ(0);
|
||||
/* Transmit the soft reset */
|
||||
ProtocolTransmit::pushMessage(softrst);
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
|
||||
eventmask_t evt = waitForEvent(
|
||||
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a hard reset */
|
||||
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* Wait for a response */
|
||||
evt = waitForEvent(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET,
|
||||
PD_T_SENDER_RESPONSE);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If we didn't get a response before the timeout, send a hard reset */
|
||||
if (evt == 0) {
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
/* Get the response message */
|
||||
if (messageWaiting()) {
|
||||
readMessage();
|
||||
/* If the source accepted our soft reset, wait for capabilities. */
|
||||
if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_ACCEPT
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkWaitCap;
|
||||
/* If the message was a Soft_Reset, do the soft reset procedure */
|
||||
} else if (PD_MSGTYPE_GET(&tempMessage) == PD_MSGTYPE_SOFT_RESET
|
||||
&& PD_NUMOBJ_GET(&tempMessage) == 0) {
|
||||
|
||||
return PESinkSoftReset;
|
||||
/* Otherwise, send a hard reset */
|
||||
} else {
|
||||
|
||||
return PESinkHardReset;
|
||||
}
|
||||
}
|
||||
return PESinkHardReset;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_send_not_supported() {
|
||||
/* Get a message object */
|
||||
union pd_msg *not_supported = &tempMessage;
|
||||
|
||||
if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_2_0) {
|
||||
/* Make a Reject message */
|
||||
not_supported->hdr = hdr_template | PD_MSGTYPE_REJECT | PD_NUMOBJ(0);
|
||||
} else if ((hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0) {
|
||||
/* Make a Not_Supported message */
|
||||
not_supported->hdr = hdr_template | PD_MSGTYPE_NOT_SUPPORTED
|
||||
| PD_NUMOBJ(0);
|
||||
}
|
||||
|
||||
/* Transmit the message */
|
||||
ProtocolTransmit::pushMessage(not_supported);
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_MSG_TX);
|
||||
eventmask_t evt = waitForEvent(
|
||||
PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR | PDB_EVT_PE_RESET);
|
||||
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
/* If the message transmission failed, send a soft reset */
|
||||
if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
|
||||
return PESinkSendSoftReset;
|
||||
}
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_chunk_received() {
|
||||
|
||||
/* Wait for tChunkingNotSupported */
|
||||
eventmask_t evt = waitForEvent(PDB_EVT_PE_RESET,
|
||||
PD_T_CHUNKING_NOT_SUPPORTED);
|
||||
/* If we got reset signaling, transition to default */
|
||||
if (evt & PDB_EVT_PE_RESET) {
|
||||
return PESinkTransitionDefault;
|
||||
}
|
||||
|
||||
return PESinkSendNotSupported;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_not_supported_received() {
|
||||
/* Inform the Device Policy Manager that we received a Not_Supported
|
||||
* message. */
|
||||
|
||||
return PESinkReady;
|
||||
}
|
||||
|
||||
PolicyEngine::policy_engine_state PolicyEngine::pe_sink_source_unresponsive() {
|
||||
//Sit and chill, as PD is not working
|
||||
osDelay(PD_T_PD_DEBOUNCE);
|
||||
|
||||
return PESinkSourceUnresponsive;
|
||||
}
|
||||
|
||||
uint32_t PolicyEngine::waitForEvent(uint32_t mask, TickType_t ticksToWait) {
|
||||
return xEventGroupWaitBits(xEventGroupHandle, mask, mask, pdFALSE,
|
||||
ticksToWait);
|
||||
|
||||
}
|
||||
|
||||
bool PolicyEngine::isPD3_0() {
|
||||
return (hdr_template & PD_HDR_SPECREV) == PD_SPECREV_3_0;
|
||||
}
|
||||
|
||||
198
source/Core/Drivers/FUSB302/policy_engine.h
Normal file
198
source/Core/Drivers/FUSB302/policy_engine.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_POLICY_ENGINE_H
|
||||
#define PDB_POLICY_ENGINE_H
|
||||
|
||||
#include <pd.h>
|
||||
|
||||
/*
|
||||
* Events for the Policy Engine thread, used internally + sent by user code
|
||||
*
|
||||
*/
|
||||
|
||||
#define PDB_EVT_PE_RESET EVENT_MASK(0)
|
||||
#define PDB_EVT_PE_MSG_RX EVENT_MASK(1)
|
||||
#define PDB_EVT_PE_TX_DONE EVENT_MASK(2)
|
||||
#define PDB_EVT_PE_TX_ERR EVENT_MASK(3)
|
||||
#define PDB_EVT_PE_HARD_SENT EVENT_MASK(4)
|
||||
#define PDB_EVT_PE_I_OVRTEMP EVENT_MASK(5)
|
||||
#define PDB_EVT_PE_MSG_RX_PEND EVENT_MASK(7) /* Never SEND THIS DIRECTLY*/
|
||||
|
||||
class PolicyEngine {
|
||||
public:
|
||||
//Sets up internal state and registers the thread
|
||||
static void init();
|
||||
//Push an incoming message to the Policy Engine
|
||||
static void handleMessage(union pd_msg *msg);
|
||||
//Send a notification
|
||||
static void notify(uint32_t notification);
|
||||
//Returns true if headers indicate PD3.0 compliant
|
||||
static bool isPD3_0();
|
||||
static bool setupCompleteOrTimedOut() {
|
||||
if (pdNegotiationComplete)
|
||||
return true;
|
||||
if (state == policy_engine_state::PESinkSourceUnresponsive)
|
||||
return true;
|
||||
if (state == policy_engine_state::PESinkReady)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
//Has pd negotiation completed
|
||||
static bool pdHasNegotiated() {
|
||||
return pdNegotiationComplete;
|
||||
}
|
||||
private:
|
||||
static bool pdNegotiationComplete;
|
||||
static int current_voltage_mv; //The current voltage PD is expecting
|
||||
static int _requested_voltage; //The voltage the unit wanted to requests
|
||||
static bool _unconstrained_power; // If the source is unconstrained
|
||||
//Current message being handled
|
||||
static union pd_msg currentMessage;
|
||||
/* PD message header template */
|
||||
static uint16_t hdr_template;
|
||||
/* Whether or not we have an explicit contract */
|
||||
static bool _explicit_contract;
|
||||
/* The number of hard resets we've sent */
|
||||
static int8_t _hard_reset_counter;
|
||||
/* The result of the last Type-C Current match comparison */
|
||||
static int8_t _old_tcc_match;
|
||||
/* The index of the first PPS APDO */
|
||||
static uint8_t _pps_index;
|
||||
/* The index of the just-requested PPS APDO */
|
||||
static uint8_t _last_pps;
|
||||
static void pe_task(const void *arg);
|
||||
enum policy_engine_state {
|
||||
PESinkStartup,
|
||||
PESinkDiscovery,
|
||||
PESinkWaitCap,
|
||||
PESinkEvalCap,
|
||||
PESinkSelectCap,
|
||||
PESinkTransitionSink,
|
||||
PESinkReady,
|
||||
PESinkGetSourceCap,
|
||||
PESinkGiveSinkCap,
|
||||
PESinkHardReset,
|
||||
PESinkTransitionDefault,
|
||||
PESinkSoftReset,
|
||||
PESinkSendSoftReset,
|
||||
PESinkSendNotSupported,
|
||||
PESinkChunkReceived,
|
||||
PESinkNotSupportedReceived,
|
||||
PESinkSourceUnresponsive
|
||||
};
|
||||
static enum policy_engine_state pe_sink_startup();
|
||||
static enum policy_engine_state pe_sink_discovery();
|
||||
static enum policy_engine_state pe_sink_wait_cap();
|
||||
static enum policy_engine_state pe_sink_eval_cap();
|
||||
static enum policy_engine_state pe_sink_select_cap();
|
||||
static enum policy_engine_state pe_sink_transition_sink();
|
||||
static enum policy_engine_state pe_sink_ready();
|
||||
static enum policy_engine_state pe_sink_get_source_cap();
|
||||
static enum policy_engine_state pe_sink_give_sink_cap();
|
||||
static enum policy_engine_state pe_sink_hard_reset();
|
||||
static enum policy_engine_state pe_sink_transition_default();
|
||||
static enum policy_engine_state pe_sink_soft_reset();
|
||||
static enum policy_engine_state pe_sink_send_soft_reset();
|
||||
static enum policy_engine_state pe_sink_send_not_supported();
|
||||
static enum policy_engine_state pe_sink_chunk_received();
|
||||
static enum policy_engine_state pe_sink_not_supported_received();
|
||||
static enum policy_engine_state pe_sink_source_unresponsive();
|
||||
static EventGroupHandle_t xEventGroupHandle;
|
||||
static StaticEventGroup_t xCreatedEventGroup;
|
||||
static uint32_t waitForEvent(uint32_t mask, TickType_t ticksToWait =
|
||||
portMAX_DELAY);
|
||||
//Task resources
|
||||
static osThreadId TaskHandle;
|
||||
static const size_t TaskStackSize = 2048 / 4;
|
||||
static uint32_t TaskBuffer[TaskStackSize];
|
||||
static osStaticThreadDef_t TaskControlBlock;
|
||||
static union pd_msg tempMessage;
|
||||
static union pd_msg _last_dpm_request;
|
||||
static policy_engine_state state;
|
||||
//queue of up to PDB_MSG_POOL_SIZE messages to send
|
||||
static StaticQueue_t xStaticQueue;
|
||||
/* The array to use as the queue's storage area. This must be at least
|
||||
uxQueueLength * uxItemSize bytes. */
|
||||
static uint8_t ucQueueStorageArea[PDB_MSG_POOL_SIZE * sizeof(union pd_msg)];
|
||||
static QueueHandle_t messagesWaiting;
|
||||
static bool messageWaiting();
|
||||
//Read a pending message into the temp message
|
||||
static bool readMessage();
|
||||
|
||||
// These callbacks are called to implement the logic for the iron to select the desired voltage
|
||||
|
||||
/*
|
||||
* Create a Request message based on the given Source_Capabilities message. If
|
||||
* capabilities is NULL, the last non-null Source_Capabilities message passes
|
||||
* is used. If none has been provided, the behavior is undefined.
|
||||
*
|
||||
* Returns true if sufficient power is available, false otherwise.
|
||||
*/
|
||||
static bool pdbs_dpm_evaluate_capability(const union pd_msg *capabilities,
|
||||
union pd_msg *request);
|
||||
|
||||
/*
|
||||
* Create a Sink_Capabilities message for our current capabilities.
|
||||
*/
|
||||
static void pdbs_dpm_get_sink_capability(union pd_msg *cap);
|
||||
|
||||
/*
|
||||
* Return whether or not GiveBack support is enabled.
|
||||
*/
|
||||
static bool pdbs_dpm_giveback_enabled();
|
||||
|
||||
/*
|
||||
* Evaluate whether or not the currently offered Type-C Current can fulfill our
|
||||
* power needs.
|
||||
*
|
||||
* Returns true if sufficient power is available, false otherwise.
|
||||
*/
|
||||
static bool pdbs_dpm_evaluate_typec_current(enum fusb_typec_current tcc);
|
||||
|
||||
/*
|
||||
* Indicate that power negotiations are starting.
|
||||
*/
|
||||
static void pdbs_dpm_pd_start();
|
||||
|
||||
/*
|
||||
* Transition the sink to default power.
|
||||
*/
|
||||
static void pdbs_dpm_transition_default();
|
||||
|
||||
/*
|
||||
* Transition to the requested minimum current.
|
||||
*/
|
||||
static void pdbs_dpm_transition_min();
|
||||
|
||||
/*
|
||||
* Transition to Sink Standby if necessary.
|
||||
*/
|
||||
static void pdbs_dpm_transition_standby();
|
||||
|
||||
/*
|
||||
* Transition to the requested power level
|
||||
*/
|
||||
static void pdbs_dpm_transition_requested();
|
||||
|
||||
/*
|
||||
* Transition to the Type-C Current power level
|
||||
*/
|
||||
static void pdbs_dpm_transition_typec();
|
||||
};
|
||||
|
||||
#endif /* PDB_POLICY_ENGINE_H */
|
||||
227
source/Core/Drivers/FUSB302/policy_engine_user.cpp
Normal file
227
source/Core/Drivers/FUSB302/policy_engine_user.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* policy_engine_user.cpp
|
||||
*
|
||||
* Created on: 14 Jun 2020
|
||||
* Author: Ralim
|
||||
*/
|
||||
#include "pd.h"
|
||||
#include "policy_engine.h"
|
||||
#include "BSP_PD.h"
|
||||
/* The current draw when the output is disabled */
|
||||
#define DPM_MIN_CURRENT PD_MA2PDI(50)
|
||||
/*
|
||||
* Find the index of the first PDO from capabilities in the voltage range,
|
||||
* using the desired order.
|
||||
*
|
||||
* If there is no such PDO, returns -1 instead.
|
||||
*/
|
||||
static int8_t dpm_get_range_fixed_pdo_index(const union pd_msg *caps) {
|
||||
/* Get the number of PDOs */
|
||||
uint8_t numobj = PD_NUMOBJ_GET(caps);
|
||||
|
||||
/* Get ready to iterate over the PDOs */
|
||||
int8_t i;
|
||||
int8_t step;
|
||||
i = numobj - 1;
|
||||
step = -1;
|
||||
uint16_t current = 100; // in centiamps
|
||||
uint16_t voltagemin = 8000;
|
||||
uint16_t voltagemax = 10000;
|
||||
/* Look at the PDOs to see if one falls in our voltage range. */
|
||||
while (0 <= i && i < numobj) {
|
||||
/* If we have a fixed PDO, its V is within our range, and its I is at
|
||||
* least our desired I */
|
||||
uint16_t v = PD_PDO_SRC_FIXED_VOLTAGE_GET(caps->obj[i]);
|
||||
if ((caps->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
|
||||
if ( PD_PDO_SRC_FIXED_CURRENT_GET(caps->obj[i]) >= current) {
|
||||
if (v >= PD_MV2PDV(voltagemin) && v <= PD_MV2PDV(voltagemax)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += step;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
bool PolicyEngine::pdbs_dpm_evaluate_capability(
|
||||
const union pd_msg *capabilities, union pd_msg *request) {
|
||||
|
||||
/* Get the number of PDOs */
|
||||
uint8_t numobj = PD_NUMOBJ_GET(capabilities);
|
||||
|
||||
/* Get whether or not the power supply is constrained */
|
||||
_unconstrained_power =
|
||||
capabilities->obj[0] & PD_PDO_SRC_FIXED_UNCONSTRAINED;
|
||||
|
||||
/* Make sure we have configuration */
|
||||
/* Look at the PDOs to see if one matches our desires */
|
||||
//Look against USB_PD_Desired_Levels to select in order of preference
|
||||
for (uint8_t desiredLevel = 0; desiredLevel < USB_PD_Desired_Levels_Len;
|
||||
desiredLevel++) {
|
||||
for (uint8_t i = 0; i < numobj; i++) {
|
||||
/* If we have a fixed PDO, its V equals our desired V, and its I is
|
||||
* at least our desired I */
|
||||
if ((capabilities->obj[i] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
|
||||
//This is a fixed PDO entry
|
||||
int voltage = PD_PDV2MV(
|
||||
PD_PDO_SRC_FIXED_VOLTAGE_GET(capabilities->obj[i]));
|
||||
int current = PD_PDO_SRC_FIXED_CURRENT_GET(
|
||||
capabilities->obj[i]);
|
||||
uint16_t desiredVoltage = USB_PD_Desired_Levels[(desiredLevel
|
||||
* 2) + 0];
|
||||
uint16_t desiredminCurrent = USB_PD_Desired_Levels[(desiredLevel
|
||||
* 2) + 1];
|
||||
//As pd stores current in 10mA increments, divide by 10
|
||||
desiredminCurrent /= 10;
|
||||
if (voltage == desiredVoltage) {
|
||||
if (current >= desiredminCurrent) {
|
||||
/* We got what we wanted, so build a request for that */
|
||||
request->hdr = hdr_template | PD_MSGTYPE_REQUEST
|
||||
| PD_NUMOBJ(1);
|
||||
|
||||
/* GiveBack disabled */
|
||||
request->obj[0] =
|
||||
PD_RDO_FV_MAX_CURRENT_SET(
|
||||
current) | PD_RDO_FV_CURRENT_SET(current)
|
||||
| PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(i + 1);
|
||||
//We support usb comms (ish)
|
||||
request->obj[0] |= PD_RDO_USB_COMMS;
|
||||
|
||||
/* Update requested voltage */
|
||||
_requested_voltage = voltage;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Nothing matched (or no configuration), so get 5 V at low current */
|
||||
request->hdr = hdr_template | PD_MSGTYPE_REQUEST | PD_NUMOBJ(1);
|
||||
request->obj[0] =
|
||||
PD_RDO_FV_MAX_CURRENT_SET(
|
||||
DPM_MIN_CURRENT) | PD_RDO_FV_CURRENT_SET(DPM_MIN_CURRENT) | PD_RDO_NO_USB_SUSPEND
|
||||
| PD_RDO_OBJPOS_SET(1);
|
||||
/* If the output is enabled and we got here, it must be a capability
|
||||
* mismatch. */
|
||||
if (pdNegotiationComplete) {
|
||||
request->obj[0] |= PD_RDO_CAP_MISMATCH;
|
||||
}
|
||||
request->obj[0] |= PD_RDO_USB_COMMS;
|
||||
|
||||
/* Update requested voltage */
|
||||
_requested_voltage = 5000;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PolicyEngine::pdbs_dpm_get_sink_capability(union pd_msg *cap) {
|
||||
/* Keep track of how many PDOs we've added */
|
||||
int numobj = 0;
|
||||
|
||||
/* If we have no configuration or want something other than 5 V, add a PDO
|
||||
* for vSafe5V */
|
||||
/* Minimum current, 5 V, and higher capability. */
|
||||
cap->obj[numobj++] =
|
||||
PD_PDO_TYPE_FIXED
|
||||
| PD_PDO_SNK_FIXED_VOLTAGE_SET(
|
||||
PD_MV2PDV(5000)) | PD_PDO_SNK_FIXED_CURRENT_SET(DPM_MIN_CURRENT);
|
||||
|
||||
/* Get the current we want */
|
||||
uint16_t current = USB_PD_Desired_Levels[1] / 10; // In centi-amps
|
||||
uint16_t voltage = USB_PD_Desired_Levels[0]; // in mv
|
||||
/* Add a PDO for the desired power. */
|
||||
cap->obj[numobj++] = PD_PDO_TYPE_FIXED
|
||||
| PD_PDO_SNK_FIXED_VOLTAGE_SET(
|
||||
PD_MV2PDV(voltage)) | PD_PDO_SNK_FIXED_CURRENT_SET(current);
|
||||
|
||||
/* Get the PDO from the voltage range */
|
||||
int8_t i = dpm_get_range_fixed_pdo_index(cap);
|
||||
|
||||
/* If it's vSafe5V, set our vSafe5V's current to what we want */
|
||||
if (i == 0) {
|
||||
cap->obj[0] &= ~PD_PDO_SNK_FIXED_CURRENT;
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_CURRENT_SET(current);
|
||||
} else {
|
||||
/* If we want more than 5 V, set the Higher Capability flag */
|
||||
if (PD_MV2PDV(voltage) != PD_MV2PDV(5000)) {
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_HIGHER_CAP;
|
||||
}
|
||||
|
||||
/* If the range PDO is a different voltage than the preferred
|
||||
* voltage, add it to the array. */
|
||||
if (i
|
||||
> 0&& PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i]) != PD_MV2PDV(voltage)) {
|
||||
cap->obj[numobj++] =
|
||||
PD_PDO_TYPE_FIXED
|
||||
| PD_PDO_SNK_FIXED_VOLTAGE_SET(
|
||||
PD_PDO_SRC_FIXED_VOLTAGE_GET(cap->obj[i])) | PD_PDO_SNK_FIXED_CURRENT_SET(
|
||||
PD_PDO_SRC_FIXED_CURRENT_GET(cap->obj[i]));
|
||||
}
|
||||
|
||||
/* If we have three PDOs at this point, make sure the last two are
|
||||
* sorted by voltage. */
|
||||
if (numobj == 3
|
||||
&& (cap->obj[1] & PD_PDO_SNK_FIXED_VOLTAGE)
|
||||
> (cap->obj[2] & PD_PDO_SNK_FIXED_VOLTAGE)) {
|
||||
cap->obj[1] ^= cap->obj[2];
|
||||
cap->obj[2] ^= cap->obj[1];
|
||||
cap->obj[1] ^= cap->obj[2];
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the unconstrained power flag. */
|
||||
if (_unconstrained_power) {
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_UNCONSTRAINED;
|
||||
}
|
||||
/* Set the USB communications capable flag. */
|
||||
cap->obj[0] |= PD_PDO_SNK_FIXED_USB_COMMS;
|
||||
|
||||
/* Set the Sink_Capabilities message header */
|
||||
cap->hdr = hdr_template | PD_MSGTYPE_SINK_CAPABILITIES | PD_NUMOBJ(numobj);
|
||||
}
|
||||
|
||||
bool PolicyEngine::pdbs_dpm_evaluate_typec_current(
|
||||
enum fusb_typec_current tcc) {
|
||||
(void) tcc;
|
||||
//This is for evaluating 5V static current advertised by resistors
|
||||
/* We don't control the voltage anymore; it will always be 5 V. */
|
||||
current_voltage_mv = _requested_voltage = 5000;
|
||||
//For the soldering iron we accept this as a fallback, but it sucks
|
||||
pdNegotiationComplete = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PolicyEngine::pdbs_dpm_transition_default() {
|
||||
/* Cast the dpm_data to the right type */
|
||||
|
||||
/* Pretend we requested 5 V */
|
||||
current_voltage_mv = 5000;
|
||||
/* Turn the output off */
|
||||
pdNegotiationComplete = false;
|
||||
}
|
||||
|
||||
void PolicyEngine::pdbs_dpm_transition_requested() {
|
||||
/* Cast the dpm_data to the right type */
|
||||
pdNegotiationComplete = true;
|
||||
}
|
||||
|
||||
void PolicyEngine::handleMessage(union pd_msg *msg) {
|
||||
xQueueSend(messagesWaiting, msg, 100);
|
||||
}
|
||||
|
||||
bool PolicyEngine::messageWaiting() {
|
||||
return uxQueueMessagesWaiting(messagesWaiting) > 0;
|
||||
}
|
||||
|
||||
bool PolicyEngine::readMessage() {
|
||||
return xQueueReceive(messagesWaiting, &tempMessage, 0) == pdTRUE;
|
||||
}
|
||||
|
||||
void PolicyEngine::pdbs_dpm_transition_typec() {
|
||||
//This means PD failed, so we either have a dump 5V only type C or a QC charger
|
||||
//For now; treat this as failed neg
|
||||
pdNegotiationComplete = false;
|
||||
}
|
||||
189
source/Core/Drivers/FUSB302/protocol_rx.cpp
Normal file
189
source/Core/Drivers/FUSB302/protocol_rx.cpp
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 "protocol_rx.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "string.h"
|
||||
#include <pd.h>
|
||||
#include "policy_engine.h"
|
||||
#include "protocol_tx.h"
|
||||
#include "fusb302b.h"
|
||||
osThreadId ProtocolReceive::TaskHandle = NULL;
|
||||
EventGroupHandle_t ProtocolReceive::xEventGroupHandle = NULL;
|
||||
StaticEventGroup_t ProtocolReceive::xCreatedEventGroup;
|
||||
uint32_t ProtocolReceive::TaskBuffer[ProtocolReceive::TaskStackSize];
|
||||
osStaticThreadDef_t ProtocolReceive::TaskControlBlock;
|
||||
union pd_msg ProtocolReceive::tempMessage;
|
||||
uint8_t ProtocolReceive::_rx_messageid;
|
||||
uint8_t ProtocolReceive::_tx_messageidcounter;
|
||||
/*
|
||||
* PRL_Rx_Wait_for_PHY_Message state
|
||||
*/
|
||||
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_wait_phy() {
|
||||
/* Wait for an event */
|
||||
_rx_messageid = 0;
|
||||
eventmask_t evt = waitForEvent(
|
||||
PDB_EVT_PRLRX_RESET | PDB_EVT_PRLRX_I_GCRCSENT | PDB_EVT_PRLRX_I_RXPEND);
|
||||
|
||||
/* If we got a reset event, reset */
|
||||
if (evt & PDB_EVT_PRLRX_RESET) {
|
||||
waitForEvent(PDB_EVT_PRLRX_RESET, 0);
|
||||
return PRLRxWaitPHY;
|
||||
}
|
||||
/* If we got an I_GCRCSENT event, read the message and decide what to do */
|
||||
if (evt & PDB_EVT_PRLRX_I_GCRCSENT) {
|
||||
/* Get a buffer to read the message into. Guaranteed to not fail
|
||||
* because we have a big enough pool and are careful. */
|
||||
union pd_msg *_rx_message = &tempMessage;
|
||||
memset(&tempMessage, 0, sizeof(tempMessage));
|
||||
/* Read the message */
|
||||
fusb_read_message(_rx_message);
|
||||
/* If it's a Soft_Reset, go to the soft reset state */
|
||||
if (PD_MSGTYPE_GET(_rx_message) == PD_MSGTYPE_SOFT_RESET
|
||||
&& PD_NUMOBJ_GET(_rx_message) == 0) {
|
||||
return PRLRxReset;
|
||||
} else {
|
||||
/* Otherwise, check the message ID */
|
||||
return PRLRxCheckMessageID;
|
||||
}
|
||||
} else if (evt & PDB_EVT_PRLRX_I_RXPEND) {
|
||||
//There is an RX message pending that is not a Good CRC
|
||||
union pd_msg *_rx_message = &tempMessage;
|
||||
/* Read the message */
|
||||
fusb_read_message(_rx_message);
|
||||
return PRLRxWaitPHY;
|
||||
}
|
||||
|
||||
return PRLRxWaitPHY;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRL_Rx_Layer_Reset_for_Receive state
|
||||
*/
|
||||
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_reset() {
|
||||
/* Reset MessageIDCounter */
|
||||
_tx_messageidcounter = 0;
|
||||
|
||||
/* Clear stored MessageID */
|
||||
_rx_messageid = -1;
|
||||
|
||||
/* TX transitions to its reset state */
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_RESET);
|
||||
taskYIELD();
|
||||
|
||||
/* If we got a RESET signal, reset the machine */
|
||||
if (waitForEvent(PDB_EVT_PRLRX_RESET, 0) != 0) {
|
||||
return PRLRxWaitPHY;
|
||||
}
|
||||
|
||||
/* Go to the Check_MessageID state */
|
||||
return PRLRxCheckMessageID;
|
||||
}
|
||||
volatile uint32_t rxCounter = 0;
|
||||
/*
|
||||
* PRL_Rx_Check_MessageID state
|
||||
*/
|
||||
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_check_messageid() {
|
||||
/* If we got a RESET signal, reset the machine */
|
||||
// if (waitForEvent(PDB_EVT_PRLRX_RESET, 0) == PDB_EVT_PRLRX_RESET) {
|
||||
// return PRLRxWaitPHY;
|
||||
// }
|
||||
/* If the message has the stored ID, we've seen this message before. Free
|
||||
* it and don't pass it to the policy engine. */
|
||||
|
||||
/* Otherwise, there's either no stored ID or this message has an ID we
|
||||
* haven't just seen. Transition to the Store_MessageID state. */
|
||||
// if (PD_MESSAGEID_GET(&tempMessage) == _rx_messageid) {
|
||||
// return PRLRxWaitPHY;
|
||||
// } else
|
||||
{
|
||||
rxCounter++;
|
||||
return PRLRxStoreMessageID;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PRL_Rx_Store_MessageID state
|
||||
*/
|
||||
ProtocolReceive::protocol_rx_state ProtocolReceive::protocol_rx_store_messageid() {
|
||||
/* Tell ProtocolTX to discard the message being transmitted */
|
||||
|
||||
ProtocolTransmit::notify(
|
||||
ProtocolTransmit::Notifications::PDB_EVT_PRLTX_DISCARD);
|
||||
|
||||
/* Update the stored MessageID */
|
||||
_rx_messageid = PD_MESSAGEID_GET(&tempMessage);
|
||||
|
||||
/* Pass the message to the policy engine. */
|
||||
|
||||
PolicyEngine::handleMessage(&tempMessage);
|
||||
PolicyEngine::notify(PDB_EVT_PE_MSG_RX);
|
||||
taskYIELD();
|
||||
/* Don't check if we got a RESET because we'd do nothing different. */
|
||||
|
||||
return PRLRxWaitPHY;
|
||||
}
|
||||
|
||||
void ProtocolReceive::init() {
|
||||
osThreadStaticDef(protRX, thread, PDB_PRIO_PRL, 0, TaskStackSize,
|
||||
TaskBuffer, &TaskControlBlock);
|
||||
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
|
||||
TaskHandle = osThreadCreate(osThread(protRX), NULL);
|
||||
}
|
||||
|
||||
void ProtocolReceive::thread(const void *args) {
|
||||
(void) args;
|
||||
ProtocolReceive::protocol_rx_state state = PRLRxWaitPHY;
|
||||
|
||||
while (true) {
|
||||
switch (state) {
|
||||
case PRLRxWaitPHY:
|
||||
state = protocol_rx_wait_phy();
|
||||
break;
|
||||
case PRLRxReset:
|
||||
state = protocol_rx_reset();
|
||||
break;
|
||||
case PRLRxCheckMessageID:
|
||||
state = protocol_rx_check_messageid();
|
||||
break;
|
||||
case PRLRxStoreMessageID:
|
||||
state = protocol_rx_store_messageid();
|
||||
break;
|
||||
default:
|
||||
/* This is an error. It really shouldn't happen. We might
|
||||
* want to handle it anyway, though. */
|
||||
state = PRLRxWaitPHY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProtocolReceive::notify(uint32_t notification) {
|
||||
if (xEventGroupHandle != NULL) {
|
||||
xEventGroupSetBits(xEventGroupHandle, notification);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ProtocolReceive::waitForEvent(uint32_t mask, TickType_t ticksToWait) {
|
||||
if (xEventGroupHandle != NULL) {
|
||||
return xEventGroupWaitBits(xEventGroupHandle, mask, mask,
|
||||
pdFALSE, ticksToWait);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
64
source/Core/Drivers/FUSB302/protocol_rx.h
Normal file
64
source/Core/Drivers/FUSB302/protocol_rx.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_PROTOCOL_RX_H
|
||||
#define PDB_PROTOCOL_RX_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pd.h>
|
||||
|
||||
/* Events for the Protocol RX thread */
|
||||
#define PDB_EVT_PRLRX_RESET EVENT_MASK(0)
|
||||
#define PDB_EVT_PRLRX_I_GCRCSENT EVENT_MASK(1)
|
||||
#define PDB_EVT_PRLRX_I_RXPEND EVENT_MASK(2)
|
||||
|
||||
class ProtocolReceive {
|
||||
public:
|
||||
static void init();
|
||||
static void notify(uint32_t notification);
|
||||
private:
|
||||
static void thread(const void *args);
|
||||
|
||||
static EventGroupHandle_t xEventGroupHandle;
|
||||
static StaticEventGroup_t xCreatedEventGroup;
|
||||
static osThreadId TaskHandle;
|
||||
static const size_t TaskStackSize = 1024 / 4;
|
||||
static uint32_t TaskBuffer[TaskStackSize];
|
||||
static osStaticThreadDef_t TaskControlBlock;
|
||||
/*
|
||||
* Protocol RX machine states
|
||||
*
|
||||
* There is no Send_GoodCRC state because the PHY sends the GoodCRC for us.
|
||||
* All transitions that would go to that state instead go to Check_MessageID.
|
||||
*/
|
||||
enum protocol_rx_state {
|
||||
PRLRxWaitPHY, PRLRxReset, PRLRxCheckMessageID, PRLRxStoreMessageID
|
||||
};
|
||||
static protocol_rx_state protocol_rx_store_messageid();
|
||||
static protocol_rx_state protocol_rx_check_messageid();
|
||||
static protocol_rx_state protocol_rx_reset();
|
||||
static protocol_rx_state protocol_rx_wait_phy();
|
||||
static union pd_msg tempMessage;
|
||||
static uint8_t _rx_messageid;
|
||||
static uint8_t _tx_messageidcounter;
|
||||
static uint32_t waitForEvent(uint32_t mask, TickType_t ticksToWait =
|
||||
portMAX_DELAY);
|
||||
|
||||
};
|
||||
|
||||
#endif /* PDB_PROTOCOL_RX_H */
|
||||
298
source/Core/Drivers/FUSB302/protocol_tx.cpp
Normal file
298
source/Core/Drivers/FUSB302/protocol_tx.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 "protocol_tx.h"
|
||||
#include <pd.h>
|
||||
#include "policy_engine.h"
|
||||
#include "protocol_rx.h"
|
||||
#include "fusb302b.h"
|
||||
#include "fusbpd.h"
|
||||
|
||||
osThreadId ProtocolTransmit::TaskHandle = NULL;
|
||||
uint32_t ProtocolTransmit::TaskBuffer[ProtocolTransmit::TaskStackSize];
|
||||
osStaticThreadDef_t ProtocolTransmit::TaskControlBlock;
|
||||
StaticQueue_t ProtocolTransmit::xStaticQueue;
|
||||
bool ProtocolTransmit::messageSending = false;
|
||||
uint8_t ProtocolTransmit::ucQueueStorageArea[PDB_MSG_POOL_SIZE
|
||||
* sizeof(union pd_msg)];
|
||||
QueueHandle_t ProtocolTransmit::messagesWaiting = NULL;
|
||||
uint8_t ProtocolTransmit::_tx_messageidcounter;
|
||||
union pd_msg ProtocolTransmit::temp_msg;
|
||||
EventGroupHandle_t ProtocolTransmit::xEventGroupHandle = NULL;
|
||||
StaticEventGroup_t ProtocolTransmit::xCreatedEventGroup;
|
||||
/*
|
||||
* PRL_Tx_PHY_Layer_Reset state
|
||||
*/
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_phy_reset() {
|
||||
/* Reset the PHY */
|
||||
fusb_reset();
|
||||
|
||||
/* If a message was pending when we got here, tell the policy engine that
|
||||
* we failed to send it */
|
||||
if (messagePending()) {
|
||||
/* Tell the policy engine that we failed */
|
||||
PolicyEngine::notify( PDB_EVT_PE_TX_ERR);
|
||||
/* Finish failing to send the message */
|
||||
while (messagePending()) {
|
||||
getMessage(); //Discard
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for a message request */
|
||||
return PRLTxWaitMessage;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRL_Tx_Wait_for_Message_Request state
|
||||
*/
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_wait_message() {
|
||||
/* Wait for an event */
|
||||
ProtocolTransmit::Notifications evt = waitForEvent(
|
||||
(uint32_t) Notifications::PDB_EVT_PRLTX_RESET
|
||||
| (uint32_t) Notifications::PDB_EVT_PRLTX_DISCARD
|
||||
| (uint32_t) Notifications::PDB_EVT_PRLTX_MSG_TX);
|
||||
|
||||
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_RESET) {
|
||||
return PRLTxPHYReset;
|
||||
}
|
||||
|
||||
/* If the policy engine is trying to send a message */
|
||||
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_MSG_TX) {
|
||||
/* Get the message */
|
||||
getMessage();
|
||||
|
||||
/* If it's a Soft_Reset, reset the TX layer first */
|
||||
if (PD_MSGTYPE_GET(&temp_msg) == PD_MSGTYPE_SOFT_RESET
|
||||
&& PD_NUMOBJ_GET(&(temp_msg)) == 0) {
|
||||
return PRLTxReset;
|
||||
/* Otherwise, just send the message */
|
||||
} else {
|
||||
return PRLTxConstructMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/* Silence the compiler warning */
|
||||
return PRLTxWaitMessage;
|
||||
}
|
||||
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_reset() {
|
||||
/* Clear MessageIDCounter */
|
||||
_tx_messageidcounter = 0;
|
||||
|
||||
/* Tell the Protocol RX thread to reset */
|
||||
ProtocolReceive::notify( PDB_EVT_PRLRX_RESET);
|
||||
taskYIELD();
|
||||
|
||||
return PRLTxConstructMessage;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRL_Tx_Construct_Message state
|
||||
*/
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_construct_message() {
|
||||
/* Set the correct MessageID in the message */
|
||||
temp_msg.hdr &= ~PD_HDR_MESSAGEID;
|
||||
temp_msg.hdr |= (_tx_messageidcounter % 8) << PD_HDR_MESSAGEID_SHIFT;
|
||||
|
||||
/* PD 3.0 collision avoidance */
|
||||
// if (PolicyEngine::isPD3_0()) {
|
||||
// /* If we're starting an AMS, wait for permission to transmit */
|
||||
// evt = waitForEvent((uint32_t) Notifications::PDB_EVT_PRLTX_START_AMS,
|
||||
// 0);
|
||||
// if ((uint32_t) evt
|
||||
// & (uint32_t) Notifications::PDB_EVT_PRLTX_START_AMS) {
|
||||
// while (fusb_get_typec_current() != fusb_sink_tx_ok) {
|
||||
// osDelay(1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
messageSending = true;
|
||||
/* Send the message to the PHY */
|
||||
fusb_send_message(&temp_msg);
|
||||
|
||||
return PRLTxWaitResponse;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRL_Tx_Wait_for_PHY_Response state
|
||||
*/
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_wait_response() {
|
||||
/* Wait for an event. There is no need to run CRCReceiveTimer, since the
|
||||
* FUSB302B handles that as part of its retry mechanism. */
|
||||
ProtocolTransmit::Notifications evt = waitForEvent(
|
||||
(uint32_t) Notifications::PDB_EVT_PRLTX_RESET
|
||||
| (uint32_t) Notifications::PDB_EVT_PRLTX_DISCARD
|
||||
| (uint32_t) Notifications::PDB_EVT_PRLTX_I_TXSENT
|
||||
| (uint32_t) Notifications::PDB_EVT_PRLTX_I_RETRYFAIL);
|
||||
|
||||
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_RESET) {
|
||||
return PRLTxPHYReset;
|
||||
}
|
||||
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_DISCARD) {
|
||||
return PRLTxDiscardMessage;
|
||||
}
|
||||
|
||||
/* If the message was sent successfully */
|
||||
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_I_TXSENT) {
|
||||
return PRLTxMatchMessageID;
|
||||
}
|
||||
/* If the message failed to be sent */
|
||||
if ((uint32_t) evt & (uint32_t) Notifications::PDB_EVT_PRLTX_I_RETRYFAIL) {
|
||||
return PRLTxTransmissionError;
|
||||
}
|
||||
|
||||
/* Silence the compiler warning */
|
||||
return PRLTxDiscardMessage;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRL_Tx_Match_MessageID state
|
||||
*/
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_match_messageid() {
|
||||
union pd_msg goodcrc;
|
||||
|
||||
/* Read the GoodCRC */
|
||||
fusb_read_message(&goodcrc);
|
||||
|
||||
/* Check that the message is correct */
|
||||
if (PD_MSGTYPE_GET(&goodcrc) == PD_MSGTYPE_GOODCRC
|
||||
&& PD_NUMOBJ_GET(&goodcrc) == 0
|
||||
&& PD_MESSAGEID_GET(&goodcrc) == _tx_messageidcounter) {
|
||||
return PRLTxMessageSent;
|
||||
} else {
|
||||
return PRLTxTransmissionError;
|
||||
}
|
||||
}
|
||||
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_transmission_error() {
|
||||
/* Increment MessageIDCounter */
|
||||
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
|
||||
|
||||
/* Tell the policy engine that we failed */
|
||||
PolicyEngine::notify( PDB_EVT_PE_TX_ERR);
|
||||
|
||||
return PRLTxWaitMessage;
|
||||
}
|
||||
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_message_sent() {
|
||||
messageSending = false;
|
||||
/* Increment MessageIDCounter */
|
||||
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
|
||||
|
||||
/* Tell the policy engine that we succeeded */
|
||||
PolicyEngine::notify( PDB_EVT_PE_TX_DONE);
|
||||
|
||||
return PRLTxWaitMessage;
|
||||
}
|
||||
|
||||
ProtocolTransmit::protocol_tx_state ProtocolTransmit::protocol_tx_discard_message() {
|
||||
/* If we were working on sending a message, increment MessageIDCounter */
|
||||
if (messageSending) {
|
||||
_tx_messageidcounter = (_tx_messageidcounter + 1) % 8;
|
||||
|
||||
return PRLTxPHYReset;
|
||||
} else {
|
||||
return PRLTxWaitMessage;
|
||||
}
|
||||
}
|
||||
void ProtocolTransmit::thread(const void *args) {
|
||||
(void) args;
|
||||
ProtocolTransmit::protocol_tx_state state = PRLTxPHYReset;
|
||||
|
||||
//Init the incoming message queue
|
||||
|
||||
while (true) {
|
||||
switch (state) {
|
||||
case PRLTxPHYReset:
|
||||
state = protocol_tx_phy_reset();
|
||||
break;
|
||||
case PRLTxWaitMessage:
|
||||
state = protocol_tx_wait_message();
|
||||
break;
|
||||
case PRLTxReset:
|
||||
state = protocol_tx_reset();
|
||||
break;
|
||||
case PRLTxConstructMessage:
|
||||
state = protocol_tx_construct_message();
|
||||
break;
|
||||
case PRLTxWaitResponse:
|
||||
state = protocol_tx_wait_response();
|
||||
break;
|
||||
case PRLTxMatchMessageID:
|
||||
state = protocol_tx_match_messageid();
|
||||
break;
|
||||
case PRLTxTransmissionError:
|
||||
state = protocol_tx_transmission_error();
|
||||
break;
|
||||
case PRLTxMessageSent:
|
||||
state = protocol_tx_message_sent();
|
||||
break;
|
||||
case PRLTxDiscardMessage:
|
||||
state = protocol_tx_discard_message();
|
||||
break;
|
||||
default:
|
||||
state = PRLTxPHYReset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProtocolTransmit::notify(ProtocolTransmit::Notifications notification) {
|
||||
if (xEventGroupHandle != NULL) {
|
||||
xEventGroupSetBits(xEventGroupHandle, (uint32_t) notification);
|
||||
}
|
||||
}
|
||||
|
||||
void ProtocolTransmit::init() {
|
||||
messagesWaiting = xQueueCreateStatic(PDB_MSG_POOL_SIZE,
|
||||
sizeof(union pd_msg), ucQueueStorageArea, &xStaticQueue);
|
||||
|
||||
osThreadStaticDef(pd_txTask, thread, PDB_PRIO_PRL, 0, TaskStackSize,
|
||||
TaskBuffer, &TaskControlBlock);
|
||||
TaskHandle = osThreadCreate(osThread(pd_txTask), NULL);
|
||||
xEventGroupHandle = xEventGroupCreateStatic(&xCreatedEventGroup);
|
||||
}
|
||||
|
||||
void ProtocolTransmit::pushMessage(union pd_msg *msg) {
|
||||
if (messagesWaiting) {
|
||||
xQueueSend(messagesWaiting, msg, 100);
|
||||
}
|
||||
}
|
||||
|
||||
bool ProtocolTransmit::messagePending() {
|
||||
if (messagesWaiting) {
|
||||
return uxQueueMessagesWaiting(messagesWaiting) > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProtocolTransmit::getMessage() {
|
||||
//Loads the pending message into the buffer
|
||||
if (messagesWaiting) {
|
||||
xQueueReceive(messagesWaiting, &temp_msg, 1);
|
||||
}
|
||||
}
|
||||
|
||||
ProtocolTransmit::Notifications ProtocolTransmit::waitForEvent(uint32_t mask,
|
||||
TickType_t ticksToWait) {
|
||||
if (xEventGroupHandle) {
|
||||
return (Notifications) xEventGroupWaitBits(xEventGroupHandle, mask,
|
||||
mask,
|
||||
pdFALSE, ticksToWait);
|
||||
}
|
||||
return (Notifications)0;
|
||||
}
|
||||
97
source/Core/Drivers/FUSB302/protocol_tx.h
Normal file
97
source/Core/Drivers/FUSB302/protocol_tx.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* 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 PDB_PROTOCOL_TX_H
|
||||
#define PDB_PROTOCOL_TX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "policy_engine.h"
|
||||
#include "protocol_rx.h"
|
||||
#include <pd.h>
|
||||
|
||||
/* Events for the Protocol TX thread */
|
||||
|
||||
class ProtocolTransmit {
|
||||
public:
|
||||
static void init();
|
||||
//Push a message to the queue to be sent out the pd comms bus
|
||||
static void pushMessage(union pd_msg *msg);
|
||||
|
||||
enum class Notifications {
|
||||
|
||||
PDB_EVT_PRLTX_RESET = EVENT_MASK(0), //
|
||||
PDB_EVT_PRLTX_I_TXSENT = EVENT_MASK(1), //
|
||||
PDB_EVT_PRLTX_I_RETRYFAIL = EVENT_MASK(2), //
|
||||
PDB_EVT_PRLTX_DISCARD = EVENT_MASK(3), //
|
||||
PDB_EVT_PRLTX_MSG_TX = EVENT_MASK(4), //
|
||||
PDB_EVT_PRLTX_START_AMS = EVENT_MASK(5), //
|
||||
};
|
||||
static void notify(Notifications notification);
|
||||
private:
|
||||
static void thread(const void *args);
|
||||
static EventGroupHandle_t xEventGroupHandle;
|
||||
static StaticEventGroup_t xCreatedEventGroup;
|
||||
static osThreadId TaskHandle;
|
||||
static const size_t TaskStackSize = 1024 / 4;
|
||||
static uint32_t TaskBuffer[TaskStackSize];
|
||||
static osStaticThreadDef_t TaskControlBlock;
|
||||
static bool messageSending;
|
||||
/*
|
||||
* Protocol TX machine states
|
||||
*
|
||||
* Because the PHY can automatically send retries, the Check_RetryCounter state
|
||||
* has been removed, transitions relating to it are modified appropriately, and
|
||||
* we don't even keep a RetryCounter.
|
||||
*/
|
||||
enum protocol_tx_state {
|
||||
PRLTxPHYReset,
|
||||
PRLTxWaitMessage,
|
||||
PRLTxReset,
|
||||
PRLTxConstructMessage,
|
||||
PRLTxWaitResponse,
|
||||
PRLTxMatchMessageID,
|
||||
PRLTxTransmissionError,
|
||||
PRLTxMessageSent,
|
||||
PRLTxDiscardMessage
|
||||
};
|
||||
//Internal states
|
||||
static protocol_tx_state protocol_tx_discard_message();
|
||||
static protocol_tx_state protocol_tx_message_sent();
|
||||
static protocol_tx_state protocol_tx_transmission_error();
|
||||
static protocol_tx_state protocol_tx_match_messageid();
|
||||
static protocol_tx_state protocol_tx_wait_response();
|
||||
static protocol_tx_state protocol_tx_construct_message();
|
||||
static protocol_tx_state protocol_tx_reset();
|
||||
static protocol_tx_state protocol_tx_wait_message();
|
||||
static protocol_tx_state protocol_tx_phy_reset();
|
||||
//queue of up to PDB_MSG_POOL_SIZE messages to send
|
||||
static StaticQueue_t xStaticQueue;
|
||||
/* The array to use as the queue's storage area. This must be at least
|
||||
uxQueueLength * uxItemSize bytes. */
|
||||
static uint8_t ucQueueStorageArea[PDB_MSG_POOL_SIZE * sizeof(union pd_msg)];
|
||||
static QueueHandle_t messagesWaiting;
|
||||
static uint8_t _tx_messageidcounter;
|
||||
static bool messagePending();
|
||||
//Reads a message off the queue into the temp message
|
||||
static void getMessage();
|
||||
static union pd_msg temp_msg;
|
||||
static Notifications waitForEvent(uint32_t mask, TickType_t ticksToWait =
|
||||
portMAX_DELAY);
|
||||
|
||||
};
|
||||
|
||||
#endif /* PDB_PROTOCOL_TX_H */
|
||||
Reference in New Issue
Block a user