1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "chpp/link.h"
18
19 #include <inttypes.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <string.h>
23
24 #include "chpp/log.h"
25 #include "chpp/macros.h"
26 #include "chpp/notifier.h"
27 #include "chpp/platform/platform_link.h"
28 #include "chpp/transport.h"
29
30 // The set of signals to use for the linkSendThread.
31 #define SIGNAL_EXIT UINT32_C(1 << 0)
32 #define SIGNAL_DATA UINT32_C(1 << 1)
33 #define SIGNAL_DATA_RX UINT32_C(1 << 2)
34
35 struct ChppNotifier gCycleSendThreadNotifier;
36
cycleSendThread(void)37 void cycleSendThread(void) {
38 chppNotifierSignal(&gCycleSendThreadNotifier, 1);
39 }
40
41 /**
42 * This thread is used to "send" TX data to the remote endpoint. The remote
43 * endpoint is defined by the ChppTransportState pointer, so a loopback link
44 * with a single CHPP instance can be supported.
45 */
linkSendThread(void * linkContext)46 static void *linkSendThread(void *linkContext) {
47 struct ChppLinuxLinkState *context =
48 (struct ChppLinuxLinkState *)(linkContext);
49 while (true) {
50 if (context->manualSendCycle) {
51 chppNotifierWait(&gCycleSendThreadNotifier);
52 }
53 uint32_t signal = chppNotifierTimedWait(&context->notifier, CHPP_TIME_MAX);
54
55 if (signal & SIGNAL_EXIT) {
56 break;
57 }
58
59 if (signal & SIGNAL_DATA) {
60 enum ChppLinkErrorCode error;
61
62 chppMutexLock(&context->mutex);
63
64 if (context->remoteLinkState == NULL) {
65 CHPP_LOGW("remoteLinkState is NULL");
66 error = CHPP_LINK_ERROR_NONE_SENT;
67
68 } else if (!context->linkEstablished) {
69 CHPP_LOGE("No (fake) link");
70 error = CHPP_LINK_ERROR_NO_LINK;
71
72 } else {
73 // Use notifiers only when there are 2 different link layers (i.e. no
74 // loopback). Otherwise call chppRxDataCb directly.
75 if (context->rxInRemoteEndpointWorker &&
76 context->remoteLinkState != context) {
77 chppNotifierSignal(&context->remoteLinkState->notifier,
78 SIGNAL_DATA_RX);
79
80 // Wait for the RX thread to consume the buffer before we can modify
81 // it.
82 chppNotifierTimedWait(&context->rxNotifier, CHPP_TIME_MAX);
83 } else if (!chppRxDataCb(context->remoteLinkState->transportContext,
84 context->buf, context->bufLen)) {
85 CHPP_LOGW("chppRxDataCb return state!=preamble (packet incomplete)");
86 }
87 error = CHPP_LINK_ERROR_NONE_SENT;
88 }
89
90 context->bufLen = 0;
91 chppLinkSendDoneCb(context->transportContext, error);
92
93 chppMutexUnlock(&context->mutex);
94 }
95
96 if (signal & SIGNAL_DATA_RX) {
97 CHPP_NOT_NULL(context->transportContext);
98 CHPP_NOT_NULL(context->remoteLinkState);
99 // Process RX data which are the TX data from the remote link.
100 chppRxDataCb(context->transportContext, context->remoteLinkState->buf,
101 context->remoteLinkState->bufLen);
102 // Unblock the TX thread when the buffer has been consumed.
103 chppNotifierSignal(&context->remoteLinkState->rxNotifier, 0x01);
104 }
105 }
106
107 return NULL;
108 }
109
init(void * linkContext,struct ChppTransportState * transportContext)110 static void init(void *linkContext,
111 struct ChppTransportState *transportContext) {
112 struct ChppLinuxLinkState *context =
113 (struct ChppLinuxLinkState *)(linkContext);
114 context->bufLen = 0;
115 context->transportContext = transportContext;
116 chppMutexInit(&context->mutex);
117 chppNotifierInit(&context->notifier);
118 chppNotifierInit(&context->rxNotifier);
119 chppNotifierInit(&gCycleSendThreadNotifier);
120 pthread_create(&context->linkSendThread, NULL /* attr */, linkSendThread,
121 context);
122 if (context->linkThreadName != NULL) {
123 pthread_setname_np(context->linkSendThread, context->linkThreadName);
124 }
125 }
126
deinit(void * linkContext)127 static void deinit(void *linkContext) {
128 struct ChppLinuxLinkState *context =
129 (struct ChppLinuxLinkState *)(linkContext);
130 context->bufLen = 0;
131 chppNotifierSignal(&context->notifier, SIGNAL_EXIT);
132 if (context->manualSendCycle) {
133 // Unblock the send thread so it exits.
134 cycleSendThread();
135 }
136 pthread_join(context->linkSendThread, NULL /* retval */);
137 chppNotifierDeinit(&context->notifier);
138 chppNotifierDeinit(&context->rxNotifier);
139 chppNotifierDeinit(&gCycleSendThreadNotifier);
140 chppMutexDeinit(&context->mutex);
141 }
142
send(void * linkContext,size_t len)143 static enum ChppLinkErrorCode send(void *linkContext, size_t len) {
144 struct ChppLinuxLinkState *context =
145 (struct ChppLinuxLinkState *)(linkContext);
146 bool success = false;
147 chppMutexLock(&context->mutex);
148 if (context->bufLen != 0) {
149 CHPP_LOGE("Failed to send data - link layer busy");
150 } else if (!context->isLinkActive) {
151 success = false;
152 } else {
153 success = true;
154 context->bufLen = len;
155 }
156 chppMutexUnlock(&context->mutex);
157
158 if (success) {
159 chppNotifierSignal(&context->notifier, SIGNAL_DATA);
160 }
161
162 return success ? CHPP_LINK_ERROR_NONE_QUEUED : CHPP_LINK_ERROR_BUSY;
163 }
164
doWork(void * linkContext,uint32_t signal)165 static void doWork(void *linkContext, uint32_t signal) {
166 UNUSED_VAR(linkContext);
167 UNUSED_VAR(signal);
168 }
169
reset(void * linkContext)170 static void reset(void *linkContext) {
171 struct ChppLinuxLinkState *context =
172 (struct ChppLinuxLinkState *)(linkContext);
173 deinit(context);
174 init(context, context->transportContext);
175 }
176
getConfig(void * linkContext)177 static struct ChppLinkConfiguration getConfig(void *linkContext) {
178 UNUSED_VAR(linkContext);
179 const struct ChppLinkConfiguration config = {
180 .txBufferLen = CHPP_LINUX_LINK_TX_MTU_BYTES,
181 .rxBufferLen = CHPP_LINUX_LINK_RX_MTU_BYTES,
182 };
183 return config;
184 }
185
getTxBuffer(void * linkContext)186 static uint8_t *getTxBuffer(void *linkContext) {
187 struct ChppLinuxLinkState *context =
188 (struct ChppLinuxLinkState *)(linkContext);
189 return &context->buf[0];
190 }
191
192 const struct ChppLinkApi gLinuxLinkApi = {
193 .init = &init,
194 .deinit = &deinit,
195 .send = &send,
196 .doWork = &doWork,
197 .reset = &reset,
198 .getConfig = &getConfig,
199 .getTxBuffer = &getTxBuffer,
200 };
201
getLinuxLinkApi(void)202 const struct ChppLinkApi *getLinuxLinkApi(void) {
203 return &gLinuxLinkApi;
204 }
205