1 /*
2  * Copyright (C) 2015 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 <stdint.h>
18 
19 #include "stm32_bl.h"
20 
21 /*
22  * checksum a sequence of bytes.
23  * length == 1 invert the byte
24  * length > 1 xor all bytes
25  */
checksum(handle_t * handle,uint8_t * bytes,int length)26 uint8_t checksum(__attribute__((unused)) handle_t *handle, uint8_t *bytes, int length)
27 {
28     int i;
29     uint8_t csum;
30 
31     if (length == 1) {
32         csum = ~bytes[0];
33     } else if (length > 1) {
34         for (csum=0,i=0; i<length; i++)
35             csum ^= bytes[i];
36     } else {
37         csum = 0xFF;
38     }
39 
40     return csum;
41 }
42 
write_len(handle_t * handle,int len)43 static uint8_t write_len(handle_t *handle, int len)
44 {
45     uint8_t buffer[sizeof(uint8_t)+1];
46 
47     buffer[0] = len-1;
48 
49     return handle->write_data(handle, buffer, sizeof(uint8_t));
50 }
51 
write_cnt(handle_t * handle,uint16_t cnt)52 static uint8_t write_cnt(handle_t *handle, uint16_t cnt)
53 {
54     uint8_t buffer[sizeof(uint16_t)+1];
55 
56     buffer[0] = (cnt >> 8) & 0xFF;
57     buffer[1] = (cnt     ) & 0xFF;
58 
59     return handle->write_data(handle, buffer, sizeof(uint16_t));
60 }
61 
write_addr(handle_t * handle,uint32_t addr)62 static uint8_t write_addr(handle_t *handle, uint32_t addr)
63 {
64     uint8_t buffer[sizeof(uint32_t)+1];
65 
66     buffer[0] = (addr >> 24) & 0xFF;
67     buffer[1] = (addr >> 16) & 0xFF;
68     buffer[2] = (addr >>  8) & 0xFF;
69     buffer[3] = (addr      ) & 0xFF;
70 
71     return handle->write_data(handle, buffer, sizeof(uint32_t));
72 }
73 
74 /* write length followed by the data */
write_len_data(handle_t * handle,int len,uint8_t * data)75 static uint8_t write_len_data(handle_t *handle, int len, uint8_t *data)
76 {
77     uint8_t buffer[sizeof(uint8_t)+256+sizeof(uint8_t)];
78     int i;
79 
80     buffer[0] = len-1;
81 
82     for (i=0; i<len; i++)
83         buffer[1+i] = data[i];
84 
85     return handle->write_data(handle, buffer, sizeof(uint8_t)+len);
86 }
87 
88 /* keep checking for ack until we receive a ack or nack */
read_ack_loop(handle_t * handle)89 static uint8_t read_ack_loop(handle_t *handle)
90 {
91     uint8_t ret;
92 
93     do {
94         ret = handle->read_ack(handle);
95     } while (ret != CMD_ACK && ret != CMD_NACK);
96 
97     return ret;
98 }
99 
100 /* erase a single sector */
erase_sector(handle_t * handle,uint16_t sector)101 uint8_t erase_sector(handle_t *handle, uint16_t sector)
102 {
103     uint8_t buffer[sizeof(uint16_t)+sizeof(uint16_t)+1];
104     uint8_t ret;
105 
106     handle->write_cmd(handle, handle->cmd_erase);
107     ret = handle->read_ack(handle);
108     if (ret != CMD_ACK)
109         return ret;
110 
111     if (sector >= 0xFFF0) {
112         /* special erase */
113         write_cnt(handle, sector);
114     } else if (handle->no_extra_sync) {
115         /* sector erase without extra sync (UART case) */
116         buffer[0] = 0;  /* MSB num of sectors - 1 */
117         buffer[1] = 0;  /* LSB num of sectors - 1 */
118         buffer[2] = (sector >> 8) & 0xFF;
119         buffer[3] = (sector     ) & 0xFF;
120         handle->write_data(handle, buffer, sizeof(uint16_t)+sizeof(uint16_t));
121     } else {
122         /* sector erase */
123         write_cnt(handle, 0x0000);
124         ret = read_ack_loop(handle);
125         if (ret != CMD_ACK)
126             return ret;
127         write_cnt(handle, sector);
128     }
129 
130     return read_ack_loop(handle);
131 }
132 
133 /* read memory - this will chop the request into 256 byte reads */
read_memory(handle_t * handle,uint32_t addr,uint32_t length,uint8_t * buffer)134 uint8_t read_memory(handle_t *handle, uint32_t addr, uint32_t length, uint8_t *buffer)
135 {
136     uint8_t ret = CMD_ACK;
137     uint32_t offset = 0;
138 
139     while (ret == CMD_ACK && length > offset) {
140         handle->write_cmd(handle, handle->cmd_read_memory);
141         ret = handle->read_ack(handle);
142         if (ret == CMD_ACK) {
143             write_addr(handle, addr+offset);
144             ret = read_ack_loop(handle);
145             if (ret == CMD_ACK) {
146                 if (length-offset >= 256) {
147                     write_len(handle, 256);
148                     ret = read_ack_loop(handle);
149                     if (ret == CMD_ACK) {
150                         handle->read_data(handle, &buffer[offset], 256);
151                         offset += 256;
152                     }
153                 } else {
154                     write_len(handle, length-offset);
155                     ret = read_ack_loop(handle);
156                     if (ret == CMD_ACK) {
157                         handle->read_data(handle, &buffer[offset], length - offset);
158                         offset = length;
159                     }
160                 }
161             }
162         }
163     }
164 
165     return ret;
166 }
167 
168 /* write memory - this will chop the request into 256 byte writes */
write_memory(handle_t * handle,uint32_t addr,uint32_t length,uint8_t * buffer)169 uint8_t write_memory(handle_t *handle, uint32_t addr, uint32_t length, uint8_t *buffer)
170 {
171     uint8_t ret = CMD_ACK;
172     uint32_t offset = 0;
173 
174     while (ret == CMD_ACK && length > offset) {
175         handle->write_cmd(handle, handle->cmd_write_memory);
176         ret = handle->read_ack(handle);
177         if (ret == CMD_ACK) {
178             write_addr(handle, addr+offset);
179             ret = read_ack_loop(handle);
180             if (ret == CMD_ACK) {
181                 if (length-offset >= 256) {
182                     write_len_data(handle, 256, &buffer[offset]);
183                     offset += 256;
184                 } else {
185                     write_len_data(handle, length-offset, &buffer[offset]);
186                     offset = length;
187                 }
188                 ret = read_ack_loop(handle);
189             }
190         }
191     }
192 
193     return ret;
194 }
195