blob: 6f09be0382df4da73453747ea0f4b81dd052fcbb [file] [log] [blame]
Andrew Scull1c021e82017-09-20 17:04:55 +01001/*
2 * Copyright (C) 2017 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 <nos/transport.h>
18
Anatol Pomazaubc775632018-03-26 13:58:36 -070019#include <errno.h>
Andrew Scull1c021e82017-09-20 17:04:55 +010020#include <stdarg.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
Allen Webb362e1672017-12-14 16:14:13 -080024#include <unistd.h>
Andrew Scull1c021e82017-09-20 17:04:55 +010025
26#include <application.h>
27
28/* Note: evaluates expressions multiple times */
29#define MIN(a, b) (((a) < (b)) ? (a) : (b))
30
31#define VERBOSE_LOG 0
32#define DEBUG_LOG 0
33
34#ifdef ANDROID
35/* Logging for Android */
Bill Richardson08c561d2018-03-06 13:13:16 -080036#define LOG_TAG "libnos_transport"
Andrew Scull1c021e82017-09-20 17:04:55 +010037#include <sys/types.h>
Bill Richardson1242dc12018-03-08 12:12:14 -080038#include <log/log.h>
Andrew Scull1c021e82017-09-20 17:04:55 +010039
40#define NLOGE(...) ALOGE(__VA_ARGS__)
Bill Richardson08c561d2018-03-06 13:13:16 -080041#define NLOGV(...) ALOGV(__VA_ARGS__)
42#define NLOGD(...) ALOGD(__VA_ARGS__)
Andrew Scull1c021e82017-09-20 17:04:55 +010043
Allen Webbaf290aa2017-12-21 09:25:04 -080044extern int usleep (uint32_t usec);
45
Andrew Scull1c021e82017-09-20 17:04:55 +010046#else
47/* Logging for other platforms */
48#include <stdio.h>
49
Bill Richardsone7f9c7a2017-09-28 14:38:26 +080050#define NLOGE(...) do { fprintf(stderr, __VA_ARGS__); \
Andrew Scull36ebf2d2017-10-10 11:25:21 +010051 fprintf(stderr, "\n"); } while (0)
Andrew Scull1c021e82017-09-20 17:04:55 +010052#define NLOGV(...) do { if (VERBOSE_LOG) { \
Andrew Scull36ebf2d2017-10-10 11:25:21 +010053 printf(__VA_ARGS__); printf("\n"); } } while (0)
Andrew Scull1c021e82017-09-20 17:04:55 +010054#define NLOGD(...) do { if (DEBUG_LOG) { \
Andrew Scull36ebf2d2017-10-10 11:25:21 +010055 printf(__VA_ARGS__); printf("\n"); } } while (0)
Andrew Scull1c021e82017-09-20 17:04:55 +010056
57#endif
58
Anatol Pomazau0ee610c2018-03-28 13:09:47 -070059/* Citadel might take up to 100ms to wake up */
60#define RETRY_COUNT 25
61#define RETRY_WAIT_TIME_US 5000
62
63static int nos_device_read(const struct nos_device *dev, uint32_t command,
64 uint8_t *buf, uint32_t len) {
65 int retries = RETRY_COUNT;
66 while (retries--) {
67 int err = dev->ops.read(dev->ctx, command, buf, len);
68
69 if (err == -EAGAIN) {
70 /* Linux driver returns EAGAIN error if Citadel chip is asleep.
71 * Give to the chip a little bit of time to awake and retry reading
72 * status again. */
73 usleep(RETRY_WAIT_TIME_US);
74 continue;
75 }
76
77 if (err) {
78 NLOGE("Failed to read: %s", strerror(-err));
79 }
80 return -err;
81 }
82
83 return ETIMEDOUT;
84}
85
86static int nos_device_write(const struct nos_device *dev, uint32_t command,
87 uint8_t *buf, uint32_t len) {
88 int retries = RETRY_COUNT;
89 while (retries--) {
90 int err = dev->ops.write(dev->ctx, command, buf, len);
91
92 if (err == -EAGAIN) {
93 /* Linux driver returns EAGAIN error if Citadel chip is asleep.
94 * Give to the chip a little bit of time to awake and retry reading
95 * status again. */
96 usleep(RETRY_WAIT_TIME_US);
97 continue;
98 }
99
100 if (err) {
101 NLOGE("Failed to write: %s", strerror(-err));
102 }
103 return -err;
104 }
105
106 return ETIMEDOUT;
107}
108
Andrew Scull1c021e82017-09-20 17:04:55 +0100109/* status is non-zero on error */
Andrew Scull3935b182017-10-11 16:02:39 +0100110static int get_status(const struct nos_device *dev,
111 uint8_t app_id, uint32_t *status, uint16_t *ulen)
Andrew Scull1c021e82017-09-20 17:04:55 +0100112{
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100113 uint8_t buf[6];
114 uint32_t command = CMD_ID(app_id) | CMD_IS_READ | CMD_TRANSPORT;
Andrew Scull1c021e82017-09-20 17:04:55 +0100115
Anatol Pomazau0ee610c2018-03-28 13:09:47 -0700116 if (0 != nos_device_read(dev, command, buf, sizeof(buf))) {
117 NLOGE("Failed to read device status");
Andrew Scull3935b182017-10-11 16:02:39 +0100118 return -1;
119 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100120
Anatol Pomazau0ee610c2018-03-28 13:09:47 -0700121 /* The read operation is successful */
122 *status = *(uint32_t *)buf;
123 *ulen = *(uint16_t *)(buf + 4);
124 return 0;
Andrew Scull1c021e82017-09-20 17:04:55 +0100125}
126
Andrew Scull3935b182017-10-11 16:02:39 +0100127static int clear_status(const struct nos_device *dev, uint8_t app_id)
Andrew Scull1c021e82017-09-20 17:04:55 +0100128{
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100129 uint32_t command = CMD_ID(app_id) | CMD_TRANSPORT;
Andrew Scull1c021e82017-09-20 17:04:55 +0100130
Anatol Pomazau0ee610c2018-03-28 13:09:47 -0700131 if (0 != nos_device_write(dev, command, 0, 0)) {
Andrew Scull3935b182017-10-11 16:02:39 +0100132 NLOGE("Failed to clear device status");
133 return -1;
134 }
135
136 return 0;
Andrew Scull1c021e82017-09-20 17:04:55 +0100137}
138
Andrew Scull1c021e82017-09-20 17:04:55 +0100139uint32_t nos_call_application(const struct nos_device *dev,
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100140 uint8_t app_id, uint16_t params,
141 const uint8_t *args, uint32_t arg_len,
142 uint8_t *reply, uint32_t *reply_len)
Andrew Scull1c021e82017-09-20 17:04:55 +0100143{
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100144 uint32_t command;
145 uint8_t buf[MAX_DEVICE_TRANSFER];
146 uint32_t status;
147 uint16_t ulen;
148 uint32_t poll_count = 0;
Andrew Scull1c021e82017-09-20 17:04:55 +0100149
Anatol Pomazaubc775632018-03-26 13:58:36 -0700150 if (get_status(dev, app_id, &status, &ulen) != 0) {
Andrew Scull3935b182017-10-11 16:02:39 +0100151 return APP_ERROR_IO;
152 }
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100153 NLOGV("%d: query status 0x%08x ulen 0x%04x", __LINE__, status, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100154
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100155 /* It's not idle, but we're the only ones telling it what to do, so it
156 * should be. */
157 if (status != APP_STATUS_IDLE) {
Andrew Scull1c021e82017-09-20 17:04:55 +0100158
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100159 /* Try clearing the status */
160 NLOGV("clearing previous status");
Andrew Scull3935b182017-10-11 16:02:39 +0100161 if (clear_status(dev, app_id) != 0) {
162 return APP_ERROR_IO;
163 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100164
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100165 /* Check again */
Andrew Scull3935b182017-10-11 16:02:39 +0100166 if (get_status(dev, app_id, &status, &ulen) != 0) {
167 return APP_ERROR_IO;
168 }
Bill Richardson08c561d2018-03-06 13:13:16 -0800169 NLOGV("%d: query status 0x%08x ulen 0x%04x",__LINE__, status, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100170
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100171 /* It's ignoring us and is still not ready, so it's broken */
Andrew Scull3935b182017-10-11 16:02:39 +0100172 if (status != APP_STATUS_IDLE) {
173 NLOGE("Device is not responding");
174 return APP_ERROR_IO;
175 }
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100176 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100177
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100178 /* Send args data */
179 command = CMD_ID(app_id) | CMD_TRANSPORT | CMD_IS_DATA;
180 do {
181 /*
182 * We can't send more than the device can take. For
183 * Citadel using the TPM Wait protocol on SPS, this is
184 * a constant. For other buses, it may not be.
185 *
186 * For each Write, Citadel requires that we send the length of
187 * what we're about to send in the params field.
188 */
189 ulen = MIN(arg_len, MAX_DEVICE_TRANSFER);
190 CMD_SET_PARAM(command, ulen);
191 if (args && ulen)
192 memcpy(buf, args, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100193
Bill Richardson08c561d2018-03-06 13:13:16 -0800194 NLOGV("Write command 0x%08x, bytes 0x%x", command, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100195
Anatol Pomazau0ee610c2018-03-28 13:09:47 -0700196 if (0 != nos_device_write(dev, command, buf, ulen)) {
Andrew Scull3935b182017-10-11 16:02:39 +0100197 NLOGE("Failed to send datagram to device");
198 return APP_ERROR_IO;
199 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100200
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100201 /* Additional data needs the additional flag set */
202 command |= CMD_MORE_TO_COME;
Andrew Scull1c021e82017-09-20 17:04:55 +0100203
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100204 if (args)
205 args += ulen;
206 if (arg_len)
207 arg_len -= ulen;
208 } while (arg_len);
Andrew Scull1c021e82017-09-20 17:04:55 +0100209
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100210 /* See if we had any errors while sending the args */
Andrew Scull3935b182017-10-11 16:02:39 +0100211 if (get_status(dev, app_id, &status, &ulen) != 0) {
212 return APP_ERROR_IO;
213 }
Bill Richardson08c561d2018-03-06 13:13:16 -0800214 NLOGV("%d: query status 0x%08x ulen 0x%04x", __LINE__, status, ulen);
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100215 if (status & APP_STATUS_DONE)
216 /* Yep, problems. It should still be idle. */
217 goto reply;
Andrew Scull1c021e82017-09-20 17:04:55 +0100218
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100219 /* Now tell the app to do whatever */
220 command = CMD_ID(app_id) | CMD_PARAM(params);
Bill Richardson08c561d2018-03-06 13:13:16 -0800221 NLOGV("Write command 0x%08x", command);
Anatol Pomazau0ee610c2018-03-28 13:09:47 -0700222 if (0 != nos_device_write(dev, command, 0, 0)) {
Andrew Scull3935b182017-10-11 16:02:39 +0100223 NLOGE("Failed to send command datagram to device");
224 return APP_ERROR_IO;
225 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100226
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100227 /* Poll the app status until it's done */
228 do {
Andrew Scull3935b182017-10-11 16:02:39 +0100229 if (get_status(dev, app_id, &status, &ulen) != 0) {
230 return APP_ERROR_IO;
231 }
Bill Richardson08c561d2018-03-06 13:13:16 -0800232 NLOGD("%d: poll status 0x%08x ulen 0x%04x", __LINE__, status, ulen);
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100233 poll_count++;
234 } while (!(status & APP_STATUS_DONE));
Bill Richardson08c561d2018-03-06 13:13:16 -0800235 NLOGV("polled %d times, status 0x%08x ulen 0x%04x", poll_count,
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100236 status, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100237
238reply:
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100239 /* Read any result only if there's a place with room to put it */
240 if (reply && reply_len && *reply_len) {
241 uint16_t left = MIN(*reply_len, ulen);
242 uint16_t gimme, got;
Andrew Scull1c021e82017-09-20 17:04:55 +0100243
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100244 command = CMD_ID(app_id) | CMD_IS_READ |
245 CMD_TRANSPORT | CMD_IS_DATA;
Andrew Scull1c021e82017-09-20 17:04:55 +0100246
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100247 got = 0 ;
248 while (left) {
Andrew Scull1c021e82017-09-20 17:04:55 +0100249
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100250 /*
251 * We can't read more than the device can send. For
252 * Citadel using the TPM Wait protocol on SPS, this is
253 * a constant. For other buses, it may not be.
254 */
255 gimme = MIN(left, MAX_DEVICE_TRANSFER);
Bill Richardson08c561d2018-03-06 13:13:16 -0800256 NLOGV("Read command 0x%08x, bytes 0x%x", command, gimme);
Anatol Pomazau0ee610c2018-03-28 13:09:47 -0700257 if (0 != nos_device_read(dev, command, buf, gimme)) {
Andrew Scull3935b182017-10-11 16:02:39 +0100258 NLOGE("Failed to receive datagram from device");
259 return APP_ERROR_IO;
260 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100261
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100262 memcpy(reply, buf, gimme);
263 reply += gimme;
264 left -= gimme;
265 got += gimme;
266 }
267 /* got it all */
268 *reply_len = got;
269 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100270
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100271 /* Clear the reply manually for the next caller */
272 command = CMD_ID(app_id) | CMD_TRANSPORT;
Anatol Pomazau0ee610c2018-03-28 13:09:47 -0700273 if (0 != nos_device_write(dev, command, 0, 0)) {
Andrew Scull3935b182017-10-11 16:02:39 +0100274 NLOGE("Failed to clear the reply");
275 return APP_ERROR_IO;
276 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100277
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100278 return APP_STATUS_CODE(status);
Andrew Scull1c021e82017-09-20 17:04:55 +0100279}