Browse Source

Wrote a mostly-complete USB PD 2.0 implementation

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.
Clara Hobbs 7 years ago
parent
commit
2fe933c846
22 changed files with 2713 additions and 43 deletions
  1. 1
    1
      Makefile
  2. 124
    0
      src/device_policy_manager.c
  3. 50
    0
      src/device_policy_manager.h
  4. 205
    0
      src/fusb302b.c
  5. 288
    0
      src/fusb302b.h
  6. 166
    0
      src/hard_reset.c
  7. 40
    0
      src/hard_reset.h
  8. 82
    0
      src/int_n.c
  9. 29
    0
      src/int_n.h
  10. 116
    0
      src/led.c
  11. 41
    0
      src/led.h
  12. 85
    42
      src/main.c
  13. 36
    0
      src/messages.c
  14. 60
    0
      src/messages.h
  15. 219
    0
      src/pd.h
  16. 523
    0
      src/policy_engine.c
  17. 44
    0
      src/policy_engine.h
  18. 33
    0
      src/priorities.h
  19. 190
    0
      src/protocol_rx.c
  20. 43
    0
      src/protocol_rx.h
  21. 289
    0
      src/protocol_tx.c
  22. 49
    0
      src/protocol_tx.h

+ 1
- 1
Makefile View File

@@ -10,7 +10,7 @@ endif
10 10
 
11 11
 # C specific options here (added to USE_OPT).
12 12
 ifeq ($(USE_COPT),)
13
-  USE_COPT = 
13
+  USE_COPT = -std=c11
14 14
 endif
15 15
 
16 16
 # C++ specific options here (added to USE_OPT).

+ 124
- 0
src/device_policy_manager.c View File

@@ -0,0 +1,124 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "device_policy_manager.h"
20
+
21
+#include <stdint.h>
22
+
23
+#include <hal.h>
24
+
25
+#include "led.h"
26
+#include "pd.h"
27
+
28
+
29
+/* The voltage and current we want */
30
+/* XXX In the future, these won't be *quite* so convenient to access */
31
+static uint16_t dpm_desired_v = 400; /* 20 V */
32
+static uint16_t dpm_desired_i = 225; /* 2.25 A */
33
+
34
+/* Whether or not the power supply is unconstrained */
35
+static uint8_t dpm_unconstrained_power;
36
+
37
+bool pdb_dpm_evaluate_capability(const union pd_msg *capabilities, union pd_msg *request)
38
+{
39
+    /* Get the number of PDOs */
40
+    uint8_t numobj = PD_NUMOBJ_GET(capabilities);
41
+
42
+    /* Make the LED blink to indicate ongoing power negotiations */
43
+    chEvtSignal(pdb_led_thread, PDB_EVT_LED_FAST_BLINK);
44
+
45
+    /* Get whether or not the power supply is constrained */
46
+    if (capabilities->obj[0] & PD_PDO_SRC_FIXED_UNCONSTRAINED) {
47
+        dpm_unconstrained_power = 1;
48
+    } else {
49
+        dpm_unconstrained_power = 0;
50
+    }
51
+
52
+    /* Look at the PDOs to see if one matches our desires */
53
+    for (uint8_t i = 0; i < numobj; i++) {
54
+        /* Fixed Supply PDOs come first, so when we see a PDO that isn't a
55
+         * Fixed Supply, stop reading. */
56
+        if ((capabilities->obj[i] & PD_PDO_TYPE) != PD_PDO_TYPE_FIXED) {
57
+            break;
58
+        }
59
+        /* If the V from the PDO equals our desired V and the I is at least our
60
+         * desired I */
61
+        if (PD_PDO_SRC_FIXED_VOLTAGE_GET(capabilities, i) == dpm_desired_v
62
+                && PD_PDO_SRC_FIXED_CURRENT_GET(capabilities, i) >= dpm_desired_i) {
63
+            /* We got what we wanted, so build a request for that */
64
+            request->hdr = PD_MSGTYPE_REQUEST | PD_DATAROLE_UFP |
65
+                PD_SPECREV_2_0 | PD_POWERROLE_SINK | PD_NUMOBJ(1);
66
+            request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(dpm_desired_i)
67
+                | PD_RDO_FV_CURRENT_SET(dpm_desired_i)
68
+                | PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(i + 1);
69
+            return true;
70
+        }
71
+    }
72
+    /* Nothing matched, so get 5 V */
73
+    request->hdr = PD_MSGTYPE_REQUEST | PD_DATAROLE_UFP |
74
+        PD_SPECREV_2_0 | PD_POWERROLE_SINK | PD_NUMOBJ(1);
75
+    request->obj[0] = PD_RDO_FV_MAX_CURRENT_SET(10)
76
+        | PD_RDO_FV_CURRENT_SET(10)
77
+        | PD_RDO_NO_USB_SUSPEND | PD_RDO_CAP_MISMATCH
78
+        | PD_RDO_OBJPOS_SET(1);
79
+    return false;
80
+}
81
+
82
+void pdb_dpm_get_sink_capability(union pd_msg *cap)
83
+{
84
+    /* If we want 5 V, we need to send only one PDO */
85
+    if (dpm_desired_v == 100) {
86
+        /* Sink_Capabilities message */
87
+        cap->hdr = PD_MSGTYPE_SINK_CAPABILITIES | PD_DATAROLE_UFP
88
+            | PD_SPECREV_2_0 | PD_POWERROLE_SINK | PD_NUMOBJ(1);
89
+        /* vSafe5V at the desired current. */
90
+        cap->obj[0] = PD_PDO_TYPE_FIXED
91
+            | PD_PDO_SNK_FIXED_VOLTAGE_SET(dpm_desired_v)
92
+            | PD_PDO_SNK_FIXED_CURRENT_SET(dpm_desired_i);
93
+    /* Otherwise, send two PDOs, one for 5 V and one for the desired power. */
94
+    } else {
95
+        /* Sink_Capabilities message */
96
+        cap->hdr = PD_MSGTYPE_SINK_CAPABILITIES | PD_DATAROLE_UFP
97
+            | PD_SPECREV_2_0 | PD_POWERROLE_SINK | PD_NUMOBJ(2);
98
+        /* First, vSafe5V.  100 mA, 5 V, and higher capability. */
99
+        cap->obj[0] = PD_PDO_TYPE_FIXED
100
+            | PD_PDO_SNK_FIXED_VOLTAGE_SET(100)
101
+            | PD_PDO_SNK_FIXED_CURRENT_SET(10);
102
+        /* Next, desired_v and desired_i */
103
+        cap->obj[1] = PD_PDO_TYPE_FIXED
104
+            | PD_PDO_SNK_FIXED_VOLTAGE_SET(dpm_desired_v)
105
+            | PD_PDO_SNK_FIXED_CURRENT_SET(dpm_desired_i);
106
+    }
107
+
108
+    /* Set the unconstrained power flag. */
109
+    if (dpm_unconstrained_power) {
110
+        cap->obj[0] |= PD_PDO_SNK_FIXED_UNCONSTRAINED;
111
+    }
112
+}
113
+
114
+void pdb_dpm_output_on(void)
115
+{
116
+    chEvtSignal(pdb_led_thread, PDB_EVT_LED_MEDIUM_BLINK_OFF);
117
+    palSetLine(LINE_OUT_CTRL);
118
+}
119
+
120
+void pdb_dpm_output_off(void)
121
+{
122
+    chEvtSignal(pdb_led_thread, PDB_EVT_LED_ON);
123
+    palClearLine(LINE_OUT_CTRL);
124
+}

+ 50
- 0
src/device_policy_manager.h View File

@@ -0,0 +1,50 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_DEVICE_POLICY_MANAGER_H
20
+#define PDB_DEVICE_POLICY_MANAGER_H
21
+
22
+#include <stdbool.h>
23
+
24
+#include "messages.h"
25
+
26
+
27
+/*
28
+ * Create a Request message based on the given Source_Capabilities message.
29
+ *
30
+ * Returns false if sufficient power is not available, true if it is.
31
+ */
32
+bool pdb_dpm_evaluate_capability(const union pd_msg *capabilities, union pd_msg *request);
33
+
34
+/*
35
+ * Create a Sink_Capabilities message for our current capabilities.
36
+ */
37
+void pdb_dpm_get_sink_capability(union pd_msg *cap);
38
+
39
+/*
40
+ * Turn on the power output, with LED indication.
41
+ */
42
+void pdb_dpm_output_on(void);
43
+
44
+/*
45
+ * Turn off the power output, with LED indication.
46
+ */
47
+void pdb_dpm_output_off(void);
48
+
49
+
50
+#endif /* PDB_DEVICE_POLICY_MANAGER_H */

+ 205
- 0
src/fusb302b.c View File

@@ -0,0 +1,205 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "fusb302b.h"
20
+
21
+#include <ch.h>
22
+#include <hal.h>
23
+
24
+#include "pd.h"
25
+
26
+
27
+/*
28
+ * Read a single byte from the FUSB302B
29
+ */
30
+static uint8_t fusb_read_byte(uint8_t addr)
31
+{
32
+    uint8_t buf;
33
+    i2cMasterTransmit(&I2CD2, FUSB302B_ADDR, &addr, 1, &buf, 1);
34
+    return buf;
35
+}
36
+
37
+/*
38
+ * Read multiple bytes from the FUSB302B
39
+ */
40
+static void fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf)
41
+{
42
+    i2cMasterTransmit(&I2CD2, FUSB302B_ADDR, &addr, 1, buf, size);
43
+}
44
+
45
+/*
46
+ * Write a single byte to the FUSB302B
47
+ */
48
+static void fusb_write_byte(uint8_t addr, uint8_t byte)
49
+{
50
+    uint8_t buf[2] = {addr, byte};
51
+    i2cMasterTransmit(&I2CD2, FUSB302B_ADDR, buf, 2, NULL, 0);
52
+}
53
+
54
+/*
55
+ * Write multiple bytes to the FUSB302B
56
+ */
57
+static void fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf)
58
+{
59
+    uint8_t txbuf[size + 1];
60
+
61
+    /* Prepare the transmit buffer */
62
+    txbuf[0] = addr;
63
+    for (int i = 0; i < size; i++) {
64
+        txbuf[i + 1] = buf[i];
65
+    }
66
+
67
+    i2cMasterTransmit(&I2CD2, FUSB302B_ADDR, txbuf, size + 1, NULL, 0);
68
+}
69
+
70
+void fusb_send_message(const union pd_msg *msg)
71
+{
72
+    /* Token sequences for the FUSB302B */
73
+    static uint8_t sop_seq[5] = {0x12, 0x12, 0x12, 0x13, 0x80};
74
+    static uint8_t eop_seq[4] = {0xFF, 0x14, 0xFE, 0xA1};
75
+
76
+    /* Take the I2C2 mutex now so there can't be a race condition on sop_seq */
77
+    i2cAcquireBus(&I2CD2);
78
+
79
+    /* Get the length of the message: a two-octet header plus NUMOBJ four-octet
80
+     * data objects */
81
+    uint8_t msg_len = 2 + 4 * PD_NUMOBJ_GET(msg);
82
+
83
+    /* Set the number of bytes to be transmitted in the packet */
84
+    sop_seq[4] = 0x80 | msg_len;
85
+
86
+    /* Write all three parts of the message to the TX FIFO */
87
+    fusb_write_buf(FUSB_FIFOS, 5, sop_seq);
88
+    fusb_write_buf(FUSB_FIFOS, msg_len, msg->bytes);
89
+    fusb_write_buf(FUSB_FIFOS, 4, eop_seq);
90
+
91
+    i2cReleaseBus(&I2CD2);
92
+}
93
+
94
+uint8_t fusb_read_message(union pd_msg *msg)
95
+{
96
+    uint8_t garbage[4];
97
+    uint8_t numobj;
98
+
99
+    i2cAcquireBus(&I2CD2);
100
+
101
+    /* If this isn't an SOP message, return error.
102
+     * Because of our configuration, we should be able to assume this means the
103
+     * buffer is empty, and not try to read past a non-SOP message. */
104
+    if ((fusb_read_byte(FUSB_FIFOS) & FUSB_FIFO_RX_TOKEN_BITS)
105
+            != FUSB_FIFO_RX_SOP) {
106
+        i2cReleaseBus(&I2CD2);
107
+        return 1;
108
+    }
109
+    /* Read the message header into msg */
110
+    fusb_read_buf(FUSB_FIFOS, 2, msg->bytes);
111
+    /* Get the number of data objects */
112
+    numobj = PD_NUMOBJ_GET(msg);
113
+    /* If there is at least one data object, read the data objects */
114
+    if (numobj > 0) {
115
+        fusb_read_buf(FUSB_FIFOS, numobj * 4, msg->bytes + 2);
116
+    }
117
+    /* Throw the CRC32 in the garbage, since the PHY already checked it. */
118
+    fusb_read_buf(FUSB_FIFOS, 4, garbage);
119
+
120
+    i2cReleaseBus(&I2CD2);
121
+    return 0;
122
+}
123
+
124
+void fusb_send_hardrst(void)
125
+{
126
+    i2cAcquireBus(&I2CD2);
127
+
128
+    /* Send a hard reset */
129
+    fusb_write_byte(FUSB_CONTROL3, 0x07 | FUSB_CONTROL3_SEND_HARD_RESET);
130
+
131
+    i2cReleaseBus(&I2CD2);
132
+}
133
+
134
+void fusb_setup(void)
135
+{
136
+    i2cAcquireBus(&I2CD2);
137
+
138
+    /* Fully reset the FUSB302B */
139
+    fusb_write_byte(FUSB_RESET, FUSB_RESET_SW_RES);
140
+
141
+    /* Turn on all power */
142
+    fusb_write_byte(FUSB_POWER, 0x0F);
143
+
144
+    /* Set interrupt masks */
145
+    fusb_write_byte(FUSB_MASK1, 0x00);
146
+    fusb_write_byte(FUSB_MASKA, 0x00);
147
+    fusb_write_byte(FUSB_MASKB, 0x00);
148
+    fusb_write_byte(FUSB_CONTROL0, 0x04);
149
+
150
+    /* Enable automatic retransmission */
151
+    fusb_write_byte(FUSB_CONTROL3, 0x07);
152
+
153
+    /* Flush the RX buffer */
154
+    fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
155
+
156
+    /* Measure CC1 */
157
+    fusb_write_byte(FUSB_SWITCHES0, 0x07);
158
+    chThdSleepMicroseconds(250);
159
+    uint8_t cc1;
160
+    cc1 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
161
+
162
+    /* Measure CC2 */
163
+    fusb_write_byte(FUSB_SWITCHES0, 0x0B);
164
+    chThdSleepMicroseconds(250);
165
+    uint8_t cc2;
166
+    cc2 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
167
+
168
+    /* Select the correct CC line for BMC signaling; also enable AUTO_CRC */
169
+    if (cc1 > cc2) {
170
+        fusb_write_byte(FUSB_SWITCHES1, 0x25);
171
+        fusb_write_byte(FUSB_SWITCHES0, 0x07);
172
+    } else {
173
+        fusb_write_byte(FUSB_SWITCHES1, 0x26);
174
+        fusb_write_byte(FUSB_SWITCHES0, 0x0B);
175
+    }
176
+
177
+    /* Reset the PD logic */
178
+    fusb_write_byte(FUSB_RESET, FUSB_RESET_PD_RESET);
179
+
180
+    i2cReleaseBus(&I2CD2);
181
+}
182
+
183
+void fusb_get_status(union fusb_status *status)
184
+{
185
+    i2cAcquireBus(&I2CD2);
186
+
187
+    /* Read the interrupt and status flags into status */
188
+    fusb_read_buf(FUSB_STATUS0A, 7, status->bytes);
189
+
190
+    i2cReleaseBus(&I2CD2);
191
+}
192
+
193
+void fusb_reset(void)
194
+{
195
+    i2cAcquireBus(&I2CD2);
196
+
197
+    /* Flush the TX buffer */
198
+    fusb_write_byte(FUSB_CONTROL0, 0x44);
199
+    /* Flush the RX buffer */
200
+    fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
201
+    /* Reset the PD logic */
202
+    fusb_write_byte(FUSB_RESET, FUSB_RESET_PD_RESET);
203
+
204
+    i2cReleaseBus(&I2CD2);
205
+}

+ 288
- 0
src/fusb302b.h View File

@@ -0,0 +1,288 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_FUSB302B_H
20
+#define PDB_FUSB302B_H
21
+
22
+#include <stdint.h>
23
+
24
+#include "messages.h"
25
+
26
+
27
+/* I2C address of the FUSB302B */
28
+#define FUSB302B_ADDR 0x22
29
+
30
+/* Device ID register */
31
+#define FUSB_DEVICE_ID 0x01
32
+#define FUSB_DEVICE_ID_VERSION_ID_SHIFT 4
33
+#define FUSB_DEVICE_ID_VERSION_ID (0xF << FUSB_DEVICE_ID_VERSION_ID_SHIFT)
34
+#define FUSB_DEVICE_ID_PRODUCT_ID_SHIFT 2
35
+#define FUSB_DEVICE_ID_PRODUCT_ID (0x3 << FUSB_DEVICE_ID_PRODUCT_ID_SHIFT)
36
+#define FUSB_DEVICE_ID_REVISION_ID_SHIFT 0
37
+#define FUSB_DEVICE_ID_REVISION_ID (0x3 << FUSB_DEVICE_ID_REVISION_ID_SHIFT)
38
+
39
+/* Switches0 register */
40
+#define FUSB_SWITCHES0 0x02
41
+#define FUSB_SWITCHES0_PU_EN2 (1 << 7)
42
+#define FUSB_SWITCHES0_PU_EN1 (1 << 6)
43
+#define FUSB_SWITCHES0_VCONN_CC2 (1 << 5)
44
+#define FUSB_SWITCHES0_VCONN_CC1 (1 << 4)
45
+#define FUSB_SWITCHES0_MEAS_CC2 (1 << 3)
46
+#define FUSB_SWITCHES0_MEAS_CC1 (1 << 2)
47
+#define FUSB_SWITCHES0_PDWN_2 (1 << 1)
48
+#define FUSB_SWITCHES0_PDWN_1 1
49
+
50
+/* Switches1 register */
51
+#define FUSB_SWITCHES1 0x03
52
+#define FUSB_SWITCHES1_POWERROLE (1 << 7)
53
+#define FUSB_SWITCHES1_SPECREV_SHIFT 5
54
+#define FUSB_SWITCHES1_SPECREV (0x3 << FUSB_SWITCHES1_SPECREV_SHIFT)
55
+#define FUSB_SWITCHES1_DATAROLE (1 << 4)
56
+#define FUSB_SWITCHES1_AUTO_CRC (1 << 2)
57
+#define FUSB_SWITCHES1_TXCC2 (1 << 1)
58
+#define FUSB_SWITCHES1_TXCC1 1
59
+
60
+/* Measure register */
61
+#define FUSB_MEASURE 0x04
62
+#define FUSB_MEASURE_MEAS_VBUS (1 << 6)
63
+#define FUSB_MEASURE_MDAC_SHIFT 0
64
+#define FUSB_MEASURE_MDAC (0x3F << FUSB_MEASURE_MDAC_SHIFT)
65
+
66
+/* Slice register */
67
+#define FUSB_SLICE 0x05
68
+#define FUSB_SLICE_SDAC_HYS_SHIFT 6
69
+#define FUSB_SLICE_SDAC_HYS (0x3 << FUSB_SLICE_SDAC_HYS_SHIFT)
70
+#define FUSB_SLICE_SDAC_SHIFT 0
71
+#define FUSB_SLICE_SDAC (0x3F << FUSB_SLICE_SDAC_SHIFT)
72
+
73
+/* Control0 register */
74
+#define FUSB_CONTROL0 0x06
75
+#define FUSB_CONTROL0_TX_FLUSH (1 << 6)
76
+#define FUSB_CONTROL0_INT_MASK (1 << 5)
77
+#define FUSB_CONTROL0_HOST_CUR_SHIFT 2
78
+#define FUSB_CONTROL0_HOST_CUR (0x3 << FUSB_CONTROL0_HOST_CUR_SHIFT)
79
+#define FUSB_CONTROL0_AUTO_PRE (1 << 1)
80
+#define FUSB_CONTROL0_TX_START 1
81
+
82
+/* Control1 register */
83
+#define FUSB_CONTROL1 0x07
84
+#define FUSB_CONTROL1_ENSOP2DB (1 << 6)
85
+#define FUSB_CONTROL1_ENSOP1DB (1 << 5)
86
+#define FUSB_CONTROL1_BIST_MODE2 (1 << 4)
87
+#define FUSB_CONTROL1_RX_FLUSH (1 << 2)
88
+#define FUSB_CONTROL1_ENSOP2 (1 << 1)
89
+#define FUSB_CONTROL1_ENSOP1 1
90
+
91
+/* Control2 register */
92
+#define FUSB_CONTROL2 0x08
93
+#define FUSB_CONTROL2_TOG_SAVE_PWR_SHIFT 6
94
+#define FUSB_CONTROL2_TOG_SAVE_PWR (0x3 << FUSB_CONTROL2_TOG_SAVE_PWR)
95
+#define FUSB_CONTROL2_TOG_RD_ONLY (1 << 5)
96
+#define FUSB_CONTROL2_WAKE_EN (1 << 3)
97
+#define FUSB_CONTROL2_MODE_SHIFT 1
98
+#define FUSB_CONTROL2_MODE (0x3 << FUSB_CONTROL2_MODE_SHIFT)
99
+#define FUSB_CONTROL2_TOGGLE 1
100
+
101
+/* Control3 register */
102
+#define FUSB_CONTROL3 0x09
103
+#define FUSB_CONTROL3_SEND_HARD_RESET (1 << 6)
104
+#define FUSB_CONTROL3_BIST_TMODE (1 << 5)
105
+#define FUSB_CONTROL3_AUTO_HARDRESET (1 << 4)
106
+#define FUSB_CONTROL3_AUTO_SOFTRESET (1 << 3)
107
+#define FUSB_CONTROL3_N_RETRIES_SHIFT 1
108
+#define FUSB_CONTROL3_N_RETRIES (0x3 << FUSB_CONTROL3_N_RETRIES_SHIFT)
109
+#define FUSB_CONTROL3_AUTO_RETRY 1
110
+
111
+/* Mask1 register */
112
+#define FUSB_MASK1 0x0A
113
+#define FUSB_MASK1_M_VBUSOK (1 << 7)
114
+#define FUSB_MASK1_M_ACTIVITY (1 << 6)
115
+#define FUSB_MASK1_M_COMP_CHNG (1 << 5)
116
+#define FUSB_MASK1_M_CRC_CHK (1 << 4)
117
+#define FUSB_MASK1_M_ALERT (1 << 3)
118
+#define FUSB_MASK1_M_WAKE (1 << 2)
119
+#define FUSB_MASK1_M_COLLISION (1 << 1)
120
+#define FUSB_MASK1_M_BC_LVL (1 << 0)
121
+
122
+/* Power register */
123
+#define FUSB_POWER 0x0B
124
+#define FUSB_POWER_PWR3 (1 << 3)
125
+#define FUSB_POWER_PWR2 (1 << 2)
126
+#define FUSB_POWER_PWR1 (1 << 1)
127
+#define FUSB_POWER_PWR0 1
128
+
129
+/* Reset register */
130
+#define FUSB_RESET 0x0C
131
+#define FUSB_RESET_PD_RESET (1 << 1)
132
+#define FUSB_RESET_SW_RES 1
133
+
134
+/* OCPreg register */
135
+#define FUSB_OCPREG 0x0D
136
+#define FUSB_OCPREG_OCP_RANGE (1 << 3)
137
+#define FUSB_OCPREG_OCP_CUR_SHIFT 0
138
+#define FUSB_OCPREG_OCP_CUR (0x7 << FUSB_OCPREG_OCP_CUR_SHIFT)
139
+
140
+/* Maska register */
141
+#define FUSB_MASKA 0x0E
142
+#define FUSB_MASKA_M_OCP_TEMP (1 << 7)
143
+#define FUSB_MASKA_M_TOGDONE (1 << 6)
144
+#define FUSB_MASKA_M_SOFTFAIL (1 << 5)
145
+#define FUSB_MASKA_M_RETRYFAIL (1 << 4)
146
+#define FUSB_MASKA_M_HARDSENT (1 << 3)
147
+#define FUSB_MASKA_M_TXSENT (1 << 2)
148
+#define FUSB_MASKA_M_SOFTRST (1 << 1)
149
+#define FUSB_MASKA_M_HARDRST 1
150
+
151
+/* Maskb register */
152
+#define FUSB_MASKB 0x0F
153
+#define FUSB_MASKB_M_GCRCSENT 1
154
+
155
+/* Control4 register */
156
+#define FUSB_CONTROL4 0x10
157
+#define FUSB_CONTROL4_TOG_EXIT_AUD 1
158
+
159
+/* Status0a register */
160
+#define FUSB_STATUS0A 0x3C
161
+#define FUSB_STATUS0A_SOFTFAIL (1 << 5)
162
+#define FUSB_STATUS0A_RETRYFAIL (1 << 4)
163
+#define FUSB_STATUS0A_POWER3 (1 << 3)
164
+#define FUSB_STATUS0A_POWER2 (1 << 2)
165
+#define FUSB_STATUS0A_SOFTRST (1 << 1)
166
+#define FUSB_STATUS0A_HARDRST 1
167
+
168
+/* Status1a register */
169
+#define FUSB_STATUS1A 0x3D
170
+#define FUSB_STATUS1A_TOGSS_SHIFT 3
171
+#define FUSB_STATUS1A_TOGSS (0x7 << FUSB_STATUS1A_TOGSS_SHIFT)
172
+#define FUSB_STATUS1A_RXSOP2DB (1 << 2)
173
+#define FUSB_STATUS1A_RXSOP1DB (1 << 1)
174
+#define FUSB_STATUS1A_RXSOP 1
175
+
176
+/* Interrupta register */
177
+#define FUSB_INTERRUPTA 0x3E
178
+#define FUSB_INTERRUPTA_I_OCP_TEMP (1 << 7)
179
+#define FUSB_INTERRUPTA_I_TOGDONE (1 << 6)
180
+#define FUSB_INTERRUPTA_I_SOFTFAIL (1 << 5)
181
+#define FUSB_INTERRUPTA_I_RETRYFAIL (1 << 4)
182
+#define FUSB_INTERRUPTA_I_HARDSENT (1 << 3)
183
+#define FUSB_INTERRUPTA_I_TXSENT (1 << 2)
184
+#define FUSB_INTERRUPTA_I_SOFTRST (1 << 1)
185
+#define FUSB_INTERRUPTA_I_HARDRST 1
186
+
187
+/* Interruptb register */
188
+#define FUSB_INTERRUPTB 0x3F
189
+#define FUSB_INTERRUPTB_I_GCRCSENT 1
190
+
191
+/* Status0 register */
192
+#define FUSB_STATUS0 0x40
193
+#define FUSB_STATUS0_VBUSOK (1 << 7)
194
+#define FUSB_STATUS0_ACTIVITY (1 << 6)
195
+#define FUSB_STATUS0_COMP (1 << 5)
196
+#define FUSB_STATUS0_CRC_CHK (1 << 4)
197
+#define FUSB_STATUS0_ALERT (1 << 3)
198
+#define FUSB_STATUS0_WAKE (1 << 2)
199
+#define FUSB_STATUS0_BC_LVL_SHIFT 0
200
+#define FUSB_STATUS0_BC_LVL (0x3 << FUSB_STATUS0_BC_LVL_SHIFT)
201
+
202
+/* Status1 register */
203
+#define FUSB_STATUS1 0x41
204
+#define FUSB_STATUS1_RXSOP2 (1 << 7)
205
+#define FUSB_STATUS1_RXSOP1 (1 << 6)
206
+#define FUSB_STATUS1_RX_EMPTY (1 << 5)
207
+#define FUSB_STATUS1_RX_FULL (1 << 4)
208
+#define FUSB_STATUS1_TX_EMPTY (1 << 3)
209
+#define FUSB_STATUS1_TX_FULL (1 << 2)
210
+#define FUSB_STATUS1_OVRTEMP (1 << 1)
211
+#define FUSB_STATUS1_OCP 1
212
+
213
+/* Interrupt register */
214
+#define FUSB_INTERRUPT 0x42
215
+#define FUSB_INTERRUPT_I_VBUSOK (1 << 7)
216
+#define FUSB_INTERRUPT_I_ACTIVITY (1 << 6)
217
+#define FUSB_INTERRUPT_I_COMP_CHNG (1 << 5)
218
+#define FUSB_INTERRUPT_I_CRC_CHK (1 << 4)
219
+#define FUSB_INTERRUPT_I_ALERT (1 << 3)
220
+#define FUSB_INTERRUPT_I_WAKE (1 << 2)
221
+#define FUSB_INTERRUPT_I_COLLISION (1 << 1)
222
+#define FUSB_INTERRUPT_I_BC_LVL 1
223
+
224
+/* FIFOs register */
225
+#define FUSB_FIFOS 0x43
226
+
227
+#define FUSB_FIFO_RX_TOKEN_BITS 0xE0
228
+#define FUSB_FIFO_RX_SOP 0xE0
229
+#define FUSB_FIFO_RX_SOP1 0xC0
230
+#define FUSB_FIFO_RX_SOP2 0xA0
231
+#define FUSB_FIFO_RX_SOP1DB 0x80
232
+#define FUSB_FIFO_RX_SOP2DB 0x60
233
+
234
+
235
+/*
236
+ * FUSB status union
237
+ *
238
+ * Provides a nicer structure than just an array of uint8_t for working with
239
+ * the FUSB302B status and interrupt flags.
240
+ */
241
+union fusb_status {
242
+    uint8_t bytes[7];
243
+    struct {
244
+        uint8_t status0a;
245
+        uint8_t status1a;
246
+        uint8_t interrupta;
247
+        uint8_t interruptb;
248
+        uint8_t status0;
249
+        uint8_t status1;
250
+        uint8_t interrupt;
251
+    };
252
+};
253
+
254
+
255
+/* FUSB functions */
256
+
257
+/*
258
+ * Send a USB Power Delivery message to the FUSB302B
259
+ */
260
+void fusb_send_message(const union pd_msg *msg);
261
+
262
+/*
263
+ * Read a USB Power Delivery message from the FUSB302B
264
+ */
265
+uint8_t fusb_read_message(union pd_msg *msg);
266
+
267
+/*
268
+ * Tell the FUSB302B to send a hard reset signal
269
+ */
270
+void fusb_send_hardrst(void);
271
+
272
+/*
273
+ * Read the FUSB302B status and interrupt flags into *status
274
+ */
275
+void fusb_get_status(union fusb_status *status);
276
+
277
+/*
278
+ * Initialization routine for the FUSB302B
279
+ */
280
+void fusb_setup(void);
281
+
282
+/*
283
+ * Reset the FUSB302B
284
+ */
285
+void fusb_reset(void);
286
+
287
+
288
+#endif /* PDB_FUSB302B_H */

+ 166
- 0
src/hard_reset.c View File

@@ -0,0 +1,166 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "hard_reset.h"
20
+
21
+#include "priorities.h"
22
+#include "policy_engine.h"
23
+#include "protocol_rx.h"
24
+#include "protocol_tx.h"
25
+#include "fusb302b.h"
26
+#include "pd.h"
27
+
28
+
29
+thread_t *pdb_hardrst_thread;
30
+
31
+/*
32
+ * Hard Reset machine states
33
+ */
34
+enum hardrst_state {
35
+    PRLHRResetLayer,
36
+    PRLHRIndicateHardReset,
37
+    PRLHRRequestHardReset,
38
+    PRLHRWaitPHY,
39
+    PRLHRHardResetRequested,
40
+    PRLHRWaitPE,
41
+    PRLHRComplete
42
+};
43
+
44
+/*
45
+ * PRL_HR_Reset_Layer state
46
+ */
47
+static enum hardrst_state hardrst_reset_layer(void)
48
+{
49
+    /* First, wait for the signal to run a hard reset. */
50
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_HARDRST_RESET
51
+            | PDB_EVT_HARDRST_I_HARDRST);
52
+
53
+    /* Reset the stored message IDs */
54
+    pdb_prlrx_messageid = 0;
55
+    pdb_prltx_messageidcounter = 0;
56
+
57
+    /* Reset the Protocol RX machine */
58
+    chEvtSignal(pdb_prlrx_thread, PDB_EVT_PRLRX_RESET);
59
+    chThdYield();
60
+
61
+    /* Reset the Protocol TX machine */
62
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_RESET);
63
+    chThdYield();
64
+
65
+    /* Continue the process based on what event started the reset. */
66
+    if (evt & PDB_EVT_HARDRST_RESET) {
67
+        /* Policy Engine started the reset. */
68
+        return PRLHRRequestHardReset;
69
+    } else {
70
+        /* PHY started the reset */
71
+        return PRLHRIndicateHardReset;
72
+    }
73
+}
74
+
75
+static enum hardrst_state hardrst_indicate_hard_reset(void)
76
+{
77
+    /* Tell the PE that we're doing a hard reset */
78
+    chEvtSignal(pdb_pe_thread, PDB_EVT_PE_RESET);
79
+
80
+    return PRLHRWaitPE;
81
+}
82
+
83
+static enum hardrst_state hardrst_request_hard_reset(void)
84
+{
85
+    /* Tell the PHY to send a hard reset */
86
+    fusb_send_hardrst();
87
+
88
+    return PRLHRWaitPHY;
89
+}
90
+
91
+static enum hardrst_state hardrst_wait_phy(void)
92
+{
93
+    /* Wait for the PHY to tell us that it's done sending the hard reset */
94
+    chEvtWaitAnyTimeout(PDB_EVT_HARDRST_I_HARDSENT, PD_T_HARD_RESET_COMPLETE);
95
+
96
+    /* Move on no matter what made us stop waiting. */
97
+    return PRLHRHardResetRequested;
98
+}
99
+
100
+static enum hardrst_state hardrst_hard_reset_requested(void)
101
+{
102
+    /* Tell the PE that the hard reset was sent */
103
+    chEvtSignal(pdb_pe_thread, PDB_EVT_PE_HARD_SENT);
104
+
105
+    return PRLHRWaitPE;
106
+}
107
+
108
+static enum hardrst_state hardrst_wait_pe(void)
109
+{
110
+    /* Wait for the PE to tell us that it's done */
111
+    chEvtWaitAny(PDB_EVT_HARDRST_DONE);
112
+
113
+    return PRLHRComplete;
114
+}
115
+
116
+static enum hardrst_state hardrst_complete(void)
117
+{
118
+    /* I'm not aware of anything we have to tell the FUSB302B, so just finish
119
+     * the reset routine. */
120
+    return PRLHRResetLayer;
121
+}
122
+
123
+/*
124
+ * Hard Reset state machine thread
125
+ */
126
+static THD_WORKING_AREA(waHardReset, 128);
127
+static THD_FUNCTION(HardReset, arg) {
128
+    (void) arg;
129
+    enum hardrst_state state = PRLHRResetLayer;
130
+
131
+    while (true) {
132
+        switch (state) {
133
+            case PRLHRResetLayer:
134
+                state = hardrst_reset_layer();
135
+                break;
136
+            case PRLHRIndicateHardReset:
137
+                state = hardrst_indicate_hard_reset();
138
+                break;
139
+            case PRLHRRequestHardReset:
140
+                state = hardrst_request_hard_reset();
141
+                break;
142
+            case PRLHRWaitPHY:
143
+                state = hardrst_wait_phy();
144
+                break;
145
+            case PRLHRHardResetRequested:
146
+                state = hardrst_hard_reset_requested();
147
+                break;
148
+            case PRLHRWaitPE:
149
+                state = hardrst_wait_pe();
150
+                break;
151
+            case PRLHRComplete:
152
+                state = hardrst_complete();
153
+                break;
154
+            default:
155
+                /* This is an error.  It really shouldn't happen.  We might
156
+                 * want to handle it anyway, though. */
157
+                break;
158
+        }
159
+    }
160
+}
161
+
162
+void pdb_hardrst_run(void)
163
+{
164
+    pdb_hardrst_thread = chThdCreateStatic(waHardReset, sizeof(waHardReset),
165
+            PDB_PRIO_PRL, HardReset, NULL);
166
+}

+ 40
- 0
src/hard_reset.h View File

@@ -0,0 +1,40 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_HARD_RESET_H
20
+#define PDB_HARD_RESET_H
21
+
22
+#include <ch.h>
23
+
24
+
25
+/* Events for the Hard Reset thread */
26
+#define PDB_EVT_HARDRST_RESET EVENT_MASK(0)
27
+#define PDB_EVT_HARDRST_I_HARDRST EVENT_MASK(1)
28
+#define PDB_EVT_HARDRST_I_HARDSENT EVENT_MASK(2)
29
+#define PDB_EVT_HARDRST_DONE EVENT_MASK(3)
30
+
31
+/* The Hard Reset thread object */
32
+extern thread_t *pdb_hardrst_thread;
33
+
34
+/*
35
+ * Start the Hard Reset thread
36
+ */
37
+void pdb_hardrst_run(void);
38
+
39
+
40
+#endif /* PDB_HARD_RESET_H */

+ 82
- 0
src/int_n.c View File

@@ -0,0 +1,82 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "int_n.h"
20
+
21
+#include <ch.h>
22
+#include <hal.h>
23
+
24
+#include "priorities.h"
25
+#include "fusb302b.h"
26
+#include "protocol_rx.h"
27
+#include "protocol_tx.h"
28
+#include "hard_reset.h"
29
+
30
+
31
+/*
32
+ * INT_N polling thread
33
+ */
34
+static THD_WORKING_AREA(waIntNPoll, 128);
35
+static THD_FUNCTION(IntNPoll, arg) {
36
+    (void) arg;
37
+
38
+    union fusb_status status;
39
+    eventmask_t events;
40
+
41
+    while (true) {
42
+        /* If the INT_N line is low */
43
+        if (palReadLine(LINE_INT_N) == PAL_LOW) {
44
+            /* Read the FUSB302B status and interrupt registers */
45
+            fusb_get_status(&status);
46
+
47
+            /* If the I_GCRCSENT flag is set, tell the Protocol RX thread */
48
+            if (status.interruptb & FUSB_INTERRUPTB_I_GCRCSENT) {
49
+                chEvtSignal(pdb_prlrx_thread, PDB_EVT_PRLRX_I_GCRCSENT);
50
+            }
51
+
52
+            /* If the I_TXSENT or I_RETRYFAIL flag is set, tell the Protocol TX
53
+             * thread */
54
+            events = 0;
55
+            if (status.interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) {
56
+                events |= PDB_EVT_PRLTX_I_RETRYFAIL;
57
+            }
58
+            if (status.interrupta & FUSB_INTERRUPTA_I_TXSENT) {
59
+                events |= PDB_EVT_PRLTX_I_TXSENT;
60
+            }
61
+            chEvtSignal(pdb_prltx_thread, events);
62
+
63
+            /* If the I_HARDRST or I_HARDSENT flag is set, tell the Hard Reset
64
+             * thread */
65
+            events = 0;
66
+            if (status.interrupta & FUSB_INTERRUPTA_I_HARDRST) {
67
+                events |= PDB_EVT_HARDRST_I_HARDRST;
68
+            }
69
+            if (status.interrupta & FUSB_INTERRUPTA_I_HARDSENT) {
70
+                events |= PDB_EVT_HARDRST_I_HARDSENT;
71
+            }
72
+            chEvtSignal(pdb_hardrst_thread, events);
73
+        }
74
+        chThdSleepMilliseconds(1);
75
+    }
76
+}
77
+
78
+void pdb_int_n_run(void)
79
+{
80
+    chThdCreateStatic(waIntNPoll, sizeof(waIntNPoll), PDB_PRIO_PRL_INT_N,
81
+            IntNPoll, NULL);
82
+}

+ 29
- 0
src/int_n.h View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_INT_N_H
20
+#define PDB_INT_N_H
21
+
22
+
23
+/*
24
+ * Start the INT_N polling thread
25
+ */
26
+void pdb_int_n_run(void);
27
+
28
+
29
+#endif /* PDB_INT_N_H */

+ 116
- 0
src/led.c View File

@@ -0,0 +1,116 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "led.h"
20
+
21
+#include <hal.h>
22
+
23
+#include "priorities.h"
24
+
25
+/* Delays for blink modes */
26
+#define LED_FAST MS2ST(125)
27
+#define LED_MEDIUM MS2ST(250)
28
+#define LED_SLOW MS2ST(500)
29
+
30
+/* Number of blinks for medium blink off mode */
31
+#define LED_MEDIUM_BLINKS 3
32
+
33
+
34
+thread_t *pdb_led_thread;
35
+
36
+/*
37
+ * LED blinker thread.
38
+ */
39
+static THD_WORKING_AREA(waLED, 128);
40
+static THD_FUNCTION(LED, arg) {
41
+    (void) arg;
42
+    /* LED starts turned off */
43
+    eventmask_t state = PDB_EVT_LED_OFF;
44
+    /* The event just received */
45
+    eventmask_t newstate;
46
+    /* Timeout.  TIME_INFINITE for steady modes, other values for blinking
47
+     * modes. */
48
+    systime_t timeout = TIME_INFINITE;
49
+    /* Counter for blinking modes */
50
+    int i;
51
+
52
+    while (true) {
53
+        /* Wait for any event except the last one we saw */
54
+        newstate = chEvtWaitOneTimeout(ALL_EVENTS & ~state, timeout);
55
+        /* If we're changing state, reset the counter */
56
+        if (newstate != 0) {
57
+            /* Clear the events to make sure we don't get an unexpected state
58
+             * change.  As an example of such an unexpected state change, if we
59
+             * set the state to ON, then ON, then BLINK, the second ON wouldn't
60
+             * be seen until after we handled the BLINK event, causing us to
61
+             * end up in the ON state in the end. */
62
+            chEvtGetAndClearEvents(ALL_EVENTS);
63
+            state = newstate;
64
+            i = 0;
65
+        }
66
+
67
+        switch (state) {
68
+            case PDB_EVT_LED_OFF:
69
+                timeout = TIME_INFINITE;
70
+                palClearLine(LINE_LED);
71
+                break;
72
+            case PDB_EVT_LED_ON:
73
+                timeout = TIME_INFINITE;
74
+                palSetLine(LINE_LED);
75
+                break;
76
+            case PDB_EVT_LED_FAST_BLINK:
77
+                timeout = LED_FAST;
78
+                if (i == 0) {
79
+                    palSetLine(LINE_LED);
80
+                } else {
81
+                    palToggleLine(LINE_LED);
82
+                }
83
+                break;
84
+            case PDB_EVT_LED_MEDIUM_BLINK_OFF:
85
+                timeout = LED_MEDIUM;
86
+                if (i == 0) {
87
+                    palSetLine(LINE_LED);
88
+                } else if (i < (LED_MEDIUM_BLINKS * 2) - 1) {
89
+                    palToggleLine(LINE_LED);
90
+                } else {
91
+                    palClearLine(LINE_LED);
92
+                    timeout = TIME_INFINITE;
93
+                }
94
+                break;
95
+            case PDB_EVT_LED_SLOW_BLINK:
96
+                timeout = LED_SLOW;
97
+                if (i == 0) {
98
+                    palSetLine(LINE_LED);
99
+                } else {
100
+                    palToggleLine(LINE_LED);
101
+                }
102
+                break;
103
+            default:
104
+                break;
105
+        }
106
+
107
+        /* Increment the counter for the blinking modes */
108
+        i++;
109
+    }
110
+}
111
+
112
+void pdb_led_run(void)
113
+{
114
+    pdb_led_thread = chThdCreateStatic(waLED, sizeof(waLED), PDB_PRIO_LED, LED,
115
+            NULL);
116
+}

+ 41
- 0
src/led.h View File

@@ -0,0 +1,41 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_LED_H
20
+#define PDB_LED_H
21
+
22
+#include <ch.h>
23
+
24
+
25
+/* Events for the LED thread */
26
+#define PDB_EVT_LED_OFF EVENT_MASK(0)
27
+#define PDB_EVT_LED_ON EVENT_MASK(1)
28
+#define PDB_EVT_LED_FAST_BLINK EVENT_MASK(2)
29
+#define PDB_EVT_LED_MEDIUM_BLINK_OFF EVENT_MASK(3)
30
+#define PDB_EVT_LED_SLOW_BLINK EVENT_MASK(4)
31
+
32
+/* The LED thread object */
33
+extern thread_t *pdb_led_thread;
34
+
35
+/*
36
+ * Start the LED thread
37
+ */
38
+void pdb_led_run(void);
39
+
40
+
41
+#endif /* PDB_LED_H */

+ 85
- 42
src/main.c View File

@@ -17,11 +17,11 @@
17 17
  */
18 18
 
19 19
 /*
20
-    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
20
+   ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio
21 21
 
22
-    Licensed under the Apache License, Version 2.0 (the "License");
23
-    you may not use this file except in compliance with the License.
24
-    You may obtain a copy of the License at
22
+   Licensed under the Apache License, Version 2.0 (the "License");
23
+   you may not use this file except in compliance with the License.
24
+   You may obtain a copy of the License at
25 25
 
26 26
         http://www.apache.org/licenses/LICENSE-2.0
27 27
 
@@ -32,23 +32,66 @@
32 32
     limitations under the License.
33 33
 */
34 34
 
35
-#include "ch.h"
36
-#include "hal.h"
35
+#include <ch.h>
36
+#include <hal.h>
37
+
38
+#include "priorities.h"
39
+#include "led.h"
40
+#include "policy_engine.h"
41
+#include "protocol_tx.h"
42
+#include "protocol_rx.h"
43
+#include "hard_reset.h"
44
+#include "int_n.h"
45
+#include "fusb302b.h"
46
+#include "messages.h"
37 47
 
38 48
 /*
39
- * LED blinker thread, times are in milliseconds.
49
+ * I2C configuration object.
50
+ * I2C2_TIMINGR: 1000 kHz with I2CCLK = 48 MHz, rise time = 100 ns,
51
+ *               fall time = 10 ns (0x00700818)
40 52
  */
41
-static THD_WORKING_AREA(waThread1, 128);
42
-static THD_FUNCTION(Thread1, arg) {
43
-
44
-  (void)arg;
45
-//  chRegSetThreadName("blinker1");
46
-  while (true) {
47
-    palClearLine(LINE_LED);
48
-    chThdSleepMilliseconds(125);
49
-    palSetLine(LINE_LED);
50
-    chThdSleepMilliseconds(125);
51
-  }
53
+static const I2CConfig i2c2config = {
54
+    0x00700818,
55
+    0,
56
+    0
57
+};
58
+
59
+static void setup(void)
60
+{
61
+    chEvtSignal(pdb_led_thread, PDB_EVT_LED_SLOW_BLINK);
62
+
63
+    /* TODO: implement the configuration mode */
64
+
65
+    while (true) {
66
+        chThdSleepMilliseconds(1000);
67
+    }
68
+}
69
+
70
+static void pd_buddy(void)
71
+{
72
+    chEvtSignal(pdb_led_thread, PDB_EVT_LED_FAST_BLINK);
73
+
74
+    /* Start I2C2 to make communication with the PHY possible */
75
+    i2cStart(&I2CD2, &i2c2config);
76
+
77
+    /* Initialize the FUSB302B */
78
+    fusb_setup();
79
+
80
+    /* Create the policy engine thread. */
81
+    pdb_pe_run();
82
+
83
+    /* Create the protocol layer threads. */
84
+    pdb_prlrx_run();
85
+    pdb_prltx_run();
86
+    pdb_hardrst_run();
87
+
88
+    /* Create the INT_N thread. */
89
+    pdb_int_n_run();
90
+
91
+    /* Wait, letting all the other threads do their work. */
92
+    while (true) {
93
+        chThdSleepMilliseconds(1000);
94
+    }
52 95
 }
53 96
 
54 97
 /*
@@ -56,28 +99,28 @@ static THD_FUNCTION(Thread1, arg) {
56 99
  */
57 100
 int main(void) {
58 101
 
59
-  /*
60
-   * System initializations.
61
-   * - HAL initialization, this also initializes the configured device drivers
62
-   *   and performs the board-specific initializations.
63
-   * - Kernel initialization, the main() function becomes a thread and the
64
-   *   RTOS is active.
65
-   */
66
-  halInit();
67
-  chSysInit();
68
-
69
-  /*
70
-   * Creates the blinker thread.
71
-   */
72
-  chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
73
-
74
-  /*
75
-   * Normal main() thread activity, in this demo it does nothing except
76
-   * sleeping in a loop and check the button state, when the button is
77
-   * pressed the test procedure is launched with output on the serial
78
-   * driver 1.
79
-   */
80
-  while (true) {
81
-    chThdSleepMilliseconds(500);
82
-  }
102
+    /*
103
+     * System initializations.
104
+     * - HAL initialization, this also initializes the configured device drivers
105
+     *   and performs the board-specific initializations.
106
+     * - Kernel initialization, the main() function becomes a thread and the
107
+     *   RTOS is active.
108
+     */
109
+    halInit();
110
+    chSysInit();
111
+
112
+    /* Set up the free messages mailbox */
113
+    pdb_msg_pool_init();
114
+
115
+    /* Create the LED thread. */
116
+    pdb_led_run();
117
+
118
+    /* Decide what mode to enter by the state of the button */
119
+    if (palReadLine(LINE_BUTTON) == PAL_HIGH) {
120
+        /* Button pressed -> setup mode */
121
+        setup();
122
+    } else {
123
+        /* Button unpressed -> deliver power, buddy! */
124
+        pd_buddy();
125
+    }
83 126
 }

+ 36
- 0
src/messages.c View File

@@ -0,0 +1,36 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "messages.h"
20
+
21
+
22
+/* The messages that will be available for threads to pass each other */
23
+static union pd_msg pd_messages[PDB_MSG_POOL_SIZE] __attribute__((aligned(sizeof(stkalign_t))));
24
+
25
+/* The pool of available messages */
26
+memory_pool_t pdb_msg_pool;
27
+
28
+
29
+void pdb_msg_pool_init(void)
30
+{
31
+    /* Initialize the pool itself */
32
+    chPoolObjectInit(&pdb_msg_pool, sizeof (union pd_msg), NULL);
33
+
34
+    /* Fill the pool with the available buffers */
35
+    chPoolLoadArray(&pdb_msg_pool, pd_messages, PDB_MSG_POOL_SIZE);
36
+}

+ 60
- 0
src/messages.h View File

@@ -0,0 +1,60 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_MESSAGES_H
20
+#define PDB_MESSAGES_H
21
+
22
+#include <stdint.h>
23
+
24
+#include <ch.h>
25
+
26
+
27
+/*
28
+ * PD message union
29
+ *
30
+ * This can be safely read from or written to in either form without any
31
+ * transformations because everything in the system is little-endian.
32
+ *
33
+ * Two bytes of padding are required at the start to prevent problems due to
34
+ * alignment.  Specifically, without the padding, &obj[0] != &bytes[2], making
35
+ * the statement in the previous paragraph invalid.
36
+ */
37
+union pd_msg {
38
+    struct {
39
+        uint8_t _pad1[2];
40
+        uint8_t bytes[30];
41
+    } __attribute__((packed));
42
+    struct {
43
+        uint8_t _pad2[2];
44
+        uint16_t hdr;
45
+        uint32_t obj[7];
46
+    } __attribute__((packed));
47
+};
48
+
49
+/* Available messages */
50
+#define PDB_MSG_POOL_SIZE 4
51
+
52
+extern memory_pool_t pdb_msg_pool;
53
+
54
+/*
55
+ * Initialize the msg_pool
56
+ */
57
+void pdb_msg_pool_init(void);
58
+
59
+
60
+#endif /* PDB_MESSAGES_H */

+ 219
- 0
src/pd.h View File

@@ -0,0 +1,219 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_PD_H
20
+#define PDB_PD_H
21
+
22
+#include <ch.h>
23
+
24
+
25
+/*
26
+ * Macros for working with USB Power Delivery messages.
27
+ *
28
+ * This file is mostly written from the PD Rev. 2.0 spec, but the header is
29
+ * written from the Rev. 3.0 spec.
30
+ */
31
+
32
+/*
33
+ * PD Header
34
+ */
35
+#define PD_HDR_MSGTYPE_SHIFT 0
36
+#define PD_HDR_MSGTYPE (0x1F << PD_HDR_MSGTYPE_SHIFT)
37
+#define PD_HDR_DATAROLE_SHIFT 5
38
+#define PD_HDR_DATAROLE (0x1 << PD_HDR_DATAROLE_SHIFT)
39
+#define PD_HDR_SPECREV_SHIFT 6
40
+#define PD_HDR_SPECREV (0x3 << PD_HDR_SPECREV_SHIFT)
41
+#define PD_HDR_POWERROLE_SHIFT 8
42
+#define PD_HDR_POWERROLE (1 << PD_HDR_POWERROLE_SHIFT)
43
+#define PD_HDR_MESSAGEID_SHIFT 9
44
+#define PD_HDR_MESSAGEID (0x7 << PD_HDR_MESSAGEID_SHIFT)
45
+#define PD_HDR_NUMOBJ_SHIFT 12
46
+#define PD_HDR_NUMOBJ (0x7 << PD_HDR_NUMOBJ_SHIFT)
47
+#define PD_HDR_EXT (1 << 15)
48
+
49
+/* Message types */
50
+#define PD_MSGTYPE_GET(msg) (((msg)->hdr & PD_HDR_MSGTYPE) >> PD_HDR_MSGTYPE_SHIFT)
51
+/* Control Message */
52
+#define PD_MSGTYPE_GOODCRC 0x01
53
+#define PD_MSGTYPE_GOTOMIN 0x02
54
+#define PD_MSGTYPE_ACCEPT 0x03
55
+#define PD_MSGTYPE_REJECT 0x04
56
+#define PD_MSGTYPE_PING 0x05
57
+#define PD_MSGTYPE_PS_RDY 0x06
58
+#define PD_MSGTYPE_GET_SOURCE_CAP 0x07
59
+#define PD_MSGTYPE_GET_SINK_CAP 0x08
60
+#define PD_MSGTYPE_DR_SWAP 0x09
61
+#define PD_MSGTYPE_PR_SWAP 0x0A
62
+#define PD_MSGTYPE_VCONN_SWAP 0x0B
63
+#define PD_MSGTYPE_WAIT 0x0C
64
+#define PD_MSGTYPE_SOFT_RESET 0x0D
65
+#define PD_MSGTYPE_NOT_SUPPORTED 0x10
66
+#define PD_MSGTYPE_GET_SOURCE_CAP_EXTENDED 0x11
67
+#define PD_MSGTYPE_GET_STATUS 0x12
68
+#define PD_MSGTYPE_FR_SWAP 0x13
69
+#define PD_MSGTYPE_GET_PPS_STATUS 0x14
70
+#define PD_MSGTYPE_GET_COUNTRY_CODES 0x15
71
+/* Data Message */
72
+#define PD_MSGTYPE_SOURCE_CAPABILITIES 0x01
73
+#define PD_MSGTYPE_REQUEST 0x02
74
+#define PD_MSGTYPE_BIST 0x03
75
+#define PD_MSGTYPE_SINK_CAPABILITIES 0x04
76
+#define PD_MSGTYPE_BATTERY_STATUS 0x05
77
+#define PD_MSGTYPE_ALERT 0x06
78
+#define PD_MSGTYPE_GET_COUNTRY_INFO 0x07
79
+#define PD_MSGTYPE_VENDOR_DEFINED 0x0F
80
+/* Extended Message */
81
+#define PD_MSGTYPE_SOURCE_CAPABILITIES_EXTENDED 0x01
82
+#define PD_MSGTYPE_STATUS 0x02
83
+#define PD_MSGTYPE_GET_BATTERY_CAP 0x03
84
+#define PD_MSGTYPE_GET_BATTERY_STATUS 0x04
85
+#define PD_MSGTYPE_BATTERY_CAPABILITIES 0x05
86
+#define PD_MSGTYPE_GET_MANUFACTURER_INFO 0x06
87
+#define PD_MSGTYPE_MANUFACTURER_INFO 0x07
88
+#define PD_MSGTYPE_SECURITY_REQUEST 0x08
89
+#define PD_MSGTYPE_SECURITY_RESPONSE 0x09
90
+#define PD_MSGTYPE_FIRMWARE_UPDATE_REQUEST 0x0A
91
+#define PD_MSGTYPE_FIRMWARE_UPDATE_RESPONSE 0x0B
92
+#define PD_MSGTYPE_PPS_STATUS 0x0C
93
+#define PD_MSGTYPE_COUNTRY_INFO 0x0D
94
+#define PD_MSGTYPE_COUNTRY_CODES 0x0E
95
+
96
+/* Data roles */
97
+#define PD_DATAROLE_UFP (0x0 << PD_HDR_DATAROLE_SHIFT)
98
+#define PD_DATAROLE_DFP (0x1 << PD_HDR_DATAROLE_SHIFT)
99
+
100
+/* Specification revisions */
101
+#define PD_SPECREV_1_0 (0x0 << PD_HDR_SPECREV_SHIFT)
102
+#define PD_SPECREV_2_0 (0x1 << PD_HDR_SPECREV_SHIFT)
103
+#define PD_SPECREV_3_0 (0x2 << PD_HDR_SPECREV_SHIFT)
104
+
105
+/* Port power roles */
106
+#define PD_POWERROLE_SINK (0x0 << PD_HDR_POWERROLE_SHIFT)
107
+#define PD_POWERROLE_SOURCE (0x1 << PD_HDR_POWERROLE_SHIFT)
108
+
109
+/* Message ID */
110
+#define PD_MESSAGEID_GET(msg) (((msg)->hdr & PD_HDR_MESSAGEID) >> PD_HDR_MESSAGEID_SHIFT)
111
+
112
+/* Number of data objects */
113
+#define PD_NUMOBJ(n) (((n) << PD_HDR_NUMOBJ_SHIFT) & PD_HDR_NUMOBJ)
114
+#define PD_NUMOBJ_GET(msg) (((msg)->hdr & PD_HDR_NUMOBJ) >> PD_HDR_NUMOBJ_SHIFT)
115
+
116
+
117
+/*
118
+ * PD Power Data Object
119
+ */
120
+#define PD_PDO_TYPE_SHIFT 30
121
+#define PD_PDO_TYPE (0x3 << PD_PDO_TYPE_SHIFT)
122
+
123
+/* PDO types */
124
+#define PD_PDO_TYPE_FIXED (0x0 << PD_PDO_TYPE_SHIFT)
125
+#define PD_PDO_TYPE_BATTERY (0x1 << PD_PDO_TYPE_SHIFT)
126
+#define PD_PDO_TYPE_VARIABLE (0x2 << PD_PDO_TYPE_SHIFT)
127
+
128
+/* PD Source Fixed PDO */
129
+#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT 29
130
+#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT)
131
+#define PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT 28
132
+#define PD_PDO_SRC_FIXED_USB_SUSPEND (1 << PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT)
133
+#define PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT 27
134
+#define PD_PDO_SRC_FIXED_UNCONSTRAINED (1 << PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT)
135
+#define PD_PDO_SRC_FIXED_USB_COMMS_SHIFT 26
136
+#define PD_PDO_SRC_FIXED_USB_COMMS (1 << PD_PDO_SRC_FIXED_USB_COMMS_SHIFT)
137
+#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT 25
138
+#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT)
139
+#define PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT 20
140
+#define PD_PDO_SRC_FIXED_PEAK_CURRENT (0x3 << PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT)
141
+#define PD_PDO_SRC_FIXED_VOLTAGE_SHIFT 10
142
+#define PD_PDO_SRC_FIXED_VOLTAGE (0x3FF << PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
143
+#define PD_PDO_SRC_FIXED_CURRENT_SHIFT 0
144
+#define PD_PDO_SRC_FIXED_CURRENT (0x3FF << PD_PDO_SRC_FIXED_CURRENT_SHIFT)
145
+
146
+/* PD Source Fixed PDO current */
147
+#define PD_PDO_SRC_FIXED_CURRENT_GET(msg, i) (((msg)->obj[(i)] & PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT)
148
+
149
+/* PD Source Fixed PDO voltage */
150
+#define PD_PDO_SRC_FIXED_VOLTAGE_GET(msg, i) (((msg)->obj[(i)] & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
151
+
152
+/* TODO: other types of source PDO */
153
+
154
+/* PD Sink Fixed PDO */
155
+#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT 29
156
+#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT)
157
+#define PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT 28
158
+#define PD_PDO_SNK_FIXED_HIGHER_CAP (1 << PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT)
159
+#define PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT 27
160
+#define PD_PDO_SNK_FIXED_UNCONSTRAINED (1 << PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT)
161
+#define PD_PDO_SNK_FIXED_USB_COMMS_SHIFT 26
162
+#define PD_PDO_SNK_FIXED_USB_COMMS (1 << PD_PDO_SNK_FIXED_USB_COMMS_SHIFT)
163
+#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT 25
164
+#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT)
165
+#define PD_PDO_SNK_FIXED_VOLTAGE_SHIFT 10
166
+#define PD_PDO_SNK_FIXED_VOLTAGE (0x3FF << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT)
167
+#define PD_PDO_SNK_FIXED_CURRENT_SHIFT 0
168
+#define PD_PDO_SNK_FIXED_CURRENT (0x3FF << PD_PDO_SNK_FIXED_CURRENT_SHIFT)
169
+
170
+/* PD Sink Fixed PDO current */
171
+#define PD_PDO_SNK_FIXED_CURRENT_SET(i) (((i) << PD_PDO_SNK_FIXED_CURRENT_SHIFT) & PD_PDO_SNK_FIXED_CURRENT)
172
+
173
+/* PD Sink Fixed PDO voltage */
174
+#define PD_PDO_SNK_FIXED_VOLTAGE_SET(v) (((v) << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT) & PD_PDO_SNK_FIXED_VOLTAGE)
175
+
176
+/* TODO: other types of sink PDO */
177
+
178
+
179
+/*
180
+ * PD Request Data Object
181
+ */
182
+#define PD_RDO_OBJPOS_SHIFT 28
183
+#define PD_RDO_OBJPOS (0x7 << PD_RDO_OBJPOS_SHIFT)
184
+#define PD_RDO_GIVEBACK_SHIFT 27
185
+#define PD_RDO_GIVEBACK (1 << PD_RDO_GIVEBACK_SHIFT)
186
+#define PD_RDO_CAP_MISMATCH_SHIFT 26
187
+#define PD_RDO_CAP_MISMATCH (1 << PD_RDO_CAP_MISMATCH_SHIFT)
188
+#define PD_RDO_USB_COMMS_SHIFT 25
189
+#define PD_RDO_USB_COMMS (1 << PD_RDO_USB_COMMS_SHIFT)
190
+#define PD_RDO_NO_USB_SUSPEND_SHIFT 24
191
+#define PD_RDO_NO_USB_SUSPEND (1 << PD_RDO_NO_USB_SUSPEND_SHIFT)
192
+
193
+#define PD_RDO_OBJPOS_SET(i) (((i) << PD_RDO_OBJPOS_SHIFT) & PD_RDO_OBJPOS)
194
+
195
+/* Fixed and Variable RDO, no giveback support */
196
+#define PD_RDO_FV_CURRENT_SHIFT 10
197
+#define PD_RDO_FV_CURRENT (0x3FF << PD_RDO_FV_CURRENT_SHIFT)
198
+#define PD_RDO_FV_MAX_CURRENT_SHIFT 0
199
+#define PD_RDO_FV_MAX_CURRENT (0x3FF << PD_RDO_FV_MAX_CURRENT_SHIFT)
200
+
201
+#define PD_RDO_FV_CURRENT_SET(i) (((i) << PD_RDO_FV_CURRENT_SHIFT) & PD_RDO_FV_CURRENT)
202
+#define PD_RDO_FV_MAX_CURRENT_SET(i) (((i) << PD_RDO_FV_MAX_CURRENT_SHIFT) & PD_RDO_FV_MAX_CURRENT)
203
+
204
+/* TODO: other types of RDO */
205
+
206
+
207
+/*
208
+ * Time values
209
+ *
210
+ * Where a range is specified, the middle of the range (rounded down to the
211
+ * nearest millisecond) is used.
212
+ */
213
+#define PD_T_HARD_RESET_COMPLETE MS2ST(4)
214
+#define PD_T_PS_TRANSITION MS2ST(500)
215
+#define PD_T_SENDER_RESPONSE MS2ST(27)
216
+#define PD_T_TYPEC_SINK_WAIT_CAP MS2ST(465)
217
+
218
+
219
+#endif /* PDB_PD_H */

+ 523
- 0
src/policy_engine.c View File

@@ -0,0 +1,523 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "policy_engine.h"
20
+
21
+#include "messages.h"
22
+#include "priorities.h"
23
+#include "device_policy_manager.h"
24
+#include "protocol_tx.h"
25
+#include "hard_reset.h"
26
+#include "pd.h"
27
+
28
+
29
+thread_t *pdb_pe_thread;
30
+
31
+enum policy_engine_state {
32
+    PESinkStartup,
33
+    PESinkDiscovery,
34
+    PESinkWaitCap,
35
+    PESinkEvalCap,
36
+    PESinkSelectCap,
37
+    PESinkTransitionSink,
38
+    PESinkReady,
39
+    PESinkGetSourceCap,
40
+    PESinkGiveSinkCap,
41
+    PESinkHardReset,
42
+    PESinkTransitionDefault,
43
+    PESinkSoftReset,
44
+    PESinkSendSoftReset,
45
+    PESinkSendReject
46
+};
47
+
48
+/* The received message we're currently working with */
49
+static union pd_msg *policy_engine_message = NULL;
50
+/* Whether or not the source capabilities match our required power */
51
+static bool capability_match = false;
52
+/* Policy Engine thread mailbox */
53
+static msg_t pdb_pe_mailbox_queue[PDB_MSG_POOL_SIZE];
54
+mailbox_t pdb_pe_mailbox;
55
+
56
+static enum policy_engine_state pe_sink_startup(void)
57
+{
58
+    /* No need to reset the protocol layer here.  There are two ways into this
59
+     * state: startup and exiting hard reset.  On startup, the protocol layer
60
+     * is reset by the startup procedure.  When exiting hard reset, the
61
+     * protocol layer is reset by the hard reset state machine.  Since it's
62
+     * already done somewhere else, there's no need to do it again here. */
63
+
64
+    return PESinkDiscovery;
65
+}
66
+
67
+static enum policy_engine_state pe_sink_discovery(void)
68
+{
69
+    /* Wait for VBUS.  Since it's our only power source, we already know that
70
+     * we have it, so just move on. */
71
+
72
+    return PESinkWaitCap;
73
+}
74
+
75
+static enum policy_engine_state pe_sink_wait_cap(void)
76
+{
77
+    /* Fetch a message from the protocol layer */
78
+    eventmask_t evt = chEvtWaitAnyTimeout(PDB_EVT_PE_MSG_RX, PD_T_TYPEC_SINK_WAIT_CAP);
79
+    /* If we timed out waiting for Source_Capabilities, send a hard reset */
80
+    if (evt == 0) {
81
+        return PESinkHardReset;
82
+    }
83
+
84
+    /* Get the message */
85
+    if (chMBFetch(&pdb_pe_mailbox, (msg_t *) &policy_engine_message, TIME_IMMEDIATE) == MSG_OK) {
86
+        /* If we got a Source_Capabilities message, read it. */
87
+        if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_SOURCE_CAPABILITIES
88
+                && PD_NUMOBJ_GET(policy_engine_message) > 0) {
89
+            return PESinkEvalCap;
90
+        /* If the message was a Soft_Reset, do the soft reset procedure */
91
+        } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_SOFT_RESET
92
+                && PD_NUMOBJ_GET(policy_engine_message) == 0) {
93
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
94
+            policy_engine_message = NULL;
95
+            return PESinkSoftReset;
96
+        /* If we got an unexpected message, reset */
97
+        } else {
98
+            /* Free the received message */
99
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
100
+            return PESinkHardReset;
101
+        }
102
+    }
103
+
104
+    /* If we failed to get a message, send a hard reset */
105
+    return PESinkHardReset;
106
+}
107
+
108
+static enum policy_engine_state pe_sink_eval_cap(void)
109
+{
110
+    /* Get a message object */
111
+    union pd_msg *request = chPoolAlloc(&pdb_msg_pool);
112
+    /* Ask the DPM what to request */
113
+    capability_match = pdb_dpm_evaluate_capability(policy_engine_message, request);
114
+    /* Free the Source_Capabilities message */
115
+    chPoolFree(&pdb_msg_pool, policy_engine_message);
116
+    /* Put the request into policy_engine_message */
117
+    policy_engine_message = request;
118
+
119
+    return PESinkSelectCap;
120
+}
121
+
122
+static enum policy_engine_state pe_sink_select_cap(void)
123
+{
124
+    /* Transmit the request */
125
+    chMBPost(&pdb_prltx_mailbox, (msg_t) policy_engine_message, TIME_IMMEDIATE);
126
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_MSG_TX);
127
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR);
128
+    /* Free the sent message */
129
+    chPoolFree(&pdb_msg_pool, policy_engine_message);
130
+    policy_engine_message = NULL;
131
+    /* If the message transmission failed, send a hard reset */
132
+    if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
133
+        return PESinkHardReset;
134
+    }
135
+
136
+    /* Wait for a response */
137
+    evt = chEvtWaitAnyTimeout(PDB_EVT_PE_MSG_RX, PD_T_SENDER_RESPONSE);
138
+    /* If we didn't get a response before the timeout, send a hard reset */
139
+    if (evt == 0) {
140
+        return PESinkHardReset;
141
+    }
142
+
143
+    /* Get the response message */
144
+    if (chMBFetch(&pdb_pe_mailbox, (msg_t *) &policy_engine_message, TIME_IMMEDIATE) == MSG_OK) {
145
+        /* If the source accepted our request, wait for the new power */
146
+        if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_ACCEPT
147
+                && PD_NUMOBJ_GET(policy_engine_message) == 0) {
148
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
149
+            policy_engine_message = NULL;
150
+            return PESinkTransitionSink;
151
+        /* If the message was a Soft_Reset, do the soft reset procedure */
152
+        } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_SOFT_RESET
153
+                && PD_NUMOBJ_GET(policy_engine_message) == 0) {
154
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
155
+            policy_engine_message = NULL;
156
+            return PESinkSoftReset;
157
+        /* TODO: Wait and Reject messages should be recognized here */
158
+        } else {
159
+            return PESinkSendSoftReset;
160
+        }
161
+    }
162
+    return PESinkHardReset;
163
+}
164
+
165
+static enum policy_engine_state pe_sink_transition_sink(void)
166
+{
167
+    /* Wait for the PS_RDY message */
168
+    eventmask_t evt = chEvtWaitAnyTimeout(PDB_EVT_PE_MSG_RX, PD_T_PS_TRANSITION);
169
+    /* If no message was received, send a hard reset */
170
+    if (evt == 0) {
171
+        return PESinkHardReset;
172
+    }
173
+
174
+    /* If we received a message, read it */
175
+    if (chMBFetch(&pdb_pe_mailbox, (msg_t *) &policy_engine_message, TIME_IMMEDIATE) == MSG_OK) {
176
+        /* If we got a PS_RDY, handle it */
177
+        if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_PS_RDY
178
+                && PD_NUMOBJ_GET(policy_engine_message) == 0) {
179
+            /* Set the output appropriately */
180
+            if (capability_match) {
181
+                pdb_dpm_output_on();
182
+            } else {
183
+                pdb_dpm_output_off();
184
+            }
185
+
186
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
187
+            policy_engine_message = NULL;
188
+            return PESinkReady;
189
+        /* If there was a protocol error, send a hard reset */
190
+        } else {
191
+            /* Turn off the power output before this hard reset to make sure we
192
+             * don't supply an incorrect voltage to the device we're powering.
193
+             */
194
+            pdb_dpm_output_off();
195
+
196
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
197
+            policy_engine_message = NULL;
198
+            return PESinkHardReset;
199
+        }
200
+    }
201
+
202
+    return PESinkHardReset;
203
+}
204
+
205
+static enum policy_engine_state pe_sink_ready(void)
206
+{
207
+    /* Wait for an event */
208
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PE_MSG_RX | PDB_EVT_PE_RESET);
209
+
210
+    /* If we got reset signaling, transition to default */
211
+    if (evt & PDB_EVT_PE_RESET) {
212
+        return PESinkTransitionDefault;
213
+    }
214
+
215
+    /* If we received a message */
216
+    if (evt & PDB_EVT_PE_MSG_RX) {
217
+        if (chMBFetch(&pdb_pe_mailbox, (msg_t *) &policy_engine_message, TIME_IMMEDIATE) == MSG_OK) {
218
+            /* Ignore vendor-defined messages */
219
+            /* TODO: we're supposed to send NAKs in response to all
220
+             * unsupported structured VDMs except Attention. */
221
+            if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_VENDOR_DEFINED
222
+                    && PD_NUMOBJ_GET(policy_engine_message) > 0) {
223
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
224
+                policy_engine_message = NULL;
225
+                return PESinkReady;
226
+            /* Ignore Ping messages */
227
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_PING
228
+                    && PD_NUMOBJ_GET(policy_engine_message) == 0) {
229
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
230
+                policy_engine_message = NULL;
231
+                return PESinkReady;
232
+            /* Reject DR_Swap messages */
233
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_DR_SWAP
234
+                    && PD_NUMOBJ_GET(policy_engine_message) == 0) {
235
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
236
+                policy_engine_message = NULL;
237
+                return PESinkSendReject;
238
+            /* Reject Get_Source_Cap messages */
239
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_GET_SOURCE_CAP
240
+                    && PD_NUMOBJ_GET(policy_engine_message) == 0) {
241
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
242
+                policy_engine_message = NULL;
243
+                return PESinkSendReject;
244
+            /* Reject PR_Swap messages */
245
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_PR_SWAP
246
+                    && PD_NUMOBJ_GET(policy_engine_message) == 0) {
247
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
248
+                policy_engine_message = NULL;
249
+                return PESinkSendReject;
250
+            /* Reject VCONN_Swap messages */
251
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_VCONN_SWAP
252
+                    && PD_NUMOBJ_GET(policy_engine_message) == 0) {
253
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
254
+                policy_engine_message = NULL;
255
+                return PESinkSendReject;
256
+            /* Reject Request messages */
257
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_REQUEST
258
+                    && PD_NUMOBJ_GET(policy_engine_message) > 0) {
259
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
260
+                policy_engine_message = NULL;
261
+                return PESinkSendReject;
262
+            /* Reject Sink_Capabilities messages */
263
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_SINK_CAPABILITIES
264
+                    && PD_NUMOBJ_GET(policy_engine_message) > 0) {
265
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
266
+                policy_engine_message = NULL;
267
+                return PESinkSendReject;
268
+            /* Evaluate new Source_Capabilities */
269
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_SOURCE_CAPABILITIES
270
+                    && PD_NUMOBJ_GET(policy_engine_message) > 0) {
271
+                return PESinkEvalCap;
272
+            /* Give sink capabilities when asked */
273
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_GET_SINK_CAP
274
+                    && PD_NUMOBJ_GET(policy_engine_message) == 0) {
275
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
276
+                policy_engine_message = NULL;
277
+                return PESinkGiveSinkCap;
278
+            /* If the message was a Soft_Reset, do the soft reset procedure */
279
+            } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_SOFT_RESET
280
+                    && PD_NUMOBJ_GET(policy_engine_message) == 0) {
281
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
282
+                policy_engine_message = NULL;
283
+                return PESinkSoftReset;
284
+            /* If we got an unknown message, send a soft reset */
285
+            } else {
286
+                chPoolFree(&pdb_msg_pool, policy_engine_message);
287
+                policy_engine_message = NULL;
288
+                return PESinkSendSoftReset;
289
+            }
290
+        }
291
+    }
292
+
293
+    return PESinkReady;
294
+}
295
+
296
+static enum policy_engine_state pe_sink_get_source_cap(void)
297
+{
298
+    /* Stubbed until we actually have a need for this */
299
+    return PESinkReady;
300
+}
301
+
302
+static enum policy_engine_state pe_sink_give_sink_cap(void)
303
+{
304
+    /* Get a message object */
305
+    union pd_msg *snk_cap = chPoolAlloc(&pdb_msg_pool);
306
+    /* Get our capabilities from the DPM */
307
+    pdb_dpm_get_sink_capability(snk_cap);
308
+
309
+    /* Transmit our capabilities */
310
+    chMBPost(&pdb_prltx_mailbox, (msg_t) snk_cap, TIME_IMMEDIATE);
311
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_MSG_TX);
312
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR);
313
+
314
+    /* Free the Sink_Capabilities message */
315
+    chPoolFree(&pdb_msg_pool, snk_cap);
316
+    snk_cap = NULL;
317
+
318
+    /* If the message transmission failed, send a hard reset */
319
+    if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
320
+        return PESinkHardReset;
321
+    }
322
+
323
+    return PESinkReady;
324
+}
325
+
326
+static enum policy_engine_state pe_sink_hard_reset(void)
327
+{
328
+    /* Generate a hard reset signal */
329
+    chEvtSignal(pdb_hardrst_thread, PDB_EVT_HARDRST_RESET);
330
+    chEvtWaitAny(PDB_EVT_PE_HARD_SENT);
331
+
332
+    /* TODO: maintain HardResetCounter */
333
+
334
+    return PESinkTransitionDefault;
335
+}
336
+
337
+static enum policy_engine_state pe_sink_transition_default(void)
338
+{
339
+    /* Tell the DPM to turn off the output */
340
+    pdb_dpm_output_off();
341
+
342
+    /* There is no local hardware to reset. */
343
+    /* Since we never change our data role from UFP, there is no reason to set
344
+     * it here. */
345
+
346
+    /* Tell the protocol layer we're done with the reset */
347
+    chEvtSignal(pdb_hardrst_thread, PDB_EVT_HARDRST_DONE);
348
+
349
+    return PESinkStartup;
350
+}
351
+
352
+static enum policy_engine_state pe_sink_soft_reset(void)
353
+{
354
+    /* No need to explicitly reset the protocol layer here.  It resets itself
355
+     * when a Soft_Reset message is received. */
356
+
357
+    /* Get a message object */
358
+    union pd_msg *accept = chPoolAlloc(&pdb_msg_pool);
359
+    /* Make an Accept message */
360
+    accept->hdr = PD_MSGTYPE_ACCEPT | PD_DATAROLE_UFP | PD_SPECREV_2_0
361
+        | PD_POWERROLE_SINK | PD_NUMOBJ(0);
362
+    /* Transmit the Accept */
363
+    chMBPost(&pdb_prltx_mailbox, (msg_t) accept, TIME_IMMEDIATE);
364
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_MSG_TX);
365
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR);
366
+    /* Free the sent message */
367
+    chPoolFree(&pdb_msg_pool, accept);
368
+    accept = NULL;
369
+    /* If the message transmission failed, send a hard reset */
370
+    if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
371
+        return PESinkHardReset;
372
+    }
373
+
374
+    return PESinkWaitCap;
375
+}
376
+
377
+static enum policy_engine_state pe_sink_send_soft_reset(void)
378
+{
379
+    /* No need to explicitly reset the protocol layer here.  It resets itself
380
+     * just before a Soft_Reset message is transmitted. */
381
+
382
+    /* Get a message object */
383
+    union pd_msg *softrst = chPoolAlloc(&pdb_msg_pool);
384
+    /* Make a Soft_Reset message */
385
+    softrst->hdr = PD_MSGTYPE_SOFT_RESET | PD_DATAROLE_UFP | PD_SPECREV_2_0
386
+        | PD_POWERROLE_SINK | PD_NUMOBJ(0);
387
+    /* Transmit the soft reset */
388
+    chMBPost(&pdb_prltx_mailbox, (msg_t) softrst, TIME_IMMEDIATE);
389
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_MSG_TX);
390
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR);
391
+    /* Free the sent message */
392
+    chPoolFree(&pdb_msg_pool, softrst);
393
+    softrst = NULL;
394
+    /* If the message transmission failed, send a hard reset */
395
+    if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
396
+        return PESinkHardReset;
397
+    }
398
+
399
+    /* Wait for a response */
400
+    evt = chEvtWaitAnyTimeout(PDB_EVT_PE_MSG_RX, PD_T_SENDER_RESPONSE);
401
+    /* If we didn't get a response before the timeout, send a hard reset */
402
+    if (evt == 0) {
403
+        return PESinkHardReset;
404
+    }
405
+
406
+    /* Get the response message */
407
+    if (chMBFetch(&pdb_pe_mailbox, (msg_t *) &policy_engine_message, TIME_IMMEDIATE) == MSG_OK) {
408
+        /* If the source accepted our soft reset, wait for capabilities. */
409
+        if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_ACCEPT
410
+                && PD_NUMOBJ_GET(policy_engine_message) == 0) {
411
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
412
+            policy_engine_message = NULL;
413
+            return PESinkWaitCap;
414
+        /* If the message was a Soft_Reset, do the soft reset procedure */
415
+        } else if (PD_MSGTYPE_GET(policy_engine_message) == PD_MSGTYPE_SOFT_RESET
416
+                && PD_NUMOBJ_GET(policy_engine_message) == 0) {
417
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
418
+            policy_engine_message = NULL;
419
+            return PESinkSoftReset;
420
+        /* Otherwise, send a hard reset */
421
+        } else {
422
+            chPoolFree(&pdb_msg_pool, policy_engine_message);
423
+            policy_engine_message = NULL;
424
+            return PESinkHardReset;
425
+        }
426
+    }
427
+    return PESinkHardReset;
428
+}
429
+
430
+static enum policy_engine_state pe_sink_send_reject(void)
431
+{
432
+    /* Get a message object */
433
+    union pd_msg *reject = chPoolAlloc(&pdb_msg_pool);
434
+    /* Make a Reject message */
435
+    reject->hdr = PD_MSGTYPE_REJECT | PD_DATAROLE_UFP | PD_SPECREV_2_0
436
+        | PD_POWERROLE_SINK | PD_NUMOBJ(0);
437
+
438
+    /* Transmit the message */
439
+    chMBPost(&pdb_prltx_mailbox, (msg_t) reject, TIME_IMMEDIATE);
440
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_MSG_TX);
441
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PE_TX_DONE | PDB_EVT_PE_TX_ERR);
442
+
443
+    /* Free the Reject message */
444
+    chPoolFree(&pdb_msg_pool, reject);
445
+    reject = NULL;
446
+
447
+    /* If the message transmission failed, send a soft reset */
448
+    if ((evt & PDB_EVT_PE_TX_DONE) == 0) {
449
+        return PESinkSendSoftReset;
450
+    }
451
+
452
+    return PESinkReady;
453
+}
454
+
455
+/*
456
+ * Policy Engine state machine thread
457
+ */
458
+static THD_WORKING_AREA(waPolicyEngine, 128);
459
+static THD_FUNCTION(PolicyEngine, arg) {
460
+    (void) arg;
461
+    enum policy_engine_state state = PESinkStartup;
462
+
463
+    /* Initialize the mailbox */
464
+    chMBObjectInit(&pdb_pe_mailbox, pdb_pe_mailbox_queue, PDB_MSG_POOL_SIZE);
465
+
466
+    while (true) {
467
+        switch (state) {
468
+            case PESinkStartup:
469
+                state = pe_sink_startup();
470
+                break;
471
+            case PESinkDiscovery:
472
+                state = pe_sink_discovery();
473
+                break;
474
+            case PESinkWaitCap:
475
+                state = pe_sink_wait_cap();
476
+                break;
477
+            case PESinkEvalCap:
478
+                state = pe_sink_eval_cap();
479
+                break;
480
+            case PESinkSelectCap:
481
+                state = pe_sink_select_cap();
482
+                break;
483
+            case PESinkTransitionSink:
484
+                state = pe_sink_transition_sink();
485
+                break;
486
+            case PESinkReady:
487
+                state = pe_sink_ready();
488
+                break;
489
+            case PESinkGetSourceCap:
490
+                state = pe_sink_get_source_cap();
491
+                break;
492
+            case PESinkGiveSinkCap:
493
+                state = pe_sink_give_sink_cap();
494
+                break;
495
+            case PESinkHardReset:
496
+                state = pe_sink_hard_reset();
497
+                break;
498
+            case PESinkTransitionDefault:
499
+                state = pe_sink_transition_default();
500
+                break;
501
+            case PESinkSoftReset:
502
+                state = pe_sink_soft_reset();
503
+                break;
504
+            case PESinkSendSoftReset:
505
+                state = pe_sink_send_soft_reset();
506
+                break;
507
+            case PESinkSendReject:
508
+                state = pe_sink_send_reject();
509
+                break;
510
+            default:
511
+                /* This is an error.  It really shouldn't happen.  We might
512
+                 * want to handle it anyway, though. */
513
+                state = PESinkStartup;
514
+                break;
515
+        }
516
+    }
517
+}
518
+
519
+void pdb_pe_run(void)
520
+{
521
+    pdb_pe_thread = chThdCreateStatic(waPolicyEngine, sizeof(waPolicyEngine),
522
+            PDB_PRIO_PE, PolicyEngine, NULL);
523
+}

+ 44
- 0
src/policy_engine.h View File

@@ -0,0 +1,44 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_POLICY_ENGINE_H
20
+#define PDB_POLICY_ENGINE_H
21
+
22
+#include <ch.h>
23
+
24
+
25
+/* Events for the Policy Engine thread */
26
+#define PDB_EVT_PE_RESET EVENT_MASK(0)
27
+#define PDB_EVT_PE_MSG_RX EVENT_MASK(1)
28
+#define PDB_EVT_PE_TX_DONE EVENT_MASK(2)
29
+#define PDB_EVT_PE_TX_ERR EVENT_MASK(3)
30
+#define PDB_EVT_PE_HARD_SENT EVENT_MASK(4)
31
+
32
+/* The Policy Engine thread object */
33
+extern thread_t *pdb_pe_thread;
34
+
35
+/* Policy Engine thread mailbox */
36
+extern mailbox_t pdb_pe_mailbox;
37
+
38
+/*
39
+ * Start the Policy Engine thread
40
+ */
41
+void pdb_pe_run(void);
42
+
43
+
44
+#endif /* PDB_POLICY_ENGINE_H */

+ 33
- 0
src/priorities.h View File

@@ -0,0 +1,33 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_PRIORITIES_H
20
+#define PDB_PRIORITIES_H
21
+
22
+
23
+#include <ch.h>
24
+
25
+/* PD Buddy thread priorities */
26
+#define PDB_PRIO_PE (NORMALPRIO - 1)
27
+#define PDB_PRIO_PRL (PDB_PRIO_PE - 1)
28
+#define PDB_PRIO_PRL_INT_N (PDB_PRIO_PRL - 1)
29
+
30
+#define PDB_PRIO_LED HIGHPRIO
31
+
32
+
33
+#endif /* PDB_PRIORITIES_H */

+ 190
- 0
src/protocol_rx.c View File

@@ -0,0 +1,190 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "protocol_rx.h"
20
+
21
+#include <stdlib.h>
22
+
23
+#include "priorities.h"
24
+#include "policy_engine.h"
25
+#include "protocol_tx.h"
26
+#include "fusb302b.h"
27
+#include "messages.h"
28
+#include "pd.h"
29
+
30
+
31
+thread_t *pdb_prlrx_thread;
32
+
33
+/*
34
+ * Protocol RX machine states
35
+ *
36
+ * There is no Send_GoodCRC state because the PHY sends the GoodCRC for us.
37
+ * All transitions that would go to that state instead go to Check_MessageID.
38
+ */
39
+enum protocol_rx_state {
40
+    PRLRxWaitPHY,
41
+    PRLRxReset,
42
+    PRLRxCheckMessageID,
43
+    PRLRxStoreMessageID
44
+};
45
+
46
+/* The received message we're currently working with */
47
+static union pd_msg *protocol_rx_message = NULL;
48
+/* The ID of the last received message */
49
+int8_t pdb_prlrx_messageid = -1;
50
+
51
+/*
52
+ * PRL_Rx_Wait_for_PHY_Message state
53
+ */
54
+static enum protocol_rx_state protocol_rx_wait_phy(void)
55
+{
56
+    /* Wait for an event */
57
+    eventmask_t evt = chEvtWaitAny(ALL_EVENTS);
58
+
59
+    /* If we got a reset event, reset */
60
+    if (evt & PDB_EVT_PRLRX_RESET) {
61
+        return PRLRxWaitPHY;
62
+    }
63
+    /* If we got an I_GCRCSENT event, read the message and decide what to do */
64
+    if (evt & PDB_EVT_PRLRX_I_GCRCSENT) {
65
+        /* Get a buffer to read the message into.  Guaranteed to not fail
66
+         * because we have a big enough pool and are careful. */
67
+        protocol_rx_message = chPoolAlloc(&pdb_msg_pool);
68
+        /* Read the message */
69
+        fusb_read_message(protocol_rx_message);
70
+        /* If it's a Soft_Reset, go to the soft reset state */
71
+        if (PD_MSGTYPE_GET(protocol_rx_message) == PD_MSGTYPE_SOFT_RESET
72
+                && PD_NUMOBJ_GET(protocol_rx_message) == 0) {
73
+            return PRLRxReset;
74
+        /* Otherwise, check the message ID */
75
+        } else {
76
+            return PRLRxCheckMessageID;
77
+        }
78
+    }
79
+
80
+    /* We shouldn't ever get here.  This just silence the compiler warning. */
81
+    return PRLRxWaitPHY;
82
+}
83
+
84
+/*
85
+ * PRL_Rx_Layer_Reset_for_Receive state
86
+ */
87
+static enum protocol_rx_state protocol_rx_reset(void)
88
+{
89
+    /* Reset MessageIDCounter */
90
+    pdb_prltx_messageidcounter = 0;
91
+
92
+    /* Clear stored MessageID */
93
+    pdb_prlrx_messageid = -1;
94
+
95
+    /* TX transitions to its reset state */
96
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_RESET);
97
+    chThdYield();
98
+
99
+    /* If we got a RESET signal, reset the machine */
100
+    if (chEvtGetAndClearEvents(PDB_EVT_PRLRX_RESET) != 0) {
101
+        chPoolFree(&pdb_msg_pool, protocol_rx_message);
102
+        protocol_rx_message = NULL;
103
+        return PRLRxWaitPHY;
104
+    }
105
+
106
+    /* Go to the Check_MessageID state */
107
+    return PRLRxCheckMessageID;
108
+}
109
+
110
+/*
111
+ * PRL_Rx_Check_MessageID state
112
+ */
113
+static enum protocol_rx_state protocol_rx_check_messageid(void)
114
+{
115
+    /* If we got a RESET signal, reset the machine */
116
+    if (chEvtGetAndClearEvents(PDB_EVT_PRLRX_RESET) != 0) {
117
+        chPoolFree(&pdb_msg_pool, protocol_rx_message);
118
+        protocol_rx_message = NULL;
119
+        return PRLRxWaitPHY;
120
+    }
121
+
122
+    /* If the message has the stored ID, we've seen this message before.  Free
123
+     * it and don't pass it to the policy engine. */
124
+    if (PD_MESSAGEID_GET(protocol_rx_message) == pdb_prlrx_messageid) {
125
+        chPoolFree(&pdb_msg_pool, protocol_rx_message);
126
+        protocol_rx_message = NULL;
127
+        return PRLRxWaitPHY;
128
+    /* Otherwise, there's either no stored ID or this message has an ID we
129
+     * haven't just seen.  Transition to the Store_MessageID state. */
130
+    } else {
131
+        return PRLRxStoreMessageID;
132
+    }
133
+}
134
+
135
+/*
136
+ * PRL_Rx_Store_MessageID state
137
+ */
138
+static enum protocol_rx_state protocol_rx_store_messageid(void)
139
+{
140
+    /* Tell ProtocolTX to discard the message being transmitted */
141
+    chEvtSignal(pdb_prltx_thread, PDB_EVT_PRLTX_DISCARD);
142
+    chThdYield();
143
+
144
+    /* Update the stored MessageID */
145
+    pdb_prlrx_messageid = PD_MESSAGEID_GET(protocol_rx_message);
146
+
147
+    /* Pass the message to the policy engine. */
148
+    chMBPost(&pdb_pe_mailbox, (msg_t) protocol_rx_message, TIME_IMMEDIATE);
149
+    chEvtSignal(pdb_pe_thread, PDB_EVT_PE_MSG_RX);
150
+
151
+    /* Don't check if we got a RESET because we'd do nothing different. */
152
+
153
+    return PRLRxWaitPHY;
154
+}
155
+
156
+/*
157
+ * Protocol layer RX state machine thread
158
+ */
159
+static THD_WORKING_AREA(waProtocolRX, 128);
160
+static THD_FUNCTION(ProtocolRX, arg) {
161
+    (void) arg;
162
+    enum protocol_rx_state state = PRLRxWaitPHY;
163
+
164
+    while (true) {
165
+        switch (state) {
166
+            case PRLRxWaitPHY:
167
+                state = protocol_rx_wait_phy();
168
+                break;
169
+            case PRLRxReset:
170
+                state = protocol_rx_reset();
171
+                break;
172
+            case PRLRxCheckMessageID:
173
+                state = protocol_rx_check_messageid();
174
+                break;
175
+            case PRLRxStoreMessageID:
176
+                state = protocol_rx_store_messageid();
177
+                break;
178
+            default:
179
+                /* This is an error.  It really shouldn't happen.  We might
180
+                 * want to handle it anyway, though. */
181
+                break;
182
+        }
183
+    }
184
+}
185
+
186
+void pdb_prlrx_run(void)
187
+{
188
+    pdb_prlrx_thread = chThdCreateStatic(waProtocolRX, sizeof(waProtocolRX),
189
+            PDB_PRIO_PRL, ProtocolRX, NULL);
190
+}

+ 43
- 0
src/protocol_rx.h View File

@@ -0,0 +1,43 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_PROTOCOL_RX_H
20
+#define PDB_PROTOCOL_RX_H
21
+
22
+#include <stdint.h>
23
+
24
+#include <ch.h>
25
+
26
+
27
+/* Events for the Protocol RX thread */
28
+#define PDB_EVT_PRLRX_RESET EVENT_MASK(0)
29
+#define PDB_EVT_PRLRX_I_GCRCSENT EVENT_MASK(1)
30
+
31
+/* The Protocol RX thread object */
32
+extern thread_t *pdb_prlrx_thread;
33
+
34
+/* The last received MessageID */
35
+extern int8_t pdb_prlrx_messageid;
36
+
37
+/*
38
+ * Start the Protocol RX thread
39
+ */
40
+void pdb_prlrx_run(void);
41
+
42
+
43
+#endif /* PDB_PROTOCOL_RX_H */

+ 289
- 0
src/protocol_tx.c View File

@@ -0,0 +1,289 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "protocol_tx.h"
20
+
21
+#include "priorities.h"
22
+#include "policy_engine.h"
23
+#include "protocol_rx.h"
24
+#include "fusb302b.h"
25
+#include "messages.h"
26
+#include "pd.h"
27
+
28
+
29
+thread_t *pdb_prltx_thread;
30
+
31
+/* Protocol layer TX thread mailbox */
32
+static msg_t pdb_prltx_mailbox_queue[PDB_MSG_POOL_SIZE];
33
+mailbox_t pdb_prltx_mailbox;
34
+
35
+/*
36
+ * Protocol TX machine states
37
+ *
38
+ * Because the PHY can automatically send retries, the Check_RetryCounter state
39
+ * has been removed, transitions relating to it are modified appropriately, and
40
+ * we don't even keep a RetryCounter.
41
+ */
42
+enum protocol_tx_state {
43
+    PRLTxPHYReset,
44
+    PRLTxWaitMessage,
45
+    PRLTxReset,
46
+    PRLTxConstructMessage,
47
+    PRLTxWaitResponse,
48
+    PRLTxMatchMessageID,
49
+    PRLTxTransmissionError,
50
+    PRLTxMessageSent,
51
+    PRLTxDiscardMessage
52
+};
53
+
54
+/* The message we're currently working on transmitting */
55
+static union pd_msg *protocol_tx_message = NULL;
56
+/* The ID to be used in transmission */
57
+int8_t pdb_prltx_messageidcounter = 0;
58
+
59
+
60
+/*
61
+ * PRL_Tx_PHY_Layer_Reset state
62
+ */
63
+static enum protocol_tx_state protocol_tx_phy_reset(void)
64
+{
65
+    /* Reset the PHY */
66
+    fusb_reset();
67
+
68
+    /* If a message was pending when we got here, tell the policy engine that
69
+     * we failed to send it */
70
+    if (protocol_tx_message != NULL) {
71
+        /* Tell the policy engine that we failed */
72
+        chEvtSignal(pdb_pe_thread, PDB_EVT_PE_TX_ERR);
73
+        /* Finish failing to send the message */
74
+        protocol_tx_message = NULL;
75
+    }
76
+
77
+    /* Wait for a message request */
78
+    return PRLTxWaitMessage;
79
+}
80
+
81
+/*
82
+ * PRL_Tx_Wait_for_Message_Request state
83
+ */
84
+static enum protocol_tx_state protocol_tx_wait_message(void)
85
+{
86
+    /* Wait for an event */
87
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PRLTX_RESET | PDB_EVT_PRLTX_DISCARD
88
+            | PDB_EVT_PRLTX_MSG_TX);
89
+
90
+    if (evt & PDB_EVT_PRLTX_RESET) {
91
+        return PRLTxPHYReset;
92
+    }
93
+    if (evt & PDB_EVT_PRLTX_DISCARD) {
94
+        return PRLTxDiscardMessage;
95
+    }
96
+
97
+    /* If the policy engine is trying to send a message */
98
+    if (evt & PDB_EVT_PRLTX_MSG_TX) {
99
+        /* Get the message */
100
+        chMBFetch(&pdb_prltx_mailbox, (msg_t *) &protocol_tx_message, TIME_IMMEDIATE);
101
+        /* If it's a Soft_Reset, reset the TX layer first */
102
+        if (PD_MSGTYPE_GET(protocol_tx_message) == PD_MSGTYPE_SOFT_RESET
103
+                && PD_NUMOBJ_GET(protocol_tx_message) == 0) {
104
+            return PRLTxReset;
105
+        /* Otherwise, just send the message */
106
+        } else {
107
+            return PRLTxConstructMessage;
108
+        }
109
+    }
110
+
111
+    /* Silence the compiler warning */
112
+    return PRLTxDiscardMessage;
113
+}
114
+
115
+static enum protocol_tx_state protocol_tx_reset(void)
116
+{
117
+    /* Clear MessageIDCounter */
118
+    pdb_prltx_messageidcounter = 0;
119
+
120
+    /* Tell the Protocol RX thread to reset */
121
+    chEvtSignal(pdb_prlrx_thread, PDB_EVT_PRLRX_RESET);
122
+    chThdYield();
123
+
124
+    return PRLTxConstructMessage;
125
+}
126
+
127
+/*
128
+ * PRL_Tx_Construct_Message state
129
+ */
130
+static enum protocol_tx_state protocol_tx_construct_message(void)
131
+{
132
+    /* Make sure nobody wants us to reset */
133
+    eventmask_t evt = chEvtGetAndClearEvents(PDB_EVT_PRLTX_RESET | PDB_EVT_PRLTX_DISCARD);
134
+
135
+    if (evt & PDB_EVT_PRLTX_RESET) {
136
+        return PRLTxPHYReset;
137
+    }
138
+    if (evt & PDB_EVT_PRLTX_DISCARD) {
139
+        return PRLTxDiscardMessage;
140
+    }
141
+
142
+    /* Set the correct MessageID in the message */
143
+    protocol_tx_message->hdr &= ~PD_HDR_MESSAGEID;
144
+    protocol_tx_message->hdr |= pdb_prltx_messageidcounter << PD_HDR_MESSAGEID_SHIFT;
145
+
146
+    /* Send the message to the PHY */
147
+    fusb_send_message(protocol_tx_message);
148
+
149
+    return PRLTxWaitResponse;
150
+}
151
+
152
+/*
153
+ * PRL_Tx_Wait_for_PHY_Response state
154
+ */
155
+static enum protocol_tx_state protocol_tx_wait_response(void)
156
+{
157
+    /* Wait for an event.  There is no need to run CRCReceiveTimer, since the
158
+     * FUSB302B handles that as part of its retry mechanism. */
159
+    eventmask_t evt = chEvtWaitAny(PDB_EVT_PRLTX_RESET | PDB_EVT_PRLTX_DISCARD
160
+            | PDB_EVT_PRLTX_I_TXSENT | PDB_EVT_PRLTX_I_RETRYFAIL);
161
+
162
+    if (evt & PDB_EVT_PRLTX_RESET) {
163
+        return PRLTxPHYReset;
164
+    }
165
+    if (evt & PDB_EVT_PRLTX_DISCARD) {
166
+        return PRLTxDiscardMessage;
167
+    }
168
+
169
+    /* If the message was sent successfully */
170
+    if (evt & PDB_EVT_PRLTX_I_TXSENT) {
171
+        return PRLTxMatchMessageID;
172
+    }
173
+    /* If the message failed to be sent */
174
+    if (evt & PDB_EVT_PRLTX_I_RETRYFAIL) {
175
+        return PRLTxTransmissionError;
176
+    }
177
+
178
+    /* Silence the compiler warning */
179
+    return PRLTxDiscardMessage;
180
+}
181
+
182
+/*
183
+ * PRL_Tx_Match_MessageID state
184
+ */
185
+static enum protocol_tx_state protocol_tx_match_messageid(void)
186
+{
187
+    union pd_msg goodcrc;
188
+
189
+    /* Read the GoodCRC */
190
+    fusb_read_message(&goodcrc);
191
+
192
+    /* Check that the message is correct */
193
+    if (PD_MSGTYPE_GET(&goodcrc) == PD_MSGTYPE_GOODCRC
194
+            && PD_NUMOBJ_GET(&goodcrc) == 0
195
+            && PD_MESSAGEID_GET(&goodcrc) == pdb_prltx_messageidcounter) {
196
+        return PRLTxMessageSent;
197
+    } else {
198
+        return PRLTxTransmissionError;
199
+    }
200
+}
201
+
202
+static enum protocol_tx_state protocol_tx_transmission_error(void)
203
+{
204
+    /* Increment MessageIDCounter */
205
+    pdb_prltx_messageidcounter = (pdb_prltx_messageidcounter + 1) % 8;
206
+
207
+    /* Tell the policy engine that we failed */
208
+    chEvtSignal(pdb_pe_thread, PDB_EVT_PE_TX_ERR);
209
+
210
+    protocol_tx_message = NULL;
211
+    return PRLTxWaitMessage;
212
+}
213
+
214
+static enum protocol_tx_state protocol_tx_message_sent(void)
215
+{
216
+    /* Increment MessageIDCounter */
217
+    pdb_prltx_messageidcounter = (pdb_prltx_messageidcounter + 1) % 8;
218
+
219
+    /* Tell the policy engine that we succeeded */
220
+    chEvtSignal(pdb_pe_thread, PDB_EVT_PE_TX_DONE);
221
+
222
+    protocol_tx_message = NULL;
223
+    return PRLTxWaitMessage;
224
+}
225
+
226
+static enum protocol_tx_state protocol_tx_discard_message(void)
227
+{
228
+    /* If we were working on sending a message, increment MessageIDCounter */
229
+    if (protocol_tx_message != NULL) {
230
+        pdb_prltx_messageidcounter = (pdb_prltx_messageidcounter + 1) % 8;
231
+    }
232
+
233
+    return PRLTxPHYReset;
234
+}
235
+
236
+/*
237
+ * Protocol layer TX state machine thread
238
+ */
239
+static THD_WORKING_AREA(waProtocolTX, 128);
240
+static THD_FUNCTION(ProtocolTX, arg) {
241
+    (void) arg;
242
+
243
+    enum protocol_tx_state state = PRLTxPHYReset;
244
+
245
+    /* Initialize the mailbox */
246
+    chMBObjectInit(&pdb_prltx_mailbox, pdb_prltx_mailbox_queue, PDB_MSG_POOL_SIZE);
247
+
248
+    while (true) {
249
+        switch (state) {
250
+            case PRLTxPHYReset:
251
+                state = protocol_tx_phy_reset();
252
+                break;
253
+            case PRLTxWaitMessage:
254
+                state = protocol_tx_wait_message();
255
+                break;
256
+            case PRLTxReset:
257
+                state = protocol_tx_reset();
258
+                break;
259
+            case PRLTxConstructMessage:
260
+                state = protocol_tx_construct_message();
261
+                break;
262
+            case PRLTxWaitResponse:
263
+                state = protocol_tx_wait_response();
264
+                break;
265
+            case PRLTxMatchMessageID:
266
+                state = protocol_tx_match_messageid();
267
+                break;
268
+            case PRLTxTransmissionError:
269
+                state = protocol_tx_transmission_error();
270
+                break;
271
+            case PRLTxMessageSent:
272
+                state = protocol_tx_message_sent();
273
+                break;
274
+            case PRLTxDiscardMessage:
275
+                state = protocol_tx_discard_message();
276
+                break;
277
+            default:
278
+                /* This is an error.  It really shouldn't happen.  We might
279
+                 * want to handle it anyway, though. */
280
+                break;
281
+        }
282
+    }
283
+}
284
+
285
+void pdb_prltx_run(void)
286
+{
287
+    pdb_prltx_thread = chThdCreateStatic(waProtocolTX, sizeof(waProtocolTX),
288
+            PDB_PRIO_PRL, ProtocolTX, NULL);
289
+}

+ 49
- 0
src/protocol_tx.h View File

@@ -0,0 +1,49 @@
1
+/*
2
+ * PD Buddy - USB Power Delivery for everyone
3
+ * Copyright (C) 2017 Clayton G. Hobbs <clay@lakeserv.net>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef PDB_PROTOCOL_TX_H
20
+#define PDB_PROTOCOL_TX_H
21
+
22
+#include <stdint.h>
23
+
24
+#include <ch.h>
25
+
26
+
27
+/* Events for the Protocol TX thread */
28
+#define PDB_EVT_PRLTX_RESET EVENT_MASK(0)
29
+#define PDB_EVT_PRLTX_I_TXSENT EVENT_MASK(1)
30
+#define PDB_EVT_PRLTX_I_RETRYFAIL EVENT_MASK(2)
31
+#define PDB_EVT_PRLTX_DISCARD EVENT_MASK(3)
32
+#define PDB_EVT_PRLTX_MSG_TX EVENT_MASK(4)
33
+
34
+/* The Protocol TX thread object */
35
+extern thread_t *pdb_prltx_thread;
36
+
37
+/* Protocol layer TX thread mailbox */
38
+extern mailbox_t pdb_prltx_mailbox;
39
+
40
+/* The ID to be used in transmission */
41
+extern int8_t pdb_prltx_messageidcounter;
42
+
43
+/*
44
+ * Start the Protocol TX thread
45
+ */
46
+void pdb_prltx_run(void);
47
+
48
+
49
+#endif /* PDB_PROTOCOL_TX_H */

Loading…
Cancel
Save