#!/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2012-2019 Chintalagiri Shashank
#
# This file is part of pysamloader.
# pysamloader is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pysamloader is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pysamloader. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import logging
import appdirs
from xmodem import XMODEM
from binascii import hexlify
from six import PY2
from io import BytesIO
from .samba import SamBAConnection
from . import log
if sys.version_info.major == 3 and sys.version_info.minor >= 5:
import importlib.util
elif sys.version_info.major == 3 and 3 <= sys.version_info.minor <= 4:
import importlib.machinery
else:
import imp
logger = logging.getLogger('pysamloader')
log.loggers.append(logger)
[docs]def raw_write_page(samba, page_address, data):
for i in range(0, 256, 4):
wbytes = bytearray(data[i:i+4])
wbytes.reverse()
if PY2:
wbytes = hexlify(wbytes)
else:
wbytes = wbytes.hex()
adrstr = hex(page_address + i)[2:].zfill(8)
samba.write_word(adrstr, wbytes)
[docs]def xm_write_page(samba, page_address, data):
adrstr = hex(page_address)[2:].zfill(8)
samba.xm_init_sf(adrstr)
sendbuf = BytesIO(data)
modem = XMODEM(samba.xm_getc, samba.xm_putc)
if not modem.send(sendbuf, quiet=True):
raise IOError("XMODEM Transfer Failure")
sendbuf.close()
[docs]def _page_writer(_writer, samba, device, page_address, bin_file):
"""
Send a single page worth of data from the file to the chip.
Returns 1 as long as data remains.
Returns 0 when end of file is reached.
"""
data = bin_file.read(device.PAGE_SIZE)
if len(data) == device.PAGE_SIZE:
status = 1
else:
if len(data) < device.PAGE_SIZE:
if PY2:
padding = "\255"
else:
padding = bytes("\255".encode())
data += padding * (device.PAGE_SIZE - len(data))
status = 0
logger.debug('Sending file chunk : \n {0}CEND'.format(hexlify(data)))
_writer(samba, page_address, data)
return status
[docs]def _file_writer(_writer, samba, device, filename,
start_page=0, progress_class=None):
stat = 1
if device.FullErase:
samba.efc_eraseall()
page_no = start_page
bin_file = open(filename, "rb")
num_pages = int((os.fstat(bin_file.fileno())[6] + 128) / 256)
if progress_class:
p = progress_class(max=num_pages)
else:
p = None
logger.info("Writing to Flash")
while stat:
samba.efc_wready()
page_address = int(device.FS_ADDRESS, 16) + \
(page_no * device.PAGE_SIZE)
adrstr = hex(page_address)[2:].zfill(8)
logger.debug("Start Address of page {0} : {1}".format(page_no, adrstr))
stat = _page_writer(_writer, samba, device, page_address, bin_file)
samba.efc_ewp(page_no)
logger.debug("Page done : {0}".format(page_no))
page_no = page_no + 1
if p:
p.next(note="Page {0}/{1}".format(page_no, num_pages))
if p:
p.finish()
logger.info("Writing to Flash Complete")
[docs]def xmodem_sendf(*args, **kwargs):
""" Function to burn file onto flash using XMODEM transfers """
return _file_writer(xm_write_page, *args, **kwargs)
[docs]def raw_sendf(*args, **kwargs):
""" Function to burn file onto flash without using XMODEM transfers """
return _file_writer(raw_write_page, *args, **kwargs)
[docs]def write(samba, device, filename, progress_class=None):
if isinstance(device, str):
device = get_device(device)
enable_xmodem = False
if enable_xmodem:
# See device errata in 3U4E datasheet
_ = samba.efc_readfmr().strip()[2:]
samba.efc_setfmr('00000600')
xmodem_sendf(samba, device, filename,
progress_class=progress_class)
else:
raw_sendf(samba, device, filename,
progress_class=progress_class)
[docs]def verify(samba, device, filename, start_page=0, progress_class=None):
"""
Verify the contents of flash against the contents of the file.
Returns the total number of words with errors.
"""
bin_file = open(filename, "rb")
len_bytes = os.fstat(bin_file.fileno())[6]
address = int(device.FS_ADDRESS, 16) + (start_page * device.PAGE_SIZE)
errors = 0
byte_address = 0
if progress_class:
p = progress_class(max=len_bytes)
else:
p = None
logger.info("Verifying Flash")
reversed_word = bytearray(bin_file.read(4))
word = reversed_word
word.reverse()
while reversed_word:
if p:
p.next(n=4, note="{0}/{1} Bytes".format(byte_address, len_bytes))
actual = samba.read_word(hex(address)[2:].zfill(8)).strip()
if PY2:
expected = hexlify(word)
else:
expected = word.hex()
if not actual.upper()[2:] == expected.upper():
logger.error("\nVerification Failed at {0} - {1} {2}"
"".format(hex(address), actual, expected))
errors = errors + 1
else:
logger.debug("Verified Word at {0} - {1} {2}"
"".format(hex(address), actual, expected))
address = address + 4
reversed_word = bytearray(bin_file.read(4))
word = reversed_word
word.reverse()
byte_address = byte_address + 4
if p:
p.finish()
logger.info("Verification Complete. Words with Errors : " + str(errors))
return errors
[docs]def set_boot(samba, device):
logger.info("Setting GPNVM bit to boot from flash")
for i in range(3):
if device.SGP[i] == 1:
samba.efc_setgpnvm(i)
else:
samba.efc_cleargpnvm(i)
[docs]def read_chipid(*args, **kwargs):
samba = kwargs.pop('samba', None)
if not samba:
samba = SamBAConnection(*args, **kwargs)
return samba.getchipid()
[docs]def read_flash_descriptors(*args, **kwargs):
samba = kwargs.pop('samba', None)
if not samba:
samba = SamBAConnection(*args, **kwargs)
return samba.efc_getflashdescriptor()
[docs]def read_unique_identifier(*args, **kwargs):
samba = kwargs.pop('samba', None)
if not samba:
samba = SamBAConnection(*args, **kwargs)
return samba.efc_getuid()
[docs]def _get_device_folder():
devices_folder_candidates = [
os.path.join(appdirs.user_config_dir('pysamloader',
appauthor='Quazar Technologies',
roaming=True),
'devices'),
os.path.join(os.path.split(__file__)[0], 'devices')
]
for candidate in devices_folder_candidates:
if os.path.exists(candidate):
return candidate
else:
raise FileNotFoundError("Devices folder not found!")
[docs]def get_device(name):
# See : https://stackoverflow.com/a/67692/1934174
devices_folder = _get_device_folder()
if sys.version_info.major == 3 and sys.version_info.minor >= 5:
spec = importlib.util.spec_from_file_location(
'pysamloader.devices.{}'.format(name),
os.path.join(devices_folder, '{0}.py'.format(name))
)
dev_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(dev_mod)
elif sys.version_info.major == 3 and 3 <= sys.version_info.minor <= 4:
dev_mod = importlib.machinery.SourceFileLoader(
'pysamloader.devices.{}'.format(name),
os.path.join(devices_folder, '{0}.py'.format(name))
).load_module()
else:
dev_mod = imp.load_source(
'pysamloader.devices.{}'.format(name),
os.path.join(devices_folder, '{0}.py'.format(name))
)
return getattr(dev_mod, name)
[docs]def get_supported_devices():
devices_folder = _get_device_folder()
candidates = [f for f in os.listdir(devices_folder)
if os.path.isfile(os.path.join(devices_folder, f))
and not f.startswith('_')]
_supported_devices = []
for candidate in candidates:
name, ext = os.path.splitext(candidate)
if ext == '.py':
try:
_ = get_device(name)
_supported_devices.append((name, "SAM-BA UART"))
except ImportError:
continue
return _supported_devices
supported_devices = get_supported_devices()