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