USB-C device stack

The USB-C device stack is a hardware independent interface between a Type-C Port Controller (TCPC) and customer applications. It is a port of the Google ChromeOS Type-C Port Manager (TCPM) stack. It provides the following functionalities:

  • Uses the APIs provided by the Type-C Port Controller drivers to interact with the Type-C Port Controller.

  • Provides a programming interface that’s used by a customer applications. The APIs is described in include/zephyr/usb_c/usbc.h

Currently the device stack only support implementation of Sink devices.

List of samples for different purposes.

Implementing a Sink Type-C and Power Delivery USB-C device

The configuration of a USB-C Device is done in the stack layer and devicetree.

The following devicetree, structures and callbacks need to be defined:

  • Devicetree usb-c-connector node referencing a TCPC

  • Devicetree vbus node referencing a VBUS measurment device

  • User defined structure that encapsulates application specific data

  • Policy callbacks

For example, for the Sample USB-C Sink application:

Each Physical Type-C port is represented in the devicetree by a usb-c-connector compatible node:

1		port1: usbc-port@1 {
2			compatible = "usb-c-connector";
3			reg = <1>;
4			tcpc = <&ucpd1>;
5			vbus = <&vbus1>;
6			power-role = "sink";
7			sink-pdos = <PDO_FIXED(5000, 100, 0)>;
8		};

VBUS is measured by a device that’s referenced in the devicetree by a usb-c-vbus-adc compatible node:

1	vbus1: vbus {
2		compatible = "zephyr,usb-c-vbus-adc";
3		io-channels = <&adc2 8>;
4		output-ohms = <49900>;
5		full-ohms = <(330000 + 49900)>;
6	};

A user defined structure is defined and later registered with the subsystem and can be accessed from callback through an API:

 1/**
 2 * @brief A structure that encapsulates Port data.
 3 */
 4static struct port1_data_t {
 5	/** Sink Capabilities */
 6	uint32_t snk_caps[DT_PROP_LEN(DT_NODELABEL(port1), sink_pdos)];
 7	/** Number of Sink Capabilities */
 8	int snk_cap_cnt;
 9	/** Source Capabilities */
10	uint32_t src_caps[PDO_MAX_DATA_OBJECTS];
11	/** Number of Source Capabilities */
12	int src_cap_cnt;
13	/* Power Supply Ready flag */
14	atomic_t ps_ready;
15} port1_data = {
16	.snk_caps = {DT_FOREACH_PROP_ELEM(DT_NODELABEL(port1), sink_pdos, SINK_PDO)},
17	.snk_cap_cnt = DT_PROP_LEN(DT_NODELABEL(port1), sink_pdos),
18	.src_caps = {0},
19	.src_cap_cnt = 0,
20	.ps_ready = 0
21};
22

These callbacks are used by the subsystem to set or get application specific data:

 1static int port1_policy_cb_get_snk_cap(const struct device *dev,
 2					    uint32_t **pdos,
 3					    int *num_pdos)
 4{
 5	struct port1_data_t *dpm_data = usbc_get_dpm_data(dev);
 6
 7	*pdos = dpm_data->snk_caps;
 8	num_pdos = &dpm_data->snk_cap_cnt;
 9
10	return 0;
11}
12
13static void port1_policy_cb_set_src_cap(const struct device *dev,
14					     const uint32_t *pdos,
15					     const int num_pdos)
16{
17	struct port1_data_t *dpm_data;
18	int num;
19	int i;
20
21	dpm_data = usbc_get_dpm_data(dev);
22
23	num = num_pdos;
24	if (num > PDO_MAX_DATA_OBJECTS) {
25		num = PDO_MAX_DATA_OBJECTS;
26	}
27
28	for (i = 0; i < num; i++) {
29		dpm_data->src_caps[i] = *(pdos + i);
30	}
31
32	dpm_data->src_cap_cnt = num;
33}
34
35static uint32_t port1_policy_cb_get_rdo(const struct device *dev)
36{
37	struct port1_data_t *dpm_data = usbc_get_dpm_data(dev);
38
39	return build_rdo(dpm_data);
40}

This callback is used by the subsystem to query if a certain action can be taken:

 1bool port1_policy_check(const struct device *dev,
 2			const enum usbc_policy_check_t policy_check)
 3{
 4	switch (policy_check) {
 5	case CHECK_POWER_ROLE_SWAP:
 6		/* Reject power role swaps */
 7		return false;
 8	case CHECK_DATA_ROLE_SWAP_TO_DFP:
 9		/* Reject data role swap to DFP */
10		return false;
11	case CHECK_DATA_ROLE_SWAP_TO_UFP:
12		/* Accept data role swap to UFP */
13		return true;
14	case CHECK_SNK_AT_DEFAULT_LEVEL:
15		/* This device is always at the default power level */
16		return true;
17	default:
18		/* Reject all other policy checks */
19		return false;
20
21	}
22}

This callback is used by the subsystem to notify the application of an event:

 1static void port1_notify(const struct device *dev,
 2			      const enum usbc_policy_notify_t policy_notify)
 3{
 4	struct port1_data_t *dpm_data = usbc_get_dpm_data(dev);
 5
 6	switch (policy_notify) {
 7	case PROTOCOL_ERROR:
 8		break;
 9	case MSG_DISCARDED:
10		break;
11	case MSG_ACCEPT_RECEIVED:
12		break;
13	case MSG_REJECTED_RECEIVED:
14		break;
15	case MSG_NOT_SUPPORTED_RECEIVED:
16		break;
17	case TRANSITION_PS:
18		atomic_set_bit(&dpm_data->ps_ready, 0);
19		break;
20	case PD_CONNECTED:
21		break;
22	case NOT_PD_CONNECTED:
23		break;
24	case POWER_CHANGE_0A0:
25		LOG_INF("PWR 0A");
26		break;
27	case POWER_CHANGE_DEF:
28		LOG_INF("PWR DEF");
29		break;
30	case POWER_CHANGE_1A5:
31		LOG_INF("PWR 1A5");
32		break;
33	case POWER_CHANGE_3A0:
34		LOG_INF("PWR 3A0");
35		break;
36	case DATA_ROLE_IS_UFP:
37		break;
38	case DATA_ROLE_IS_DFP:
39		break;
40	case PORT_PARTNER_NOT_RESPONSIVE:
41		LOG_INF("Port Partner not PD Capable");
42		break;
43	case SNK_TRANSITION_TO_DEFAULT:
44		break;
45	case HARD_RESET_RECEIVED:
46		break;
47	}
48}

Registering the callbacks:

 1	/* Register USB-C Callbacks */
 2
 3	/* Register Policy Check callback */
 4	usbc_set_policy_cb_check(usbc_port1, port1_policy_check);
 5	/* Register Policy Notify callback */
 6	usbc_set_policy_cb_notify(usbc_port1, port1_notify);
 7	/* Register Policy Get Sink Capabilities callback */
 8	usbc_set_policy_cb_get_snk_cap(usbc_port1, port1_policy_cb_get_snk_cap);
 9	/* Register Policy Set Source Capabilities callback */
10	usbc_set_policy_cb_set_src_cap(usbc_port1, port1_policy_cb_set_src_cap);
11	/* Register Policy Get Request Data Object callback */
12	usbc_set_policy_cb_get_rdo(usbc_port1, port1_policy_cb_get_rdo);

Register the user defined structure:

1	/* Set Application port data object. This object is passed to the policy callbacks */
2	port1_data.ps_ready = ATOMIC_INIT(0);
3	usbc_set_dpm_data(usbc_port1, &port1_data);

Start the USB-C subsystem:

1	/* Start the USB-C Subsystem */
2	usbc_start(usbc_port1);

API reference

group _usbc_device_api

USB-C Device APIs.

Defines

FIXED_5V_100MA_RDO

This Request Data Object (RDO) value can be returned from the policy_cb_get_rdo if 5V@100mA with the following options are sufficient for the Sink to operate.

The RDO is configured as follows: Maximum operating current 100mA Operating current 100mA Unchunked Extended Messages Not Supported No USB Suspend Not USB Communications Capable No capability mismatch Don’t giveback Object position 1 (5V PDO)

Enums

enum usbc_policy_request_t

Device Policy Manager requests.

Values:

enumerator REQUEST_NOP

No request

enumerator REQUEST_TC_DISABLED

Request Type-C layer to transition to Disabled State

enumerator REQUEST_TC_ERROR_RECOVERY

Request Type-C layer to transition to Error Recovery State

enumerator REQUEST_TC_END

End of Type-C requests

enumerator REQUEST_PE_DR_SWAP

Request Policy Engine layer to perform a Data Role Swap

enumerator REQUEST_PE_HARD_RESET_SEND

Request Policy Engine layer to send a hard reset

enumerator REQUEST_PE_SOFT_RESET_SEND

Request Policy Engine layer to send a soft reset

enumerator REQUEST_PE_GET_SRC_CAPS

Request Policy Engine layer to get Source Capabilities from port partner

enum usbc_policy_notify_t

Device Policy Manager notifications.

Values:

enumerator MSG_ACCEPT_RECEIVED

Power Delivery Accept message was received

enumerator MSG_REJECTED_RECEIVED

Power Delivery Reject message was received

enumerator MSG_DISCARDED

Power Delivery discarded the message being transmited

enumerator MSG_NOT_SUPPORTED_RECEIVED

Power Delivery Not Supported message was received

enumerator DATA_ROLE_IS_UFP

Data Role has been set to Upstream Facing Port (UFP)

enumerator DATA_ROLE_IS_DFP

Data Role has been set to Downstream Facing Port (DFP)

enumerator PD_CONNECTED

A PD Explicit Contract is in place

enumerator NOT_PD_CONNECTED

No PD Explicit Contract is in place

enumerator TRANSITION_PS

Transition the Power Supply

enumerator PORT_PARTNER_NOT_RESPONSIVE

Port partner is not responsive

enumerator PROTOCOL_ERROR

Protocol Error occurred

enumerator SNK_TRANSITION_TO_DEFAULT

Transition the Sink to default

enumerator HARD_RESET_RECEIVED

Hard Reset Received

enumerator POWER_CHANGE_0A0

Sink SubPower state at 0V

enumerator POWER_CHANGE_DEF

Sink SubPower state a 5V / 500mA

enumerator POWER_CHANGE_1A5

Sink SubPower state a 5V / 1.5A

enumerator POWER_CHANGE_3A0

Sink SubPower state a 5V / 3A

enum usbc_policy_check_t

Device Policy Manager checks.

Values:

enumerator CHECK_POWER_ROLE_SWAP

Check if Power Role Swap is allowed

enumerator CHECK_DATA_ROLE_SWAP_TO_DFP

Check if Data Role Swap to DFP is allowed

enumerator CHECK_DATA_ROLE_SWAP_TO_UFP

Check if Data Role Swap to UFP is allowed

enumerator CHECK_SNK_AT_DEFAULT_LEVEL

Check if Sink is at default level

enum usbc_policy_wait_t

Device Policy Manager Wait message notifications.

Values:

enumerator WAIT_SINK_REQUEST

The port partner is unable to meet the sink request at this time

enumerator WAIT_POWER_ROLE_SWAP

The port partner is unable to do a Power Role Swap at this time

enumerator WAIT_DATA_ROLE_SWAP

The port partner is unable to do a Data Role Swap at this time

enumerator WAIT_VCONN_SWAP

The port partner is unable to do a VCONN Swap at this time

Functions

int usbc_start(const struct device *dev)

Start the USB-C Subsystem.

Parameters
  • dev – Runtime device structure

Return values

0 – on success

int usbc_suspend(const struct device *dev)

Suspend the USB-C Subsystem.

Parameters
  • dev – Runtime device structure

Return values

0 – on success

int usbc_request(const struct device *dev, const enum usbc_policy_request_t req)

Make a request of the USB-C Subsystem.

Parameters
  • dev – Runtime device structure

  • req – request

Return values

0 – on success

void usbc_set_dpm_data(const struct device *dev, void *dpm_data)

Set pointer to Device Policy Manager (DPM) data.

Parameters
  • dev – Runtime device structure

  • dpm_data – pointer to dpm data

void *usbc_get_dpm_data(const struct device *dev)

Get pointer to Device Policy Manager (DPM) data.

Parameters
  • dev – Runtime device structure

Return values
  • pointer – to dpm data that was set with usbc_set_dpm_data

  • NULL – if dpm data was not set

void usbc_set_vconn_control_cb(const struct device *dev, const tcpc_vconn_control_cb_t cb)

Set the callback used to set VCONN control.

Parameters
  • dev – Runtime device structure

  • cb – VCONN control callback

void usbc_set_policy_cb_check(const struct device *dev, const policy_cb_check_t cb)

Set the callback used to check a policy.

Parameters
  • dev – Runtime device structure

  • cb – callback

void usbc_set_policy_cb_notify(const struct device *dev, const policy_cb_notify_t cb)

Set the callback used to notify Device Policy Manager of a policy change.

Parameters
  • dev – Runtime device structure

  • cb – callback

void usbc_set_policy_cb_wait_notify(const struct device *dev, const policy_cb_wait_notify_t cb)

Set the callback used to notify Device Policy Manager of WAIT message reception.

Parameters
  • dev – Runtime device structure

  • cb – callback

void usbc_set_policy_cb_get_snk_cap(const struct device *dev, const policy_cb_get_snk_cap_t cb)

Set the callback used to get the Sink Capabilities.

Parameters
  • dev – Runtime device structure

  • cb – callback

void usbc_set_policy_cb_set_src_cap(const struct device *dev, const policy_cb_set_src_cap_t cb)

Set the callback used to store the received Port Partner’s Source Capabilities.

Parameters
  • dev – Runtime device structure

  • cb – callback

void usbc_set_policy_cb_get_rdo(const struct device *dev, const policy_cb_get_rdo_t cb)

Set the callback used to get the Request Data Object (RDO)

Parameters
  • dev – Runtime device structure

  • cb – callback

void usbc_set_policy_cb_is_snk_at_default(const struct device *dev, const policy_cb_is_snk_at_default_t cb)

Set the callback used to check if the sink power supply is at the default level.

Parameters
  • dev – Runtime device structure

  • cb – callback