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