blob: c328c18bb0ebbe2faad50856a660ffcff610184c [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
19#include <stdarg.h>
20#include <stdint.h>
21#include <stdlib.h>
22#include <string.h>
Allen Webb362e1672017-12-14 16:14:13 -080023#include <unistd.h>
Andrew Scull1c021e82017-09-20 17:04:55 +010024
25#include <application.h>
26
27/* Note: evaluates expressions multiple times */
28#define MIN(a, b) (((a) < (b)) ? (a) : (b))
29
30#define VERBOSE_LOG 0
31#define DEBUG_LOG 0
32
33#ifdef ANDROID
34/* Logging for Android */
Bill Richardson08c561d2018-03-06 13:13:16 -080035#define LOG_TAG "libnos_transport"
Andrew Scull1c021e82017-09-20 17:04:55 +010036#include <sys/types.h>
Bill Richardson1242dc12018-03-08 12:12:14 -080037#include <log/log.h>
Andrew Scull1c021e82017-09-20 17:04:55 +010038
39#define NLOGE(...) ALOGE(__VA_ARGS__)
Bill Richardson08c561d2018-03-06 13:13:16 -080040#define NLOGV(...) ALOGV(__VA_ARGS__)
41#define NLOGD(...) ALOGD(__VA_ARGS__)
Andrew Scull1c021e82017-09-20 17:04:55 +010042
Allen Webbaf290aa2017-12-21 09:25:04 -080043extern int usleep (uint32_t usec);
44
Andrew Scull1c021e82017-09-20 17:04:55 +010045#else
46/* Logging for other platforms */
47#include <stdio.h>
48
Bill Richardsone7f9c7a2017-09-28 14:38:26 +080049#define NLOGE(...) do { fprintf(stderr, __VA_ARGS__); \
Andrew Scull36ebf2d2017-10-10 11:25:21 +010050 fprintf(stderr, "\n"); } while (0)
Andrew Scull1c021e82017-09-20 17:04:55 +010051#define NLOGV(...) do { if (VERBOSE_LOG) { \
Andrew Scull36ebf2d2017-10-10 11:25:21 +010052 printf(__VA_ARGS__); printf("\n"); } } while (0)
Andrew Scull1c021e82017-09-20 17:04:55 +010053#define NLOGD(...) do { if (DEBUG_LOG) { \
Andrew Scull36ebf2d2017-10-10 11:25:21 +010054 printf(__VA_ARGS__); printf("\n"); } } while (0)
Andrew Scull1c021e82017-09-20 17:04:55 +010055
56#endif
57
Andrew Scull1c021e82017-09-20 17:04:55 +010058/* status is non-zero on error */
Andrew Scull3935b182017-10-11 16:02:39 +010059static int get_status(const struct nos_device *dev,
60 uint8_t app_id, uint32_t *status, uint16_t *ulen)
Andrew Scull1c021e82017-09-20 17:04:55 +010061{
Andrew Scull36ebf2d2017-10-10 11:25:21 +010062 uint8_t buf[6];
63 uint32_t command = CMD_ID(app_id) | CMD_IS_READ | CMD_TRANSPORT;
Andrew Scull1c021e82017-09-20 17:04:55 +010064
Andrew Scull3935b182017-10-11 16:02:39 +010065 if (0 != dev->ops.read(dev->ctx, command, buf, sizeof(buf))) {
Allen Webb362e1672017-12-14 16:14:13 -080066 NLOGV("Failed to read device status");
Andrew Scull3935b182017-10-11 16:02:39 +010067 return -1;
68 }
Andrew Scull1c021e82017-09-20 17:04:55 +010069
Andrew Scull36ebf2d2017-10-10 11:25:21 +010070 *status = *(uint32_t *)buf;
71 *ulen = *(uint16_t *)(buf + 4);
Andrew Scull3935b182017-10-11 16:02:39 +010072 return 0;
Andrew Scull1c021e82017-09-20 17:04:55 +010073}
74
Andrew Scull3935b182017-10-11 16:02:39 +010075static int clear_status(const struct nos_device *dev, uint8_t app_id)
Andrew Scull1c021e82017-09-20 17:04:55 +010076{
Andrew Scull36ebf2d2017-10-10 11:25:21 +010077 uint32_t command = CMD_ID(app_id) | CMD_TRANSPORT;
Andrew Scull1c021e82017-09-20 17:04:55 +010078
Andrew Scull3935b182017-10-11 16:02:39 +010079 if (0 != dev->ops.write(dev->ctx, command, 0, 0)) {
80 NLOGE("Failed to clear device status");
81 return -1;
82 }
83
84 return 0;
Andrew Scull1c021e82017-09-20 17:04:55 +010085}
86
Andrew Scull1c021e82017-09-20 17:04:55 +010087uint32_t nos_call_application(const struct nos_device *dev,
Andrew Scull36ebf2d2017-10-10 11:25:21 +010088 uint8_t app_id, uint16_t params,
89 const uint8_t *args, uint32_t arg_len,
90 uint8_t *reply, uint32_t *reply_len)
Andrew Scull1c021e82017-09-20 17:04:55 +010091{
Andrew Scull36ebf2d2017-10-10 11:25:21 +010092 uint32_t command;
93 uint8_t buf[MAX_DEVICE_TRANSFER];
94 uint32_t status;
95 uint16_t ulen;
96 uint32_t poll_count = 0;
Allen Webb362e1672017-12-14 16:14:13 -080097 uint16_t retries = 10;
Andrew Scull1c021e82017-09-20 17:04:55 +010098
Andrew Scull36ebf2d2017-10-10 11:25:21 +010099 /* Make sure it's idle */
Allen Webb362e1672017-12-14 16:14:13 -0800100 while (retries) {
101 if (get_status(dev, app_id, &status, &ulen) == 0) {
102 break;
103 }
104 --retries;
105 usleep(5000);
106 }
107 if (!retries) {
108 NLOGE("Failed to read device status");
Andrew Scull3935b182017-10-11 16:02:39 +0100109 return APP_ERROR_IO;
110 }
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100111 NLOGV("%d: query status 0x%08x ulen 0x%04x", __LINE__, status, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100112
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100113 /* It's not idle, but we're the only ones telling it what to do, so it
114 * should be. */
115 if (status != APP_STATUS_IDLE) {
Andrew Scull1c021e82017-09-20 17:04:55 +0100116
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100117 /* Try clearing the status */
118 NLOGV("clearing previous status");
Andrew Scull3935b182017-10-11 16:02:39 +0100119 if (clear_status(dev, app_id) != 0) {
120 return APP_ERROR_IO;
121 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100122
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100123 /* Check again */
Andrew Scull3935b182017-10-11 16:02:39 +0100124 if (get_status(dev, app_id, &status, &ulen) != 0) {
125 return APP_ERROR_IO;
126 }
Bill Richardson08c561d2018-03-06 13:13:16 -0800127 NLOGV("%d: query status 0x%08x ulen 0x%04x",__LINE__, status, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100128
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100129 /* It's ignoring us and is still not ready, so it's broken */
Andrew Scull3935b182017-10-11 16:02:39 +0100130 if (status != APP_STATUS_IDLE) {
131 NLOGE("Device is not responding");
132 return APP_ERROR_IO;
133 }
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100134 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100135
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100136 /* Send args data */
137 command = CMD_ID(app_id) | CMD_TRANSPORT | CMD_IS_DATA;
138 do {
139 /*
140 * We can't send more than the device can take. For
141 * Citadel using the TPM Wait protocol on SPS, this is
142 * a constant. For other buses, it may not be.
143 *
144 * For each Write, Citadel requires that we send the length of
145 * what we're about to send in the params field.
146 */
147 ulen = MIN(arg_len, MAX_DEVICE_TRANSFER);
148 CMD_SET_PARAM(command, ulen);
149 if (args && ulen)
150 memcpy(buf, args, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100151
Bill Richardson08c561d2018-03-06 13:13:16 -0800152 NLOGV("Write command 0x%08x, bytes 0x%x", command, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100153
Andrew Scull3935b182017-10-11 16:02:39 +0100154 if (0 != dev->ops.write(dev->ctx, command, buf, ulen)) {
155 NLOGE("Failed to send datagram to device");
156 return APP_ERROR_IO;
157 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100158
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100159 /* Additional data needs the additional flag set */
160 command |= CMD_MORE_TO_COME;
Andrew Scull1c021e82017-09-20 17:04:55 +0100161
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100162 if (args)
163 args += ulen;
164 if (arg_len)
165 arg_len -= ulen;
166 } while (arg_len);
Andrew Scull1c021e82017-09-20 17:04:55 +0100167
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100168 /* See if we had any errors while sending the args */
Andrew Scull3935b182017-10-11 16:02:39 +0100169 if (get_status(dev, app_id, &status, &ulen) != 0) {
170 return APP_ERROR_IO;
171 }
Bill Richardson08c561d2018-03-06 13:13:16 -0800172 NLOGV("%d: query status 0x%08x ulen 0x%04x", __LINE__, status, ulen);
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100173 if (status & APP_STATUS_DONE)
174 /* Yep, problems. It should still be idle. */
175 goto reply;
Andrew Scull1c021e82017-09-20 17:04:55 +0100176
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100177 /* Now tell the app to do whatever */
178 command = CMD_ID(app_id) | CMD_PARAM(params);
Bill Richardson08c561d2018-03-06 13:13:16 -0800179 NLOGV("Write command 0x%08x", command);
Andrew Scull3935b182017-10-11 16:02:39 +0100180 if (0 != dev->ops.write(dev->ctx, command, 0, 0)) {
181 NLOGE("Failed to send command datagram to device");
182 return APP_ERROR_IO;
183 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100184
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100185 /* Poll the app status until it's done */
186 do {
Andrew Scull3935b182017-10-11 16:02:39 +0100187 if (get_status(dev, app_id, &status, &ulen) != 0) {
188 return APP_ERROR_IO;
189 }
Bill Richardson08c561d2018-03-06 13:13:16 -0800190 NLOGD("%d: poll status 0x%08x ulen 0x%04x", __LINE__, status, ulen);
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100191 poll_count++;
192 } while (!(status & APP_STATUS_DONE));
Bill Richardson08c561d2018-03-06 13:13:16 -0800193 NLOGV("polled %d times, status 0x%08x ulen 0x%04x", poll_count,
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100194 status, ulen);
Andrew Scull1c021e82017-09-20 17:04:55 +0100195
196reply:
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100197 /* Read any result only if there's a place with room to put it */
198 if (reply && reply_len && *reply_len) {
199 uint16_t left = MIN(*reply_len, ulen);
200 uint16_t gimme, got;
Andrew Scull1c021e82017-09-20 17:04:55 +0100201
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100202 command = CMD_ID(app_id) | CMD_IS_READ |
203 CMD_TRANSPORT | CMD_IS_DATA;
Andrew Scull1c021e82017-09-20 17:04:55 +0100204
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100205 got = 0 ;
206 while (left) {
Andrew Scull1c021e82017-09-20 17:04:55 +0100207
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100208 /*
209 * We can't read more than the device can send. For
210 * Citadel using the TPM Wait protocol on SPS, this is
211 * a constant. For other buses, it may not be.
212 */
213 gimme = MIN(left, MAX_DEVICE_TRANSFER);
Bill Richardson08c561d2018-03-06 13:13:16 -0800214 NLOGV("Read command 0x%08x, bytes 0x%x", command, gimme);
Andrew Scull3935b182017-10-11 16:02:39 +0100215 if (0 != dev->ops.read(dev->ctx, command, buf, gimme)) {
216 NLOGE("Failed to receive datagram from device");
217 return APP_ERROR_IO;
218 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100219
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100220 memcpy(reply, buf, gimme);
221 reply += gimme;
222 left -= gimme;
223 got += gimme;
224 }
225 /* got it all */
226 *reply_len = got;
227 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100228
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100229 /* Clear the reply manually for the next caller */
230 command = CMD_ID(app_id) | CMD_TRANSPORT;
Andrew Scull3935b182017-10-11 16:02:39 +0100231 if (0 != dev->ops.write(dev->ctx, command, 0, 0)) {
232 NLOGE("Failed to clear the reply");
233 return APP_ERROR_IO;
234 }
Andrew Scull1c021e82017-09-20 17:04:55 +0100235
Andrew Scull36ebf2d2017-10-10 11:25:21 +0100236 return APP_STATUS_CODE(status);
Andrew Scull1c021e82017-09-20 17:04:55 +0100237}