• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 gLinkSendDoneNotifier;
36 
waitForLinkSendDone(void)37 void waitForLinkSendDone(void) {
38   // We use a sufficiently long timeout here to avoid tests from hanging.
39   const uint64_t kTimeoutNs = 5 * CHPP_NSEC_PER_SEC;
40   uint32_t signal = chppNotifierTimedWait(&gLinkSendDoneNotifier, kTimeoutNs);
41   if (signal == 0) {
42     CHPP_LOGE("waitForLinkSendDone timed out");
43   }
44 }
45 
46 /**
47  * This thread is used to "send" TX data to the remote endpoint. The remote
48  * endpoint is defined by the ChppTransportState pointer, so a loopback link
49  * with a single CHPP instance can be supported.
50  */
linkSendThread(void * linkContext)51 static void *linkSendThread(void *linkContext) {
52   struct ChppLinuxLinkState *context =
53       (struct ChppLinuxLinkState *)(linkContext);
54   while (true) {
55     uint32_t signal = chppNotifierTimedWait(&context->notifier, CHPP_TIME_MAX);
56 
57     if (signal & SIGNAL_EXIT) {
58       break;
59     }
60 
61     if (signal & SIGNAL_DATA) {
62       enum ChppLinkErrorCode error;
63 
64       chppMutexLock(&context->mutex);
65 
66       if (context->remoteLinkState == NULL) {
67         error = CHPP_LINK_ERROR_NONE_SENT;
68 
69       } else if (!context->linkEstablished) {
70         CHPP_LOGE("No (fake) link");
71         error = CHPP_LINK_ERROR_NO_LINK;
72 
73       } else {
74         // Use notifiers only when there are 2 different link layers (i.e. no
75         // loopback). Otherwise call chppRxDataCb directly.
76         if (context->rxInRemoteEndpointWorker &&
77             context->remoteLinkState != context) {
78           chppNotifierSignal(&context->remoteLinkState->notifier,
79                              SIGNAL_DATA_RX);
80 
81           // Wait for the RX thread to consume the buffer before we can modify
82           // it.
83           chppNotifierTimedWait(&context->rxNotifier, CHPP_TIME_MAX);
84         } else if (!chppRxDataCb(context->remoteLinkState->transportContext,
85                                  context->buf, context->bufLen)) {
86           CHPP_LOGW("chppRxDataCb return state!=preamble (packet incomplete)");
87         }
88         error = CHPP_LINK_ERROR_NONE_SENT;
89       }
90 
91       context->bufLen = 0;
92       chppLinkSendDoneCb(context->transportContext, error);
93       chppNotifierSignal(&gLinkSendDoneNotifier, 1);
94 
95       chppMutexUnlock(&context->mutex);
96     }
97 
98     if (signal & SIGNAL_DATA_RX) {
99       CHPP_NOT_NULL(context->transportContext);
100       CHPP_NOT_NULL(context->remoteLinkState);
101       // Process RX data which are the TX data from the remote link.
102       chppRxDataCb(context->transportContext, context->remoteLinkState->buf,
103                    context->remoteLinkState->bufLen);
104       // Unblock the TX thread when the buffer has been consumed.
105       chppNotifierSignal(&context->remoteLinkState->rxNotifier, 0x01);
106     }
107   }
108 
109   return NULL;
110 }
111 
init(void * linkContext,struct ChppTransportState * transportContext)112 static void init(void *linkContext,
113                  struct ChppTransportState *transportContext) {
114   struct ChppLinuxLinkState *context =
115       (struct ChppLinuxLinkState *)(linkContext);
116   context->bufLen = 0;
117   context->transportContext = transportContext;
118   chppMutexInit(&context->mutex);
119   chppNotifierInit(&context->notifier);
120   chppNotifierInit(&context->rxNotifier);
121   chppNotifierInit(&gLinkSendDoneNotifier);
122   pthread_create(&context->linkSendThread, NULL /* attr */, linkSendThread,
123                  context);
124   if (context->linkThreadName != NULL) {
125     pthread_setname_np(context->linkSendThread, context->linkThreadName);
126   }
127 }
128 
deinit(void * linkContext)129 static void deinit(void *linkContext) {
130   struct ChppLinuxLinkState *context =
131       (struct ChppLinuxLinkState *)(linkContext);
132   context->bufLen = 0;
133   chppNotifierSignal(&context->notifier, SIGNAL_EXIT);
134   pthread_join(context->linkSendThread, NULL /* retval */);
135   chppNotifierDeinit(&context->notifier);
136   chppNotifierDeinit(&context->rxNotifier);
137   chppNotifierDeinit(&gLinkSendDoneNotifier);
138   chppMutexDeinit(&context->mutex);
139 }
140 
send(void * linkContext,size_t len)141 static enum ChppLinkErrorCode send(void *linkContext, size_t len) {
142   struct ChppLinuxLinkState *context =
143       (struct ChppLinuxLinkState *)(linkContext);
144   bool success = false;
145   chppMutexLock(&context->mutex);
146   if (context->bufLen != 0) {
147     CHPP_LOGE("Failed to send data - link layer busy");
148   } else if (!context->isLinkActive) {
149     success = false;
150   } else {
151     success = true;
152     context->bufLen = len;
153   }
154   chppMutexUnlock(&context->mutex);
155 
156   if (success) {
157     chppNotifierSignal(&context->notifier, SIGNAL_DATA);
158   }
159 
160   return success ? CHPP_LINK_ERROR_NONE_QUEUED : CHPP_LINK_ERROR_BUSY;
161 }
162 
doWork(void * linkContext,uint32_t signal)163 static void doWork(void *linkContext, uint32_t signal) {
164   UNUSED_VAR(linkContext);
165   UNUSED_VAR(signal);
166 }
167 
reset(void * linkContext)168 static void reset(void *linkContext) {
169   struct ChppLinuxLinkState *context =
170       (struct ChppLinuxLinkState *)(linkContext);
171   deinit(context);
172   init(context, context->transportContext);
173 }
174 
getConfig(void * linkContext)175 static struct ChppLinkConfiguration getConfig(void *linkContext) {
176   UNUSED_VAR(linkContext);
177   const struct ChppLinkConfiguration config = {
178       .txBufferLen = CHPP_LINUX_LINK_TX_MTU_BYTES,
179       .rxBufferLen = CHPP_LINUX_LINK_RX_MTU_BYTES,
180   };
181   return config;
182 }
183 
getTxBuffer(void * linkContext)184 static uint8_t *getTxBuffer(void *linkContext) {
185   struct ChppLinuxLinkState *context =
186       (struct ChppLinuxLinkState *)(linkContext);
187   return &context->buf[0];
188 }
189 
190 const struct ChppLinkApi gLinuxLinkApi = {
191     .init = &init,
192     .deinit = &deinit,
193     .send = &send,
194     .doWork = &doWork,
195     .reset = &reset,
196     .getConfig = &getConfig,
197     .getTxBuffer = &getTxBuffer,
198 };
199 
getLinuxLinkApi(void)200 const struct ChppLinkApi *getLinuxLinkApi(void) {
201   return &gLinuxLinkApi;
202 }
203