From patchwork Fri Jul 20 15:05:53 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefano Babic X-Patchwork-Id: 947027 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=googlegroups.com (client-ip=2a00:1450:4864:20::23a; helo=mail-lj1-x23a.google.com; envelope-from=swupdate+bncbcxploxj6ikrbwxuy7nakgqebjqce4a@googlegroups.com; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=denx.de Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=googlegroups.com header.i=@googlegroups.com header.b="djFn+Isj"; dkim-atps=neutral Received: from mail-lj1-x23a.google.com (mail-lj1-x23a.google.com [IPv6:2a00:1450:4864:20::23a]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41XDjV3qf9z9s55 for ; Sat, 21 Jul 2018 01:06:05 +1000 (AEST) Received: by mail-lj1-x23a.google.com with SMTP id u1-v6sf3094519lju.13 for ; Fri, 20 Jul 2018 08:06:05 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1532099163; cv=pass; d=google.com; s=arc-20160816; b=Zxazr+Tu6nhGEGUjHbpNY05t1CrE6x988KSptnMXvqVqTUWH6GxNK3bRjrY2BheYvm ie7cgwz/swFRGvKQWobpmgh0DGzLF+ywIgbjEtZdkfQq5snwHS6V7LCtS+A6LyyfYDKK OMG0dk+0bE2qMeuBq80W8jU4LAQYTds9AZs3C7naBHmw5pWDEKfXKN7X12gqhqQYCKYE e5k674/36AY19/jK/zrUdN8VcAUczT6ifbvYOSo2JUlr4fDNA6proVycOA9fftjYsQkV s613kH9dDBYy+cD9GjKh9Xy6yzZz4hVYjztg/ymeMSgx8mn8onKuhwa2PEa2AyIy8Ulu qY3g== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:arc-authentication-results :arc-message-signature:mime-version:sender:dkim-signature :arc-authentication-results; bh=/h22YtHWZ9m1AJCtj2cafYQv/t4xYVs560DqgFgoiEY=; b=GW/w258QDIjtNQ0lTanoStKyW5f+JsvUioMAqwbNnKptJ1hzeGICFj1d9YOFldG36p vyuF7/8QzoXmwvAxKzekV+j1d51Ea5D5UKVdMQqQd26qqMNO+RDjpBnqo84zWbRtxQK1 qcTfrG7HsY81/9LHeIoFBaJ7O9u6ZoBxfLm9OTx6fmcHQuEiuH38AiZXtOe8V3sCHXdd n+412ws+B0VmjiDa3kDdOCe8m1Ud5Pex/fsw8FmfwZEkmrp9rAP08WZLvAGgqV36mgR9 mA66TuNlGSWYtb/SBlpl8B3wftybz/5Y6ex3ud/Kfc4IKVuE8O+uZNUcMLA2zlztmgNP vAzg== ARC-Authentication-Results: i=2; gmr-mx.google.com; spf=neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20161025; h=sender:mime-version:from:to:cc:subject:date:message-id:in-reply-to :references:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=/h22YtHWZ9m1AJCtj2cafYQv/t4xYVs560DqgFgoiEY=; b=djFn+IsjMg+KyXEvVaTaH6PwHCa1+ZCdwzSG17xOyDdwRu8AVkotqCXf9qHpeuYThV PLhiaGreHABugk21klG0g8uL+x0aCPWsyv3Wi2HsJHgK3Eg3L2lJvTl7Mhbd07w1yNpw zhEKl6GluOlW/7lyBKu8z/0Z6IobLFTTuI21S4PySMaOliysWnNjXyp15BCp7c3aCHIb l0mUF+YwVuRFINDkR5pNzdZoKmrko2HtWKsMXG/LbzyXUZBCw/Oi3c8Ca/u6PpjLgS/9 t6NKijr4lIOmdo9IA/ScRHcQ4zv9WOh+By0g1vGj4qsU5ecqCuRb5qxQmY3beBJGiOph avLQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=sender:x-gm-message-state:mime-version:from:to:cc:subject:date :message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-spam-checked-in-group:list-post:list-help:list-archive :list-subscribe:list-unsubscribe; bh=/h22YtHWZ9m1AJCtj2cafYQv/t4xYVs560DqgFgoiEY=; b=BjCELS7zyBozBOeucMfb5PC3dIcXspagyCy0WhkkdzNE/nDrSp9+aSNXlawIlAoL6G lLKeFoyWNOO679/ixBodI6yts8lcPopqrHixOqCZd4jsDpZFYMS6RjfsmZWCKmr3USNL 0zGVfJBqwvrZ9wSDPH8+thuHF8h2OwRKOnuPJ4FMqaku8cAQKkCf93gMdsM6m4zZbOAQ v3mDs3KR5tqH6x7/SNTRmVPABGcKWvwt4rmmTnUpnEsSN3C/13MAwF7scZj1poL5nBkO dcSp2KEINEjPAFq/XFt3zCewD3e7/8SzyXPlDE5VFFdrreDUSShLQCIyGR7XRVJJHZIb FIrQ== Sender: swupdate@googlegroups.com X-Gm-Message-State: AOUpUlHNli1mPylR1FakwRLdPIyNSp1GpiV8wR2AI1iidRSdDd0FTNK5 lm/Dx58VtXYlUVZ90q5T3ZA= X-Google-Smtp-Source: AAOMgpcjugiZZf2KhM9XHvBUp7nOH8s24Dy9lbezijSDDzcud5IWYAF3AbpGbijsdiNaDnPjiM42XQ== X-Received: by 2002:a2e:9bd7:: with SMTP id w23-v6mr18078ljj.1.1532099163062; Fri, 20 Jul 2018 08:06:03 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: swupdate@googlegroups.com Received: by 2002:a2e:3c14:: with SMTP id j20-v6ls288670lja.2.gmail; Fri, 20 Jul 2018 08:06:02 -0700 (PDT) X-Received: by 2002:a2e:9ed2:: with SMTP id h18-v6mr164851ljk.16.1532099162515; Fri, 20 Jul 2018 08:06:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1532099162; cv=none; d=google.com; s=arc-20160816; b=Jsa0K43Ecpd1vB6kmatf+UMW91pRdaYk3UWHJBWmdmzK6/WjA2oEcuTeztpCgF7ss5 tnm97iJlLKlSbq+7R3kXqzy2Ee4iHbzUrmE3OwJNgEKCAuuBFs9kE+ZtkwBAYi38K4zi jrBbqKXPBOs6bcXrONLWy7DV7f8fdDNbzPPzOR7abmveMb3zuULv2dz4OmwaJJ3Hp2aS YzkSbCXI5riQ98G4xfDQoyhR6y64f1ziaYXnob4JNfiisU9L6vR5S28Z4rbraPMkQwsb 88jPggPEO1pJA1iSvfhCi7wHtLoFOsoPYAaQ2YME2srfXVT8N6ehmZ5rLU4mtIx4KGY1 K6+Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=references:in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=qq6PG2R+wifNKthoOpsDlInKDUxBiTPoCs8XGnqpBcQ=; b=N9LyiqqgcJD87PLoRBaslRyhaBPEwahPOPs94O+RYYMDV4JyTU3ExCQJVxaqlgwXIt E7d+UD3jIlqxZkIkFc8s123UlqOcjwk1j71MZoO1hLdxQnsvsp8o7IeAQrQm5n//+0Wm zT/BAwnyjNXiqwyghbyy4R68w4cE5lDZeLlstpWRl48ahdPWIsCbrBSnHUm98S8dEVeC ELLvWNhdgOQPDvtuiIVaeQ8HasiiOcZkfK2zq6RsWV4+gb+M6Tiv589cFmgE4LtifxkL xEVXqARZsqvZGsuc7QzrUUN2k+UnGurWaVY2jEyf8WtLcC7+VbkuerKUHOJdg6zTOe9z kjaA== ARC-Authentication-Results: i=1; gmr-mx.google.com; spf=neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de Received: from mail-out.m-online.net (mail-out.m-online.net. [212.18.0.9]) by gmr-mx.google.com with ESMTPS id n13-v6si47919ljc.4.2018.07.20.08.06.02 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 20 Jul 2018 08:06:02 -0700 (PDT) Received-SPF: neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) client-ip=212.18.0.9; Received: from frontend01.mail.m-online.net (unknown [192.168.8.182]) by mail-out.m-online.net (Postfix) with ESMTP id 41XDjP6JSTz1qwQd; Fri, 20 Jul 2018 17:06:01 +0200 (CEST) Received: from localhost (dynscan1.mnet-online.de [192.168.6.70]) by mail.m-online.net (Postfix) with ESMTP id 41XDjP68yGz20xT7; Fri, 20 Jul 2018 17:06:01 +0200 (CEST) X-Virus-Scanned: amavisd-new at mnet-online.de Received: from mail.mnet-online.de ([192.168.8.182]) by localhost (dynscan1.mail.m-online.net [192.168.6.70]) (amavisd-new, port 10024) with ESMTP id GLuJa6vlLlv9; Fri, 20 Jul 2018 17:06:00 +0200 (CEST) Received: from babic.homelinux.org (host-88-217-136-221.customer.m-online.net [88.217.136.221]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.mnet-online.de (Postfix) with ESMTPS; Fri, 20 Jul 2018 17:06:00 +0200 (CEST) Received: from localhost (mail.babic.homelinux.org [127.0.0.1]) by babic.homelinux.org (Postfix) with ESMTP id AD38A45404AE; Fri, 20 Jul 2018 17:05:59 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at babic.homelinux.org Received: from babic.homelinux.org ([127.0.0.1]) by localhost (mail.babic.homelinux.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id F2T-yKNQNK3E; Fri, 20 Jul 2018 17:05:57 +0200 (CEST) Received: from papero.fritz.box (papero.fritz.box [192.168.178.132]) by babic.homelinux.org (Postfix) with ESMTP id DE9764540478; Fri, 20 Jul 2018 17:05:56 +0200 (CEST) From: Stefano Babic To: swupdate@googlegroups.com Cc: Stefano Babic Subject: [swupdate] [PATCH 2/2] handlers: handler to update microcontroller via UART Date: Fri, 20 Jul 2018 17:05:53 +0200 Message-Id: <1532099153-3133-2-git-send-email-sbabic@denx.de> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1532099153-3133-1-git-send-email-sbabic@denx.de> References: <1532099153-3133-1-git-send-email-sbabic@denx.de> X-Original-Sender: sbabic@denx.de X-Original-Authentication-Results: gmr-mx.google.com; spf=neutral (google.com: 212.18.0.9 is neither permitted nor denied by best guess record for domain of sbabic@denx.de) smtp.mailfrom=sbabic@denx.de Precedence: list Mailing-list: list swupdate@googlegroups.com; contact swupdate+owners@googlegroups.com List-ID: X-Spam-Checked-In-Group: swupdate@googlegroups.com X-Google-Group-Id: 605343134186 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , The handler implements a simple protocol to upgrade a microcontroller via a UART connection. The microcontroller listens to a pin during reset to check if it should remain in bootloader and waits for a an upgrade. The handler gets information about the GPIOs for reset and programming from the sw-description file. The protocol is described in the header of the source file. Signed-off-by: Stefano Babic --- Kconfig | 4 + Makefile.deps | 4 + Makefile.flags | 4 + handlers/Config.in | 11 + handlers/Makefile | 1 + handlers/ucfw_handler.c | 571 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 595 insertions(+) create mode 100644 handlers/ucfw_handler.c diff --git a/Kconfig b/Kconfig index 16f7735..302166e 100644 --- a/Kconfig +++ b/Kconfig @@ -33,6 +33,10 @@ config HAVE_LIBCURL bool option env="HAVE_LIBCURL" +config HAVE_LIBGPIOD + bool + option env="HAVE_LIBGPIOD" + config HAVE_LIBMTD bool option env="HAVE_LIBMTD" diff --git a/Makefile.deps b/Makefile.deps index c49fe83..e2bf669 100644 --- a/Makefile.deps +++ b/Makefile.deps @@ -14,6 +14,10 @@ ifeq ($(HAVE_LIBCURL),) export HAVE_LIBCURL = y endif +ifeq ($(HAVE_LIBGPIOD),) +export HAVE_LIBGPIOD = y +endif + ifeq ($(HAVE_LIBMTD),) ifeq ($(HAVE_LINUX),y) export HAVE_LIBMTD = y diff --git a/Makefile.flags b/Makefile.flags index dac790f..936f7d5 100644 --- a/Makefile.flags +++ b/Makefile.flags @@ -165,6 +165,10 @@ ifeq ($(CONFIG_REMOTE_HANDLER),y) LDLIBS += zmq endif +ifeq ($(CONFIG_UCFWHANDLER),y) +LDLIBS += gpiod +endif + ifeq ($(CONFIG_UBOOT),y) LDLIBS += z ubootenv endif diff --git a/handlers/Config.in b/handlers/Config.in index da1110f..1137acd 100644 --- a/handlers/Config.in +++ b/handlers/Config.in @@ -187,4 +187,15 @@ config BOOTLOADERHANDLER Enable it to change bootloader environment during the installation process. +config UCFWHANDLER + bool "microcontroller firmware update" + depends on HAVE_LIBGPIOD + default n + help + Simple protocol to upgrade a microcontroller + via UART. + +comment "Microcontroller handler depends on libgpiod" + depends on !HAVE_LIBGPIOD + endmenu diff --git a/handlers/Makefile b/handlers/Makefile index 3845476..8db9e41 100644 --- a/handlers/Makefile +++ b/handlers/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_REMOTE_HANDLER) += remote_handler.o obj-$(CONFIG_SHELLSCRIPTHANDLER) += shell_scripthandler.o obj-$(CONFIG_SWUFORWARDER_HANDLER) += swuforward_handler.o obj-$(CONFIG_UBIVOL) += ubivol_handler.o +obj-$(CONFIG_UCFWHANDLER) += ucfw_handler.o diff --git a/handlers/ucfw_handler.c b/handlers/ucfw_handler.c new file mode 100644 index 0000000..4cc7e11 --- /dev/null +++ b/handlers/ucfw_handler.c @@ -0,0 +1,571 @@ +/* + * (C) Copyright 2018 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * This handler allows to update the firmware on a microcontroller + * connected to the main controller via UART. + * Parameters for setup are passed via + * sw-description file. It behavior can be extended to be + * more general. + * The protocol is ASCII based. There is a sequence to be done + * to put the microcontroller in programming mode, after that + * the handler sends the data and waits for an ACK from the + * microcontroller. + * + * The programming of the firmware shall be: + * 1. Enter firmware update mode (bootloader) + * 1. Set "reset line" to logical "low" + * 2. Set "update line" to logical "low" + * 3. Set "reset line" to logical "high" + * 2. Send programming message $PROG;<> to the microcontroller. + * (microcontroller will remain in programming state) + * 3. microcontroller confirms with $READY;<> + * 4. Data transmissions package based from mainboard to microcontroller + * package definition: + * - within a package the records are sent one after another without the end of line marker + * - the package is completed with + * 5. The microcontroller requests the next package with $READY;<> + * 6. Repeat step 4 and 5 until the complete firmware is transmitted. + * 7. The keypad confirms the firmware completion with $COMPLETED;<> + * 8. Leave firmware update mode + * 1. Set "Update line" to logical "high" + * 2. Perform a reset over the "reset line" + * + * <> : checksum. The checksum is calculated as the two's complement of + * the modulo-256 sum over all bytes of the message + * string except for the start marker "$". + * + * The handler expects to get in the properties the setup for the reset + * and prog gpios. They should be in this format: + * + * properties = { + * reset = "::"; + * prog = "::"; + * } + * + * Example: + * + * properties = { + * reset = "/dev/gpiochip0:38:false"; + * prog = "/dev/gpiochip0:39:false"; + * } + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "swupdate.h" +#include "handler.h" +#include "util.h" + +#define MODE_PROG 0 +#define MODE_NORMAL 1 + +#define RESET_CONSUMER "swupdate-uc-handler" +#define PROG_CONSUMER RESET_CONSUMER +#define DEFAULT_TIMEOUT 2 + +void ucfw_handler(void); + +/* + * struct for properties how to set + * GPIOs to put the microcontroller in + * programmming mode. + */ +struct mode_setup { + char gpiodev[SWUPDATE_GENERAL_STRING_SIZE]; + unsigned int offset; + bool active_low; +}; + +enum { + DEVGPIO, + GPIONUM, + ACTIVELOW +}; + +struct handler_priv { + struct mode_setup reset; + struct mode_setup prog; + int fduart; + bool debug; + unsigned int timeout; + char buf[1024]; /* enough for 3 records */ + unsigned int nbytes; +}; + +static int switch_mode(char *devreset, int resoffset, char *devprog, int progoffset, int mode) +{ + struct gpiod_chip *chipreset, *chipprog; + struct gpiod_line *linereset, *lineprog; + int ret = 0; + int status; + + chipreset = gpiod_chip_open(devreset); + if (strcmp(devreset, devprog)) + chipprog = gpiod_chip_open(devprog); + else + chipprog = chipreset; + + if (!chipreset || !chipprog) { + ERROR("Cannot open gpio driver"); + ret =-ENODEV; + goto freegpios; + } + + linereset = gpiod_chip_get_line(chipreset, resoffset); + lineprog = gpiod_chip_get_line(chipprog, progoffset); + + if (!linereset || !lineprog) { + ERROR("Cannot get requested GPIOs: %d on %s and %d on %s", + resoffset, devreset, + progoffset, devprog); + ret =-ENODEV; + goto freegpios; + } + + status = gpiod_line_request_output(linereset, RESET_CONSUMER, false, 0); + if (status) { + ret =-ENODEV; + ERROR("Cannot request reset line"); + goto freegpios; + } + status = gpiod_line_request_output(lineprog, PROG_CONSUMER, false, mode); + if (status) { + ret =-ENODEV; + ERROR("Cannot request prog line"); + goto freegpios; + } + + /* + * A reset is always done + */ + gpiod_line_set_value(linereset, 0); + + /* Set programming mode */ + gpiod_line_set_value(lineprog, mode); + + usleep(20000); + + /* Remove reset */ + gpiod_line_set_value(linereset, 1); + + usleep(20000); + +freegpios: + if (chipreset) gpiod_chip_close(chipreset); + if (chipprog && (chipprog != chipreset)) gpiod_chip_close(chipprog); + + return ret; +} + +static bool verify_chksum(char *buf, unsigned int *size) +{ + int i; + uint16_t chksum = 0; + int len = *size; + + if (len < 3) + return false; + while (buf[len - 1] == '\r' || (buf[len - 1] == '\n')) + len--; + chksum = from_ascii(&buf[len - 2], 2, LG_16); + len -= 2; + for (i = 1; i < len; i++) + chksum += buf[i]; + + chksum &= 0xff; + + if (chksum) + ERROR("Wrong checksum received: %x", chksum); + + /* + * Drop checksum after verification + */ + buf[len] = '\0'; + + *size = len; + + return (chksum == 0); +} + +/* + * Compute checksum as two's complement + * excluding prefix $ and add CR/LF at the end + */ + +static int insert_chksum(char *buf, unsigned int len) +{ + uint16_t chksum = 0; + unsigned int i; + + /* Skip first char */ + for (i = 1; i < len; i++) + chksum += buf[i]; + chksum = (~chksum + 1); + sprintf(&buf[len], "%02X", chksum & 0xFF); + len += 2; + + buf[len++] = '\r'; + buf[len++] = '\n'; + + return len; +} + +static int set_uart (int fd) +{ + struct termios tty; + + if (tcgetattr (fd, &tty) < 0) { + printf ("Error from tcgetattr: %s\n", strerror (errno)); + return -1; + } + + cfsetospeed (&tty, (speed_t) B115200); + cfsetispeed (&tty, (speed_t) B115200); + + tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */ + tty.c_cflag &= ~CSIZE; + tty.c_cflag |= CS8; /* 8-bit characters */ + tty.c_cflag &= ~PARENB; /* no parity bit */ + tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ + tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */ + + /* setup for non-canonical mode */ + tty.c_iflag &= + ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + tty.c_iflag |= (IGNBRK | IGNPAR); + tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | ICANON | ISIG | IEXTEN); + tty.c_oflag &= ~(OPOST | ONLCR); + + /* fetch bytes as they become available */ + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 1; + + if (tcsetattr (fd, TCSANOW, &tty) != 0) { + printf ("Error from tcsetattr: %s\n", strerror (errno)); + return -1; + } + + tcflush(fd, TCIFLUSH); + tcflush(fd, TCOFLUSH); + + return 0; +} + +/* + * This is just for debug purposes + */ +static void dump_ascii(bool rxdir, char *buf, int count) +{ + int i; + char *outbuf = (char *)malloc(count + 40); + char *tmp = outbuf; + int len; + + if (!outbuf) + return; + + len = sprintf(tmp, "%cX: %d bytes:", + rxdir ? 'R' : 'T', + count); + tmp += len; + for (i=0; i < count; i++) { + sprintf(tmp, "%c", buf[i]); + tmp++; + } + + TRACE("%s", outbuf); + + free(outbuf); +} + +static int receive_msg(int fd, char *rx, size_t size, + unsigned int timeout, bool debug) +{ + fd_set fds; + struct timeval tv; + int ret; + unsigned int count; + + /* Initialize structures for select */ + FD_ZERO(&fds); + FD_SET(fd, &fds); + + /* + * Microcontrolle answers very fast, + * Timeout is just to take care if no answer is + * sent + */ + tv.tv_sec = timeout; + tv.tv_usec = 0; /* Check for not more as 10 mSec */ + + ret = select(fd + 1, &fds, NULL, NULL, &tv); + if (ret == 0) { + ERROR("Timeout, no answer from microcontroller"); + return -EPROTO; + } + + ret = read(fd, rx, size); + if (ret < 3) { + ERROR("Error in read: %d", ret); + return -EBADMSG; + } + count = ret; + + if (debug) + dump_ascii(true, rx, count); + + /* + * Try some syntax check + */ + if (rx[0] != '$') { + ERROR("First byte is not '$' but '%c'", rx[0]); + return -EBADMSG; + } + + if (!verify_chksum(rx, &count)) { + return -EBADMSG; + } + + return 0; +} + +static int write_data(int fd, char *buf, size_t size) +{ + int written; + written = write(fd, buf, size); + if (written != size) { + ERROR("Error in write %d", written); + return -EFAULT; + } + + return 0; +} + +static int write_msg(int fd, const char *msg) +{ + int len, ret; + char *buf; + + buf = strdup(msg); + + len = insert_chksum(buf, strlen(buf)); + ret = write_data(fd, buf, len); + free(buf); + return ret; +} + +static int prepare_update(struct handler_priv *priv, + struct img_type *img) +{ + int ret; + char msg[128]; + int len; + + ret = switch_mode(priv->reset.gpiodev, priv->reset.offset, + priv->prog.gpiodev, priv->prog.offset, MODE_PROG); + if (ret < 0) { + return -ENODEV; + } + + DEBUG("Using %s", img->device); + + priv->fduart = open(img->device, O_RDWR); + + if (priv->fduart < 0) { + ERROR("Cannot open UART %s", img->device); + return -ENODEV; + } + + set_uart(priv->fduart); + + /* No FW data to be sent */ + priv->nbytes = 0; + + write_msg(priv->fduart, "$PROG;"); + + len = receive_msg(priv->fduart, msg, sizeof(msg), priv->timeout, priv->debug); + if (len < 0 || strcmp(msg, "$READY;")) + return -EBADMSG; + + return 0; +} + +static int update_fw(void *data, const void *buffer, unsigned int size) +{ + int cnt = 0; + char c; + int ret; + char msg[80]; + struct handler_priv *priv = (struct handler_priv *)data; + const char *buf = (const char *)buffer; + + while (size > 0) { + c = buf[cnt++]; + priv->buf[priv->nbytes++] = c; + size--; + if (c == '\n') { + /* Send data */ + if (priv->debug) + dump_ascii(false, priv->buf, priv->nbytes); + ret = write_data(priv->fduart, priv->buf, priv->nbytes); + if (ret < 0) + return ret; + priv->buf[priv->nbytes] = '\0'; + msg[0] = '\0'; + ret = receive_msg(priv->fduart, msg, sizeof(msg), + priv->timeout, priv->debug); + priv->nbytes = 0; + if (ret < 0) { + return ret; + } + if (!strcmp(msg, "$READY;")) + continue; + if (!strcmp(msg, "$COMPLETED;")) + return 0; + } + } + return 0; +} + +static int finish_update(struct handler_priv *priv) +{ + int ret; + + close(priv->fduart); + ret = switch_mode(priv->reset.gpiodev, priv->reset.offset, + priv->prog.gpiodev, priv->prog.offset, MODE_NORMAL); + if (ret < 0) { + return -ENODEV; + } + return 0; +} + +static int get_gpio_from_property(struct dict_list *prop, struct mode_setup *gpio) +{ + struct dict_list_elem *entry; + int i; + + memset(gpio, 0, sizeof(*gpio)); + + LIST_FOREACH(entry, prop, next) { + char *s = strdup(entry->value); + for (i = 0; i < 3; i++) { + char *t = strchr(s, ':'); + + if (t) *t = '\0'; + switch (i) { + case 0: + strncpy(gpio->gpiodev, s, sizeof(gpio->gpiodev)); + break; + case 1: + errno = 0; + gpio->offset = strtoul(s, NULL, 10); + if (errno == EINVAL) + return -EINVAL; + break; + case 2: + if (!strcmp(s, "true")) + gpio->active_low = true; + break; + } + + if (!t) + break; + s = ++t; + } + } + + return 0; +} + +static int install_uc_firmware_image(struct img_type *img, + void __attribute__ ((__unused__)) *data) +{ + struct handler_priv hnd_data; + struct dict_list *properties; + struct dict_list_elem *entry; + int cnt, ret = 0; + struct mode_setup *gpio; + + memset(&hnd_data, 0, sizeof(hnd_data)); + hnd_data.timeout = DEFAULT_TIMEOUT; + + const char *properties_list[] = { "reset", "prog"}; + + for (cnt = 0; cnt < ARRAY_SIZE(properties_list); cnt++) { + /* + * Getting GPIOs from sw-description + */ + properties = dict_get_list(&img->properties, + properties_list[cnt]); + if (!properties) { + ERROR("MIssing setup for %s GPIO", properties_list[cnt]); + return -EINVAL; + } + + gpio = (cnt == 0) ? &hnd_data.reset : &hnd_data.prog; + + get_gpio_from_property(properties, gpio); + DEBUG("line %s : device %s, num = %d, active_low = %s", + properties_list[cnt], + gpio->gpiodev, + gpio->offset, + gpio->active_low ? "true" : "false"); + } + + properties = dict_get_list(&img->properties, "debug"); + if (properties) { + entry = LIST_FIRST(properties); + if (entry && !strcmp(entry->value, "true")) + hnd_data.debug = true; + } + + properties = dict_get_list(&img->properties, "timeout"); + if (properties) { + entry = LIST_FIRST(properties); + if (entry && (strtoul(entry->value, NULL, 10) > 0)) + hnd_data.timeout = strtoul(entry->value, NULL, 10); + } + + ret = prepare_update(&hnd_data, img); + if (ret) { + ERROR("Prepare failed !!"); + goto handler_exit; + } + + ret = copyimage(&hnd_data, img, update_fw); + if (ret) { + ERROR("Transferring image to uController was not successful"); + goto handler_exit; + } + +handler_exit: + + + finish_update(&hnd_data); + return ret; +} + +__attribute__((constructor)) +void ucfw_handler(void) +{ + register_handler("ucfw", install_uc_firmware_image, + IMAGE_HANDLER, NULL); +}