It's pretty easy to implement, it turns out! A new event gets sent to
the protocol TX thread when the PE is starting an AMS. Just before the
message is transmitted, the TX thread checks if that event was received,
and if it was, it waits for the Type-C Current to look like 3.0 A
(SinkTxOk) before proceeding. Now, to the best of my knowledge, we're
actually doing Power Delivery 3.0 as correctly as we ever did Power
Delivery 2.0, which is to say, everything is right except we don't do
BIST.
According to the USB Power Delivery Specification Revision 3.0, Version
1.1, section 7.2.3.1, we're not required to enter Sink Standby when
changing the negotiated voltage on a single PPS APDO. Now we don't.
This is done in the PE, so the DPM doesn't have to lift a finger to
avoid the unnecessary standby.
Now if we make a request for a PPS APDO, the SinkPPSPeriodicTimer is
run, forcing a new request to be made every 10 s. This is important
because without it, the Source Shall perform a Hard Reset 15 s after the
request is made.
This state requires that the DPM be informed of the receipt of a
Not_Supported message. Some DPMs may not need to do this, so the new
dpm.not_supported_received function is optional.
Now when a part of a chunked extended message is received, the
ChunkingNotSupportedTimer is run. When it times out, we transition to
PE_SNK_Send_Not_Supported. Just like the spec says.
In PD 3.0, there's a new control message, Not_Supported, which is sent
unsurprisingly when unsupported messages are received. Basically, every
time we had sent a Reject message in PD 2.0, we're now supposed to send
Not_Supported instead. As of this commit, that's exactly what we do.
Set the Specification Revision field to 2.0 or 3.0
Based on the first message sent from the Source, we now set the
Specification Revision field of our messages to 2.0 or 3.0. We don't do
anything else 3.0 yet though, so it's a bit of lie, but this is a
necessary first step.
The new tempate variable sets the data role, power role, and
specification revision in one fell swoop. It's used by the PE and DPM
alike for creating messages, and in the future it may have a part in
determining if we're doing PD 2.0 or 3.0.
All the internal functions and definitions have been moved out of
lib/include/. Includes are made with the semantically correct brackets
("" or <>) throughout the firmware and library. That is to say, the
library is essentially done at this point, with only documentation
changes left to be made.
This commit splits the old messages.h file into two, one private
(messages.h) and one public (pdb_msg.h). This new arrangement keeps the
old model of one message pool for all the PD Buddy firmware library, but
moves the macro for the number of objects it contains into a new file,
pdb_conf.h, placed at the project root. The pool itself is kept in the
new pdb_msg.h file, since the DPM may need to access it.
The new configuration file also contains the stack sizes for the PD
Buddy threads (some will be moved back to secrecy in the future once the
needed sizes are known, but the PE's stack size should be kept in
pdb_conf.h since user code needs that stack).
Now the user-visible FUSB302B functions all take a struct
pdb_fusb_config * as a parameter. The static functions don't use it
yet, but at least the public API of this code is in its finished state.
The code compiles again, and it seems to work as it did before. There's
still a lot to do (we still have a mess of globals), but the PE
correctly calls the new DPM functions via the list of callbacks in the
configuration object.
/!\ BROKEN BUILD /!\
This commit moves the library code to the library directory. This
causes the firmware to not compile. A few changes were made to reduce
the nubmer of errors, but some errors just aren't going to go away until
I go ahead with the planned changes.
Only set the output in trans.sink when not min pwr
When we receive a GotoMin message, we immediately go to lower power
before entering the PE_SRC_Transition_Sink state. Therefore, the call
to pdb_dpm_output_set was redundant in this case. Now the call isn't
made in this case, removing the redundancy. This also leads somewhat
into the design of the new library.
Now the call to fusb_get_typec_current is made in the policy engine,
then passed to pdb_dpm_evaluate_typec_current. This is more consistent
with pdb_dpm_evaluate_capability, improving the PD Buddy DPM API.
That's right, I'm thinking about API design now for when I split the
code into a library and application.
It was never really necessary to update the source capabilities before
making sending a Request message. It was convenient though, since we
didn't store the previous Source_Capabilities message. Now we do, so
it's possible to make a new Request based on that.
This commit makes the Sink do just that. It doesn't follow the spec's
state diagram for this, but it's a minor change that works better with
this firmware's design, so I don't mind. There's a comment explaining
that too, so future folks won't be confused by it either.
It prints the most recently advertised PDOs in a reasonable format. The
types it doesn't know about are printed as hex so that someone
knowledgeable can still get the information they need. For now, it only
knows about fixed PDOs, so variable and battery PDOs will be shown as
hex.
Since this means we remember the most recent Source_Capabilities, it's
now becoming possible to send a new Request without sending a
Get_Source_Cap message first. That's not actually done yet, but I'd
like to do it that way. I'd also at some point like to make a
lower-current Request when the PD Buddy Sink's output is disabled, but
again, that's still to come.
Update requested power after writing configuration
The PD Buddy Sink now requests new power after configuration is written.
This means that in Setup mode, the voltage and current can be
re-negotiated on the fly. Cool, huh?
It's still impossible to turn the output on and off from the shell. New
commands will allow that soon enough, though. Also, I'm seeing some
weird behavior when switching to/from 5 V (power shuts off entirely
sometimes), but I suspect that's a quirk of the source I'm using (Asus
USB 3.1 UPD Panel) and not the PD Buddy Sink itself. I plan to make or
buy a USB power/data splitter to verify this.
Now we correctly set the GiveBack flag and minimum current fields in the
Request message when GiveBack is enabled. Also when GiveBack is
enabled, we correctly handle GotoMin messages by lowering our power
consumption, completing negotiation, and Requesting our normal power
periodically until the power supply Accepts our Request.
At least, I believe it's correct. I don't have any hardware that sends
GotoMin messages, so I can't test it other than that it doesn't break
anything with the GiveBack flag set to 1 or 0 when the source doesn't
ask to reduce power.
We used to free the Request we made after transmitting it. However, for
GiveBack to work properly, we need to be able to make that request
again. To do it without having to request the Source_Capabilities
again, it makes sense to keep the Request message in memory. We have
more messages in the pool than we need anyway.
The policy engine is supposed to transition to the
PE_SNK_Transition_to_default state from any state when hard reset
signaling is received. While it still doesn't do that exactly, in every
state that already waited for events, the PDB_EVT_PE_RESET event is also
checked, with the appropriate transition being made if the event is
received.
My justification for not checking for the event in other states is that
if there's no wait, the reset will be received soon by another state
anyway. Besides, if there's no event handling already, where exactly
would be best to insert a test for the event?
I think so, anyway. Testing with a heat gun didn't result in the Sink
resetting itself, so I'm not confident that I understand the FUSB302B's
over-temperature sensing mechanism.
I must point out though that I'm not confident that this
over-temperature protection is even useful. The PD Buddy Sink doesn't
get hot on its own, and if it did, it might not even be from the device
it's powering, making a hard reset useless. The spec says that we Shall
implement over temperature protection though, and that we Shall send a
Hard Reset message when it engages, so even though I take issue with the
requirement, I'll try to do what the standard says I Shall.
When there is a positive or negative transition of Vbus, we Shall reduce
our power consumption to no more than 2.5 W beforehand. This can be
easily achieved in all cases by simply turning off the output. This was
implemented, so that's one fewer Shall we aren't doing.
In the process, I made some API changes for the Device Policy Manager.
No more output_on() and output_off(), now it's output_set(true) and
output_set(false), plus a special case output_default() used during a
hard reset. This much better matches how these functions are used by
the policy engine.
Type-C Current is a mechanism defined in the USB Type-C spec for
indicating high-current modes at 5 V using nothing other than a voltage
on the CC line. The FUSB302B maps the voltage to one of the four ranges
we care about, so it's easy to see if 1.5 A or 3 A is available at 5 V
even in systems that don't support PD.
Now, when the PD Buddy Sink is connected to a system without Power
Delivery support, after all attempts at PD communications fail, it falls
back to Type-C Current. If the Sink is configured for 5 V, it will
monitor the CC line's voltage to see if enough Type-C Current is
available. If so, the output is turned on.
After a failed hard reset, we keep trying to negotiate power. Before,
the LED was on steady indicating negotiation failure during these
continued negotiations. Now it keeps blinking, indicating what's
actually going on.
It's simpler than I realized. All that's left to do here is the
fallback mechanism for when the Sink is configured for 5 V. This means
that yet again, a TODO was replaced with another, less critical TODO.
In the PE_SNK_Select_Capability state, we Shall do some transitions if
we receive a Wait or Reject message. Now we do. This creates another
TODO (running SinkRequestTimer in the PE_SNK_Ready state), but this
one's only for a Should, not a Shall.
From the USB PD spec, Revision 2.0, S. 6.4.4.1:
* A DFP or UFP which does not support Unstructured VDMs Shall Ignore
any Unstructured VDMs received.
From S. 6.4.4.2:
* A DFP or UFP which does not support Structured VDMs Shall Ignore any
Structured VDMs received.
Therefore, as long as we do not support any VDMs, we Shall Ignore them,
which is exactly what we're doing. We're not doing anything wrong, so I
removed the TODO comment that implied we were doing something wrong.
There are still a few things that the standard says we Shall do and we
don't, but it Works For Me™. I haven't implemented anything with
regards to GiveBack support, but that doesn't matter just yet. Our
handling of VDMs isn't quite right either. Anyway, it successfully
negotiates with so-called Split PDO power supplies, which is more than I
can say about some commercial products.