| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdint.h> |
| |
| #include "stm32_bl.h" |
| |
| /* |
| * checksum a sequence of bytes. |
| * length == 1 invert the byte |
| * length > 1 xor all bytes |
| */ |
| uint8_t checksum(__attribute__((unused)) handle_t *handle, uint8_t *bytes, int length) |
| { |
| int i; |
| uint8_t csum; |
| |
| if (length == 1) { |
| csum = ~bytes[0]; |
| } else if (length > 1) { |
| for (csum=0,i=0; i<length; i++) |
| csum ^= bytes[i]; |
| } else { |
| csum = 0xFF; |
| } |
| |
| return csum; |
| } |
| |
| static uint8_t write_len(handle_t *handle, int len) |
| { |
| uint8_t buffer[sizeof(uint8_t)+1]; |
| |
| buffer[0] = len-1; |
| |
| return handle->write_data(handle, buffer, sizeof(uint8_t)); |
| } |
| |
| static uint8_t write_cnt(handle_t *handle, uint16_t cnt) |
| { |
| uint8_t buffer[sizeof(uint16_t)+1]; |
| |
| buffer[0] = (cnt >> 8) & 0xFF; |
| buffer[1] = (cnt ) & 0xFF; |
| |
| return handle->write_data(handle, buffer, sizeof(uint16_t)); |
| } |
| |
| static uint8_t write_addr(handle_t *handle, uint32_t addr) |
| { |
| uint8_t buffer[sizeof(uint32_t)+1]; |
| |
| buffer[0] = (addr >> 24) & 0xFF; |
| buffer[1] = (addr >> 16) & 0xFF; |
| buffer[2] = (addr >> 8) & 0xFF; |
| buffer[3] = (addr ) & 0xFF; |
| |
| return handle->write_data(handle, buffer, sizeof(uint32_t)); |
| } |
| |
| /* write length followed by the data */ |
| static uint8_t write_len_data(handle_t *handle, int len, uint8_t *data) |
| { |
| uint8_t buffer[sizeof(uint8_t)+256+sizeof(uint8_t)]; |
| int i; |
| |
| buffer[0] = len-1; |
| |
| for (i=0; i<len; i++) |
| buffer[1+i] = data[i]; |
| |
| return handle->write_data(handle, buffer, sizeof(uint8_t)+len); |
| } |
| |
| /* keep checking for ack until we receive a ack or nack */ |
| static uint8_t read_ack_loop(handle_t *handle) |
| { |
| uint8_t ret; |
| |
| do { |
| ret = handle->read_ack(handle); |
| } while (ret != CMD_ACK && ret != CMD_NACK); |
| |
| return ret; |
| } |
| |
| /* erase a single sector */ |
| uint8_t erase_sector(handle_t *handle, uint16_t sector) |
| { |
| uint8_t buffer[sizeof(uint16_t)+sizeof(uint16_t)+1]; |
| uint8_t ret; |
| |
| handle->write_cmd(handle, handle->cmd_erase); |
| ret = handle->read_ack(handle); |
| if (ret != CMD_ACK) |
| return ret; |
| |
| if (sector >= 0xFFF0) { |
| /* special erase */ |
| write_cnt(handle, sector); |
| } else if (handle->no_extra_sync) { |
| /* sector erase without extra sync (UART case) */ |
| buffer[0] = 0; /* MSB num of sectors - 1 */ |
| buffer[1] = 0; /* LSB num of sectors - 1 */ |
| buffer[2] = (sector >> 8) & 0xFF; |
| buffer[3] = (sector ) & 0xFF; |
| handle->write_data(handle, buffer, sizeof(uint16_t)+sizeof(uint16_t)); |
| } else { |
| /* sector erase */ |
| write_cnt(handle, 0x0000); |
| ret = read_ack_loop(handle); |
| if (ret != CMD_ACK) |
| return ret; |
| write_cnt(handle, sector); |
| } |
| |
| return read_ack_loop(handle); |
| } |
| |
| /* read memory - this will chop the request into 256 byte reads */ |
| uint8_t read_memory(handle_t *handle, uint32_t addr, uint32_t length, uint8_t *buffer) |
| { |
| uint8_t ret = CMD_ACK; |
| uint32_t offset = 0; |
| |
| while (ret == CMD_ACK && length > offset) { |
| handle->write_cmd(handle, handle->cmd_read_memory); |
| ret = handle->read_ack(handle); |
| if (ret == CMD_ACK) { |
| write_addr(handle, addr+offset); |
| ret = read_ack_loop(handle); |
| if (ret == CMD_ACK) { |
| if (length-offset >= 256) { |
| write_len(handle, 256); |
| ret = read_ack_loop(handle); |
| if (ret == CMD_ACK) { |
| handle->read_data(handle, &buffer[offset], 256); |
| offset += 256; |
| } |
| } else { |
| write_len(handle, length-offset); |
| ret = read_ack_loop(handle); |
| if (ret == CMD_ACK) { |
| handle->read_data(handle, &buffer[offset], length - offset); |
| offset = length; |
| } |
| } |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* write memory - this will chop the request into 256 byte writes */ |
| uint8_t write_memory(handle_t *handle, uint32_t addr, uint32_t length, uint8_t *buffer) |
| { |
| uint8_t ret = CMD_ACK; |
| uint32_t offset = 0; |
| |
| while (ret == CMD_ACK && length > offset) { |
| handle->write_cmd(handle, handle->cmd_write_memory); |
| ret = handle->read_ack(handle); |
| if (ret == CMD_ACK) { |
| write_addr(handle, addr+offset); |
| ret = read_ack_loop(handle); |
| if (ret == CMD_ACK) { |
| if (length-offset >= 256) { |
| write_len_data(handle, 256, &buffer[offset]); |
| offset += 256; |
| } else { |
| write_len_data(handle, length-offset, &buffer[offset]); |
| offset = length; |
| } |
| ret = read_ack_loop(handle); |
| } |
| } |
| } |
| |
| return ret; |
| } |