1 /*****************************************************************************
2 * Copyright ©2017-2019 Gemalto – a Thales Company. All rights Reserved.
3 *
4 * This copy is 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 * http://www.apache.org/licenses/LICENSE-2.0 or https://www.apache.org/licenses/LICENSE-2.0.html
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and limitations under the License.
11
12 ****************************************************************************/
13
14 /**
15 * @file
16 * $Author$
17 * $Revision$
18 * $Date$
19 *
20 * eSE Gemalto kernel driver transport.
21 *
22 */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <time.h>
31
32 #include "iso7816_t1.h"
33 #include "transport.h"
34 #include "spi.h"
35
36 #define NSEC_PER_SEC 1000000000L
37 #define NSEC_PER_MSEC 1000000L
38
39 #define ESE_NAD 0x21
40
41 /* < 0 if t1 < t2,
42 * > 0 if t1 > t2,
43 * 0 if t1 == t2.
44 */
45 static int
ts_compare(const struct timespec * t1,const struct timespec * t2)46 ts_compare(const struct timespec *t1, const struct timespec *t2)
47 {
48 if (t1->tv_sec < t2->tv_sec)
49 return -1;
50 else if (t1->tv_sec > t2->tv_sec)
51 return 1;
52 else
53 return t1->tv_nsec - t2->tv_nsec;
54 }
55
56 static uint32_t
div_uint64_rem(uint64_t dividend,uint32_t divisor,uint64_t * remainder)57 div_uint64_rem(uint64_t dividend, uint32_t divisor, uint64_t *remainder)
58 {
59 uint32_t r = 0;
60
61 /* Compiler will optimize to modulo on capable platform */
62 while (dividend >= divisor)
63 dividend -= divisor, r++;
64
65 *remainder = dividend;
66 return r;
67 }
68
69 static struct timespec
ts_add_ns(const struct timespec ta,uint64_t ns)70 ts_add_ns(const struct timespec ta, uint64_t ns)
71 {
72 time_t sec = ta.tv_sec +
73 div_uint64_rem(ta.tv_nsec + ns, NSEC_PER_SEC, &ns);
74 struct timespec ts = { sec, ns };
75
76 return ts;
77 }
78
79 static int
crc_length(struct t1_state * t1)80 crc_length(struct t1_state *t1)
81 {
82 int n = 0;
83
84 switch (t1->chk_algo) {
85 case CHECKSUM_LRC:
86 n = 1;
87 break;
88
89 case CHECKSUM_CRC:
90 n = 2;
91 break;
92 }
93 return n;
94 }
95
96 int
block_send(struct t1_state * t1,const void * block,size_t n)97 block_send(struct t1_state *t1, const void *block, size_t n)
98 {
99 if (n < 4)
100 return -EINVAL;
101
102 return spi_write(t1->spi_fd, block, n);
103 }
104
105 int
block_recv(struct t1_state * t1,void * block,size_t n)106 block_recv(struct t1_state *t1, void *block, size_t n)
107 {
108 uint8_t c;
109 int fd;
110 uint8_t *s, i;
111 int len, max;
112 long bwt;
113
114 struct timespec ts, ts_timeout;
115
116 if (n < 4)
117 return -EINVAL;
118
119 fd = t1->spi_fd;
120 s = block;
121
122 clock_gettime(CLOCK_MONOTONIC, &ts);
123 bwt = t1->bwt * (t1->wtx ? t1->wtx : 1);
124 t1->wtx = 1;
125
126 ts_timeout = ts_add_ns(ts, bwt * NSEC_PER_MSEC);
127
128 /* Pull every 2ms */
129 i = 0;
130 do {
131 /* Wait for 2ms */
132 ts = ts_add_ns(ts, 2 * NSEC_PER_MSEC);
133 while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL))
134 if (errno != EINTR)
135 break;
136
137 len = spi_read(fd, &c, 1);
138 if (len < 0)
139 return len;
140
141 if (ts_compare(&ts, &ts_timeout) >= 0)
142 return -ETIMEDOUT;
143 } while (c != ESE_NAD);
144
145 s[i++] = c;
146
147 /* Minimal length is 3 + sizeof(checksum) */
148 max = 2 + crc_length(t1);
149 len = spi_read(fd, s + 1, max);
150 if (len < 0)
151 return len;
152
153 i += len;
154
155 /* verify that buffer is large enough. */
156 max += s[2];
157 if ((size_t)max > n)
158 return -ENOMEM;
159
160 /* get block remaining if present */
161 if (s[2]) {
162 len = spi_read(fd, s + 4, s[2]);
163 if (len < 0)
164 return len;
165 }
166
167 return max + 1;
168 }
169