mirror of
https://gitlab.com/zephray/glider.git
synced 2024-11-10 11:17:54 +00:00
Check in waveform tools
This commit is contained in:
parent
6cccb21440
commit
a8f35f2604
15 changed files with 2292 additions and 0 deletions
|
@ -887,6 +887,11 @@ Here is a list of helpful references related to driving EPDs:
|
||||||
* An early tool for reading Eink's wbf file format: https://github.com/fread-ink/inkwave
|
* An early tool for reading Eink's wbf file format: https://github.com/fread-ink/inkwave
|
||||||
* A more up-to-date Eink's wbf format parser: https://patchwork.kernel.org/project/linux-arm-kernel/patch/20220413221916.50995-2-samuel@sholland.org/
|
* A more up-to-date Eink's wbf format parser: https://patchwork.kernel.org/project/linux-arm-kernel/patch/20220413221916.50995-2-samuel@sholland.org/
|
||||||
|
|
||||||
|
On the topic of color screen resolution, color mapping, subpixel rendering:
|
||||||
|
|
||||||
|
* Klompenhouwer, Michiel A., and Erno H. A. Langendijk. “59.4: Comparing the Effective Resolution of Various RGB Subpixel Layouts.” SID International Symposium Digest of Technical Papers, vol. 39, no. 1, 2008, pp. 907–10, https://doi.org/10.1889/1.3069822.
|
||||||
|
* Lai, Chih-chang, and Ching-chih Tsai. “A Modified Stripe-RGBW TFT-LCD with Image-Processing Engine for Mobile Phone Displays.” IEEE Transactions on Consumer Electronics, vol. 53, no. 4, 2007, pp. 1628–33, https://doi.org/10.1109/TCE.2007.4429262.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This document, other than refereneces explicitly given with their corresponding license, is released into public domain.
|
This document, other than refereneces explicitly given with their corresponding license, is released into public domain.
|
||||||
|
|
7
utils/mxc_waveform_asm/Makefile
Normal file
7
utils/mxc_waveform_asm/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
all: mxc_wvfm_asm
|
||||||
|
|
||||||
|
mxc_wvfm_asm: main.c ini.c csv.c fread_csv_line.c split.c
|
||||||
|
gcc -O1 -g main.c ini.c csv.c fread_csv_line.c split.c -o mxc_wvfm_asm
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f mxc_wvfm_asm
|
143
utils/mxc_waveform_asm/csv.c
Normal file
143
utils/mxc_waveform_asm/csv.c
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void free_csv_line( char **parsed ) {
|
||||||
|
char **ptr;
|
||||||
|
|
||||||
|
for ( ptr = parsed; *ptr; ptr++ ) {
|
||||||
|
free( *ptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
free( parsed );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int count_fields( const char *line ) {
|
||||||
|
const char *ptr;
|
||||||
|
int cnt, fQuote;
|
||||||
|
|
||||||
|
for ( cnt = 1, fQuote = 0, ptr = line; *ptr; ptr++ ) {
|
||||||
|
if ( fQuote ) {
|
||||||
|
if ( *ptr == '\"' ) {
|
||||||
|
if ( ptr[1] == '\"' ) {
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fQuote = 0;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( *ptr ) {
|
||||||
|
case '\"':
|
||||||
|
fQuote = 1;
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
cnt++;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( fQuote ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a string containing no linebreaks, or containing line breaks
|
||||||
|
* which are escaped by "double quotes", extract a NULL-terminated
|
||||||
|
* array of strings, one for every cell in the row.
|
||||||
|
*/
|
||||||
|
char **parse_csv( const char *line ) {
|
||||||
|
char **buf, **bptr, *tmp, *tptr;
|
||||||
|
const char *ptr;
|
||||||
|
int fieldcnt, fQuote, fEnd;
|
||||||
|
|
||||||
|
fieldcnt = count_fields( line );
|
||||||
|
|
||||||
|
if ( fieldcnt == -1 ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc( sizeof(char*) * (fieldcnt+1) );
|
||||||
|
|
||||||
|
if ( !buf ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = malloc( strlen(line) + 1 );
|
||||||
|
|
||||||
|
if ( !tmp ) {
|
||||||
|
free( buf );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bptr = buf;
|
||||||
|
|
||||||
|
for ( ptr = line, fQuote = 0, *tmp = '\0', tptr = tmp, fEnd = 0; ; ptr++ ) {
|
||||||
|
if ( fQuote ) {
|
||||||
|
if ( !*ptr ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( *ptr == '\"' ) {
|
||||||
|
if ( ptr[1] == '\"' ) {
|
||||||
|
*tptr++ = '\"';
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fQuote = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*tptr++ = *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( *ptr ) {
|
||||||
|
case '\"':
|
||||||
|
fQuote = 1;
|
||||||
|
continue;
|
||||||
|
case '\0':
|
||||||
|
fEnd = 1;
|
||||||
|
case ',':
|
||||||
|
*tptr = '\0';
|
||||||
|
*bptr = strdup( tmp );
|
||||||
|
|
||||||
|
if ( !*bptr ) {
|
||||||
|
for ( bptr--; bptr >= buf; bptr-- ) {
|
||||||
|
free( *bptr );
|
||||||
|
}
|
||||||
|
free( buf );
|
||||||
|
free( tmp );
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bptr++;
|
||||||
|
tptr = tmp;
|
||||||
|
|
||||||
|
if ( fEnd ) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
*tptr++ = *ptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( fEnd ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*bptr = NULL;
|
||||||
|
free( tmp );
|
||||||
|
return buf;
|
||||||
|
}
|
12
utils/mxc_waveform_asm/csv.h
Normal file
12
utils/mxc_waveform_asm/csv.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef CSV_DOT_H_INCLUDE_GUARD
|
||||||
|
#define CSV_DOT_H_INCLUDE_GUARD
|
||||||
|
|
||||||
|
#define CSV_ERR_LONGLINE 0
|
||||||
|
#define CSV_ERR_NO_MEMORY 1
|
||||||
|
|
||||||
|
char **parse_csv( const char *line );
|
||||||
|
void free_csv_line( char **parsed );
|
||||||
|
char **split_on_unescaped_newlines(const char *txt);
|
||||||
|
char *fread_csv_line(FILE *fp, int max_line_size, int *done, int *err, int rst);
|
||||||
|
|
||||||
|
#endif
|
107
utils/mxc_waveform_asm/fread_csv_line.c
Normal file
107
utils/mxc_waveform_asm/fread_csv_line.c
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "csv.h"
|
||||||
|
|
||||||
|
#define READ_BLOCK_SIZE 65536
|
||||||
|
#define QUICK_GETC( ch, fp )\
|
||||||
|
do\
|
||||||
|
{\
|
||||||
|
if ( read_ptr == read_end ) {\
|
||||||
|
fread_len = fread( read_buf, sizeof(char), READ_BLOCK_SIZE, fp );\
|
||||||
|
if ( fread_len < READ_BLOCK_SIZE ) {\
|
||||||
|
read_buf[fread_len] = '\0';\
|
||||||
|
}\
|
||||||
|
read_ptr = read_buf;\
|
||||||
|
}\
|
||||||
|
ch = *read_ptr++;\
|
||||||
|
}\
|
||||||
|
while(0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a file pointer, read a CSV line from that file.
|
||||||
|
* File may include newlines escaped with "double quotes".
|
||||||
|
*
|
||||||
|
* Warning: This function is optimized for the use case where
|
||||||
|
* you repeatedly call it until the file is exhausted. It is
|
||||||
|
* very suboptimal for the use case of just grabbing one single
|
||||||
|
* line of CSV and stopping. Also, this function advances the
|
||||||
|
* file position (in the fseek/ftell sense) unpredictably. You
|
||||||
|
* should not change the file position between calls to
|
||||||
|
* fread_csv_line (e.g., don't use "getc" on the file in between
|
||||||
|
* calls to fread_csv_line).
|
||||||
|
*
|
||||||
|
* Other arguments:
|
||||||
|
* size_t max_line_size: Maximum line size, in bytes.
|
||||||
|
* int *done: Pointer to an int that will be set to 1 when file is exhausted.
|
||||||
|
* int *err: Pointer to an int where error code will be written.
|
||||||
|
*
|
||||||
|
* Warning: Calling this function on an exhausted file (as indicated by the
|
||||||
|
* 'done' flag) is undefined behavior.
|
||||||
|
*
|
||||||
|
* See csv.h for definitions of error codes.
|
||||||
|
*/
|
||||||
|
char *fread_csv_line(FILE *fp, int max_line_size, int *done, int *err, int rst) {
|
||||||
|
static FILE *bookmark;
|
||||||
|
static char read_buf[READ_BLOCK_SIZE], *read_ptr, *read_end;
|
||||||
|
static int fread_len, prev_max_line_size = -1;
|
||||||
|
static char *buf;
|
||||||
|
char *bptr, *limit;
|
||||||
|
char ch;
|
||||||
|
int fQuote;
|
||||||
|
|
||||||
|
if ( max_line_size > prev_max_line_size ) {
|
||||||
|
if ( prev_max_line_size != -1 ) {
|
||||||
|
free( buf );
|
||||||
|
}
|
||||||
|
buf = malloc( max_line_size + 1 );
|
||||||
|
if ( !buf ) {
|
||||||
|
*err = CSV_ERR_NO_MEMORY;
|
||||||
|
prev_max_line_size = -1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
prev_max_line_size = max_line_size;
|
||||||
|
}
|
||||||
|
bptr = buf;
|
||||||
|
limit = buf + max_line_size;
|
||||||
|
|
||||||
|
if (( bookmark != fp ) || ( rst )) {
|
||||||
|
read_ptr = read_end = read_buf + READ_BLOCK_SIZE;
|
||||||
|
bookmark = fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( fQuote = 0; ; ) {
|
||||||
|
QUICK_GETC(ch, fp);
|
||||||
|
|
||||||
|
if ( !ch || (ch == '\n' && !fQuote)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bptr >= limit ) {
|
||||||
|
free( buf );
|
||||||
|
*err = CSV_ERR_LONGLINE;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
*bptr++ = ch;
|
||||||
|
|
||||||
|
if ( fQuote ) {
|
||||||
|
if ( ch == '\"' ) {
|
||||||
|
QUICK_GETC(ch, fp);
|
||||||
|
|
||||||
|
if ( ch != '\"' ) {
|
||||||
|
if ( !ch || ch == '\n' ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fQuote = 0;
|
||||||
|
}
|
||||||
|
*bptr++ = ch;
|
||||||
|
}
|
||||||
|
} else if ( ch == '\"' ) {
|
||||||
|
fQuote = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*done = !ch;
|
||||||
|
*bptr = '\0';
|
||||||
|
return strdup(buf);
|
||||||
|
}
|
298
utils/mxc_waveform_asm/ini.c
Normal file
298
utils/mxc_waveform_asm/ini.c
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
/* inih -- simple .INI file parser
|
||||||
|
|
||||||
|
SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
Copyright (C) 2009-2020, Ben Hoyt
|
||||||
|
|
||||||
|
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||||
|
home page for more info:
|
||||||
|
|
||||||
|
https://github.com/benhoyt/inih
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ini.h"
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
#if INI_CUSTOM_ALLOCATOR
|
||||||
|
#include <stddef.h>
|
||||||
|
void* ini_malloc(size_t size);
|
||||||
|
void ini_free(void* ptr);
|
||||||
|
void* ini_realloc(void* ptr, size_t size);
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define ini_malloc malloc
|
||||||
|
#define ini_free free
|
||||||
|
#define ini_realloc realloc
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_SECTION 50
|
||||||
|
#define MAX_NAME 50
|
||||||
|
|
||||||
|
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||||
|
typedef struct {
|
||||||
|
const char* ptr;
|
||||||
|
size_t num_left;
|
||||||
|
} ini_parse_string_ctx;
|
||||||
|
|
||||||
|
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||||
|
static char* rstrip(char* s)
|
||||||
|
{
|
||||||
|
char* p = s + strlen(s);
|
||||||
|
while (p > s && isspace((unsigned char)(*--p)))
|
||||||
|
*p = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return pointer to first non-whitespace char in given string. */
|
||||||
|
static char* lskip(const char* s)
|
||||||
|
{
|
||||||
|
while (*s && isspace((unsigned char)(*s)))
|
||||||
|
s++;
|
||||||
|
return (char*)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||||
|
or pointer to NUL at end of string if neither found. Inline comment must
|
||||||
|
be prefixed by a whitespace character to register as a comment. */
|
||||||
|
static char* find_chars_or_comment(const char* s, const char* chars)
|
||||||
|
{
|
||||||
|
#if INI_ALLOW_INLINE_COMMENTS
|
||||||
|
int was_space = 0;
|
||||||
|
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||||
|
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||||
|
was_space = isspace((unsigned char)(*s));
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
while (*s && (!chars || !strchr(chars, *s))) {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return (char*)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Similar to strncpy, but ensures dest (size bytes) is
|
||||||
|
NUL-terminated, and doesn't pad with NULs. */
|
||||||
|
static char* strncpy0(char* dest, const char* src, size_t size)
|
||||||
|
{
|
||||||
|
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < size - 1 && src[i]; i++)
|
||||||
|
dest[i] = src[i];
|
||||||
|
dest[i] = '\0';
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||||
|
void* user)
|
||||||
|
{
|
||||||
|
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||||
|
#if INI_USE_STACK
|
||||||
|
char line[INI_MAX_LINE];
|
||||||
|
int max_line = INI_MAX_LINE;
|
||||||
|
#else
|
||||||
|
char* line;
|
||||||
|
size_t max_line = INI_INITIAL_ALLOC;
|
||||||
|
#endif
|
||||||
|
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||||
|
char* new_line;
|
||||||
|
size_t offset;
|
||||||
|
#endif
|
||||||
|
char section[MAX_SECTION] = "";
|
||||||
|
char prev_name[MAX_NAME] = "";
|
||||||
|
|
||||||
|
char* start;
|
||||||
|
char* end;
|
||||||
|
char* name;
|
||||||
|
char* value;
|
||||||
|
int lineno = 0;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
line = (char*)ini_malloc(INI_INITIAL_ALLOC);
|
||||||
|
if (!line) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if INI_HANDLER_LINENO
|
||||||
|
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||||
|
#else
|
||||||
|
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Scan through stream line by line */
|
||||||
|
while (reader(line, (int)max_line, stream) != NULL) {
|
||||||
|
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||||
|
offset = strlen(line);
|
||||||
|
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||||
|
max_line *= 2;
|
||||||
|
if (max_line > INI_MAX_LINE)
|
||||||
|
max_line = INI_MAX_LINE;
|
||||||
|
new_line = ini_realloc(line, max_line);
|
||||||
|
if (!new_line) {
|
||||||
|
ini_free(line);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
line = new_line;
|
||||||
|
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
|
||||||
|
break;
|
||||||
|
if (max_line >= INI_MAX_LINE)
|
||||||
|
break;
|
||||||
|
offset += strlen(line + offset);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lineno++;
|
||||||
|
|
||||||
|
start = line;
|
||||||
|
#if INI_ALLOW_BOM
|
||||||
|
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||||
|
(unsigned char)start[1] == 0xBB &&
|
||||||
|
(unsigned char)start[2] == 0xBF) {
|
||||||
|
start += 3;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
start = lskip(rstrip(start));
|
||||||
|
|
||||||
|
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||||
|
/* Start-of-line comment */
|
||||||
|
}
|
||||||
|
#if INI_ALLOW_MULTILINE
|
||||||
|
else if (*prev_name && *start && start > line) {
|
||||||
|
/* Non-blank line with leading whitespace, treat as continuation
|
||||||
|
of previous name's value (as per Python configparser). */
|
||||||
|
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if (*start == '[') {
|
||||||
|
/* A "[section]" line */
|
||||||
|
end = find_chars_or_comment(start + 1, "]");
|
||||||
|
if (*end == ']') {
|
||||||
|
*end = '\0';
|
||||||
|
strncpy0(section, start + 1, sizeof(section));
|
||||||
|
*prev_name = '\0';
|
||||||
|
#if INI_CALL_HANDLER_ON_NEW_SECTION
|
||||||
|
if (!HANDLER(user, section, NULL, NULL) && !error)
|
||||||
|
error = lineno;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (!error) {
|
||||||
|
/* No ']' found on section line */
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*start) {
|
||||||
|
/* Not a comment, must be a name[=:]value pair */
|
||||||
|
end = find_chars_or_comment(start, "=:");
|
||||||
|
if (*end == '=' || *end == ':') {
|
||||||
|
*end = '\0';
|
||||||
|
name = rstrip(start);
|
||||||
|
value = end + 1;
|
||||||
|
#if INI_ALLOW_INLINE_COMMENTS
|
||||||
|
end = find_chars_or_comment(value, NULL);
|
||||||
|
if (*end)
|
||||||
|
*end = '\0';
|
||||||
|
#endif
|
||||||
|
value = lskip(value);
|
||||||
|
rstrip(value);
|
||||||
|
|
||||||
|
/* Valid name[=:]value pair found, call handler */
|
||||||
|
strncpy0(prev_name, name, sizeof(prev_name));
|
||||||
|
if (!HANDLER(user, section, name, value) && !error)
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
else if (!error) {
|
||||||
|
/* No '=' or ':' found on name[=:]value line */
|
||||||
|
#if INI_ALLOW_NO_VALUE
|
||||||
|
*end = '\0';
|
||||||
|
name = rstrip(start);
|
||||||
|
if (!HANDLER(user, section, name, NULL) && !error)
|
||||||
|
error = lineno;
|
||||||
|
#else
|
||||||
|
error = lineno;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if INI_STOP_ON_FIRST_ERROR
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
ini_free(line);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||||
|
{
|
||||||
|
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||||
|
{
|
||||||
|
FILE* file;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
file = fopen(filename, "r");
|
||||||
|
if (!file)
|
||||||
|
return -1;
|
||||||
|
error = ini_parse_file(file, handler, user);
|
||||||
|
fclose(file);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An ini_reader function to read the next line from a string buffer. This
|
||||||
|
is the fgets() equivalent used by ini_parse_string(). */
|
||||||
|
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||||
|
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||||
|
const char* ctx_ptr = ctx->ptr;
|
||||||
|
size_t ctx_num_left = ctx->num_left;
|
||||||
|
char* strp = str;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (ctx_num_left == 0 || num < 2)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (num > 1 && ctx_num_left != 0) {
|
||||||
|
c = *ctx_ptr++;
|
||||||
|
ctx_num_left--;
|
||||||
|
*strp++ = c;
|
||||||
|
if (c == '\n')
|
||||||
|
break;
|
||||||
|
num--;
|
||||||
|
}
|
||||||
|
|
||||||
|
*strp = '\0';
|
||||||
|
ctx->ptr = ctx_ptr;
|
||||||
|
ctx->num_left = ctx_num_left;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||||
|
ini_parse_string_ctx ctx;
|
||||||
|
|
||||||
|
ctx.ptr = string;
|
||||||
|
ctx.num_left = strlen(string);
|
||||||
|
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||||
|
user);
|
||||||
|
}
|
150
utils/mxc_waveform_asm/ini.h
Normal file
150
utils/mxc_waveform_asm/ini.h
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/* inih -- simple .INI file parser
|
||||||
|
SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
Copyright (C) 2009-2020, Ben Hoyt
|
||||||
|
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||||
|
home page for more info:
|
||||||
|
https://github.com/benhoyt/inih
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INI_H
|
||||||
|
#define INI_H
|
||||||
|
|
||||||
|
/* Make this header file easier to include in C++ code */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||||
|
#ifndef INI_HANDLER_LINENO
|
||||||
|
#define INI_HANDLER_LINENO 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Typedef for prototype of handler function. */
|
||||||
|
#if INI_HANDLER_LINENO
|
||||||
|
typedef int (*ini_handler)(void* user, const char* section,
|
||||||
|
const char* name, const char* value,
|
||||||
|
int lineno);
|
||||||
|
#else
|
||||||
|
typedef int (*ini_handler)(void* user, const char* section,
|
||||||
|
const char* name, const char* value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Typedef for prototype of fgets-style reader function. */
|
||||||
|
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||||
|
|
||||||
|
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||||
|
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||||
|
is "" if name=value pair parsed before any section heading. name:value
|
||||||
|
pairs are also supported as a concession to Python's configparser.
|
||||||
|
For each name=value pair parsed, call handler function with given user
|
||||||
|
pointer as well as section, name, and value (data only valid for duration
|
||||||
|
of handler call). Handler should return nonzero on success, zero on error.
|
||||||
|
Returns 0 on success, line number of first error on parse error (doesn't
|
||||||
|
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||||
|
error (only when INI_USE_STACK is zero).
|
||||||
|
*/
|
||||||
|
int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||||
|
close the file when it's finished -- the caller must do that. */
|
||||||
|
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||||
|
filename. Used for implementing custom or string-based I/O (see also
|
||||||
|
ini_parse_string). */
|
||||||
|
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||||
|
void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||||
|
instead of a file. Useful for parsing INI data from a network socket or
|
||||||
|
already in memory. */
|
||||||
|
int ini_parse_string(const char* string, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||||
|
configparser. If allowed, ini_parse() will call the handler with the same
|
||||||
|
name for each subsequent line parsed. */
|
||||||
|
#ifndef INI_ALLOW_MULTILINE
|
||||||
|
#define INI_ALLOW_MULTILINE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||||
|
the file. See https://github.com/benhoyt/inih/issues/21 */
|
||||||
|
#ifndef INI_ALLOW_BOM
|
||||||
|
#define INI_ALLOW_BOM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||||
|
both ; and # comments at the start of a line by default. */
|
||||||
|
#ifndef INI_START_COMMENT_PREFIXES
|
||||||
|
#define INI_START_COMMENT_PREFIXES ";#"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||||
|
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||||
|
Python 3.2+ configparser behaviour. */
|
||||||
|
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||||
|
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||||
|
#endif
|
||||||
|
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||||
|
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||||
|
#ifndef INI_USE_STACK
|
||||||
|
#define INI_USE_STACK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||||
|
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||||
|
#ifndef INI_MAX_LINE
|
||||||
|
#define INI_MAX_LINE 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||||
|
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||||
|
zero. */
|
||||||
|
#ifndef INI_ALLOW_REALLOC
|
||||||
|
#define INI_ALLOW_REALLOC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||||
|
is zero. */
|
||||||
|
#ifndef INI_INITIAL_ALLOC
|
||||||
|
#define INI_INITIAL_ALLOC 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Stop parsing on first error (default is to keep parsing). */
|
||||||
|
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||||
|
#define INI_STOP_ON_FIRST_ERROR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to call the handler at the start of each new section (with
|
||||||
|
name and value NULL). Default is to only call the handler on
|
||||||
|
each name=value pair. */
|
||||||
|
#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
|
||||||
|
#define INI_CALL_HANDLER_ON_NEW_SECTION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
|
||||||
|
call the handler with value NULL in this case. Default is to treat
|
||||||
|
no-value lines as an error. */
|
||||||
|
#ifndef INI_ALLOW_NO_VALUE
|
||||||
|
#define INI_ALLOW_NO_VALUE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory
|
||||||
|
allocation functions (INI_USE_STACK must also be 0). These functions must
|
||||||
|
have the same signatures as malloc/free/realloc and behave in a similar
|
||||||
|
way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */
|
||||||
|
#ifndef INI_CUSTOM_ALLOCATOR
|
||||||
|
#define INI_CUSTOM_ALLOCATOR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* INI_H */
|
447
utils/mxc_waveform_asm/main.c
Normal file
447
utils/mxc_waveform_asm/main.c
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Freescale/NXP i.MX EPDC waveform assembler
|
||||||
|
*
|
||||||
|
* This tools converts human readable .csv waveform file into .fw file used
|
||||||
|
* by i.MX EPDC driver.
|
||||||
|
*
|
||||||
|
* Copyright 2021 Wenting Zhang
|
||||||
|
*
|
||||||
|
* This 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 2 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* This software 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
|
||||||
|
* the software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file is partially derived from Linux kernel driver, with the following
|
||||||
|
* copyright information:
|
||||||
|
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
|
||||||
|
* Copyright 2017 NXP
|
||||||
|
*
|
||||||
|
* inih is used in this project, which is licensed under BSD-3-Clause.
|
||||||
|
* csv_parser is used in this project, which is licensed under MIT.
|
||||||
|
******************************************************************************/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "ini.h"
|
||||||
|
#include "csv.h"
|
||||||
|
|
||||||
|
#define MAX_MODES (32) // Maximum waveform modes supported
|
||||||
|
#define MAX_TEMPS (32) // Maximum temperature ranges supported
|
||||||
|
|
||||||
|
#define MAX_CSV_LINE (1024)
|
||||||
|
|
||||||
|
#define GREYSCALE_BPP (2)
|
||||||
|
#define GREYSCALE_LEVEL (16)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int wi0;
|
||||||
|
unsigned int wi1;
|
||||||
|
unsigned int wi2;
|
||||||
|
unsigned int wi3;
|
||||||
|
unsigned int wi4;
|
||||||
|
unsigned int wi5;
|
||||||
|
unsigned int wi6;
|
||||||
|
|
||||||
|
unsigned int xwia: 24; // address of extra waveform information
|
||||||
|
unsigned int cs1: 8; // checksum 1
|
||||||
|
|
||||||
|
unsigned int wmta: 24;
|
||||||
|
unsigned int fvsn: 8;
|
||||||
|
unsigned int luts: 8;
|
||||||
|
unsigned int mc: 8; // mode count
|
||||||
|
unsigned int trc: 8; // temperature range count
|
||||||
|
unsigned int advanced_wfm_flags: 8;
|
||||||
|
unsigned int eb: 8;
|
||||||
|
unsigned int sb: 8;
|
||||||
|
unsigned int reserved0_1: 8;
|
||||||
|
unsigned int reserved0_2: 8;
|
||||||
|
unsigned int reserved0_3: 8;
|
||||||
|
unsigned int reserved0_4: 8;
|
||||||
|
unsigned int reserved0_5: 8;
|
||||||
|
unsigned int cs2: 8; // checksum 2
|
||||||
|
} waveform_data_header_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
waveform_data_header_t wdh;
|
||||||
|
uint8_t data[]; /* Temperature Range Table + Waveform Data */
|
||||||
|
} waveform_data_file_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *prefix;
|
||||||
|
int modes;
|
||||||
|
char **mode_names;
|
||||||
|
int *frame_counts; // frame_counts[mode * temps + temp]
|
||||||
|
int temps;
|
||||||
|
int *temp_ranges;
|
||||||
|
uint8_t ***luts; // luts[mode][temp][frame count * 256 + dst * 16 + src]
|
||||||
|
} context_t;
|
||||||
|
|
||||||
|
static int ini_parser_handler(void* user, const char* section, const char* name,
|
||||||
|
const char* value) {
|
||||||
|
context_t* pcontext = (context_t*)user;
|
||||||
|
|
||||||
|
if (strcmp(section, "WAVEFORM") == 0) {
|
||||||
|
if (strcmp(name, "VERSION") == 0) {
|
||||||
|
assert(strcmp(value, "1.0") == 0);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "PREFIX") == 0) {
|
||||||
|
pcontext->prefix = strdup(value);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "MODES") == 0) {
|
||||||
|
// Allocate memory for modes
|
||||||
|
pcontext->modes = atoi(value);
|
||||||
|
assert(pcontext->modes <= MAX_MODES);
|
||||||
|
pcontext->mode_names = malloc(sizeof(char*) * pcontext->modes);
|
||||||
|
assert(pcontext->mode_names);
|
||||||
|
}
|
||||||
|
else if (strcmp(name, "TEMPS") == 0) {
|
||||||
|
// Allocate memory for temp ranges
|
||||||
|
pcontext->temps = atoi(value);
|
||||||
|
assert(pcontext->temps <= MAX_TEMPS);
|
||||||
|
pcontext->temp_ranges = malloc(sizeof(int) * pcontext->temps);
|
||||||
|
assert(pcontext->temp_ranges);
|
||||||
|
pcontext->frame_counts = malloc(sizeof(int) * pcontext->modes *
|
||||||
|
pcontext->temps);
|
||||||
|
assert(pcontext->frame_counts);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size_t len = strlen(name);
|
||||||
|
if ((len >= 7) && (name[0] == 'T') &&
|
||||||
|
(strncmp(name + (len - 5), "RANGE", 5) == 0)) {
|
||||||
|
// Temperature Range
|
||||||
|
char *temp_id_s = strdup(name);
|
||||||
|
temp_id_s[len - 5] = '\0';
|
||||||
|
int temp_id = atoi(temp_id_s + 1);
|
||||||
|
free(temp_id_s);
|
||||||
|
pcontext->temp_ranges[temp_id] = atoi(value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Unknown name %s=%s\n", name, value);
|
||||||
|
return 0; // Unknown name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strncmp(section, "MODE", 4) == 0) {
|
||||||
|
int mode_id = atoi(section + 4);
|
||||||
|
assert(mode_id >= 0);
|
||||||
|
assert(mode_id < pcontext->modes);
|
||||||
|
size_t len = strlen(name);
|
||||||
|
if (strcmp(name, "NAME") == 0) {
|
||||||
|
// Mode Name
|
||||||
|
pcontext->mode_names[mode_id] = strdup(value);
|
||||||
|
}
|
||||||
|
else if ((len >= 4) && (name[0] == 'T') &&
|
||||||
|
(strncmp(name + (len - 2), "FC", 2) == 0)) {
|
||||||
|
// Frame Count
|
||||||
|
char *temp_id_s = strdup(name);
|
||||||
|
temp_id_s[len - 2] = '\0';
|
||||||
|
int temp_id = atoi(temp_id_s + 1);
|
||||||
|
free(temp_id_s);
|
||||||
|
pcontext->frame_counts[pcontext->temps * mode_id + temp_id]
|
||||||
|
= atoi(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Unknown section %s\n", section);
|
||||||
|
return 0; // Unknown section
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_uint64_le(uint8_t* dst, uint64_t val) {
|
||||||
|
dst[7] = (val >> 56) & 0xff;
|
||||||
|
dst[6] = (val >> 48) & 0xff;
|
||||||
|
dst[5] = (val >> 40) & 0xff;
|
||||||
|
dst[4] = (val >> 32) & 0xff;
|
||||||
|
dst[3] = (val >> 24) & 0xff;
|
||||||
|
dst[2] = (val >> 16) & 0xff;
|
||||||
|
dst[1] = (val >> 8) & 0xff;
|
||||||
|
dst[0] = (val) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_range(const char* str, int* begin, int* end) {
|
||||||
|
// Parse range specified in the waveform.
|
||||||
|
// Example:
|
||||||
|
// 2 - 2 to 2
|
||||||
|
// 0:15 - 0 to 15
|
||||||
|
// 4:7 - 4 to 7
|
||||||
|
char* delim = strchr(str, ':');
|
||||||
|
if (delim) {
|
||||||
|
*begin = atoi(str);
|
||||||
|
*end = atoi(delim + 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*begin = atoi(str);
|
||||||
|
*end = *begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_waveform_csv(const char* filename, int frame_count,
|
||||||
|
uint8_t* lut) {
|
||||||
|
FILE* fp = fopen(filename, "r");
|
||||||
|
assert(fp);
|
||||||
|
|
||||||
|
// Unspecified parts of LUT will be filled with 3 instead of 0 for debugging
|
||||||
|
memset(lut, 3, frame_count * 256);
|
||||||
|
|
||||||
|
char* line;
|
||||||
|
int done = 0;
|
||||||
|
int err = 0;
|
||||||
|
int rst = 1; // Reset fread_csv_line internal state in the first call
|
||||||
|
while (!done) {
|
||||||
|
line = fread_csv_line(fp, MAX_CSV_LINE, &done, &err, rst);
|
||||||
|
rst = 0;
|
||||||
|
if (!line) continue;
|
||||||
|
char** parsed = parse_csv(line);
|
||||||
|
if (!parsed) continue;
|
||||||
|
// Parse source/ destination range
|
||||||
|
int src0, src1, dst0, dst1;
|
||||||
|
// Skip empty lines
|
||||||
|
if (!parsed[0]) continue;
|
||||||
|
if (!parsed[1]) continue;
|
||||||
|
parse_range(parsed[0], &src0, &src1);
|
||||||
|
parse_range(parsed[1], &dst0, &dst1);
|
||||||
|
// Fill in LUT
|
||||||
|
for (int i = 0; i < frame_count; i++) {
|
||||||
|
assert(parsed[i]);
|
||||||
|
uint8_t val = atoi(parsed[i + 2]);
|
||||||
|
for (int src = src0; src <= src1; src++) {
|
||||||
|
for (int dst = dst0; dst <= dst1; dst++) {
|
||||||
|
lut[i * 256 + dst * 16 + src] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free_csv_line(parsed);
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_lut(int frame_count, uint8_t* lut) {
|
||||||
|
for (int src = 0; src < 16; src++) {
|
||||||
|
for (int dst = 0; dst < 16; dst++) {
|
||||||
|
printf("%x -> %x: ", src, dst);
|
||||||
|
for (int frame = 0; frame < frame_count; frame++) {
|
||||||
|
printf("%d ", lut[frame * 256 + dst * 16 + src]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_lut(uint8_t* dst, uint8_t* src, size_t src_count, int ver) {
|
||||||
|
if (ver == 1) {
|
||||||
|
memcpy(dst, src, src_count);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (size_t i = 0; i < src_count / 2; i++) {
|
||||||
|
uint8_t val;
|
||||||
|
val = *src++;
|
||||||
|
val <<= 4;
|
||||||
|
val |= *src++;
|
||||||
|
*dst++ = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
context_t context;
|
||||||
|
|
||||||
|
printf("Freescale/NXP i.MX EPDC waveform assembler\n");
|
||||||
|
|
||||||
|
// Load waveform descriptor
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr, "Usage: mxc_wvfm_asm version input_file output_file\n");
|
||||||
|
fprintf(stderr, "version: EPDC version, possible values: v1, v2\n");
|
||||||
|
fprintf(stderr, "input_file: Waveform file, in .iwf format\n");
|
||||||
|
fprintf(stderr, "output_file: MXC EPDC firmware file, in .fw format\n");
|
||||||
|
fprintf(stderr, "Example: mxc_wvfm_asm v1 e060scm_desc.iwf epdc_E060SCM.fw\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ver_string = argv[1];
|
||||||
|
char* input_fn = argv[2];
|
||||||
|
char* output_fn = argv[3];
|
||||||
|
int ver;
|
||||||
|
if (strcmp(ver_string, "v1") == 0) {
|
||||||
|
ver = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(ver_string, "v2") == 0) {
|
||||||
|
ver = 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Invalid EPDC version %s\n", ver_string);
|
||||||
|
fprintf(stderr, "Possible values: v1, v2.\n");
|
||||||
|
fprintf(stderr, "i.MX6DL/SL uses EPDCv1, i.MX7D uses EPDCv2. "
|
||||||
|
"i.MX5 EPDC is not supported.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ini_parse(input_fn, ini_parser_handler, &context) < 0) {
|
||||||
|
fprintf(stderr, "Failed to load waveform descriptor.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default name if not provided
|
||||||
|
char* default_name = "Unknown";
|
||||||
|
for (int i = 0; i < context.modes; i++) {
|
||||||
|
if (!context.mode_names[i])
|
||||||
|
context.mode_names[i] = strdup(default_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print loaded info
|
||||||
|
printf("Prefix: %s\n", context.prefix);
|
||||||
|
|
||||||
|
for (int i = 0; i < context.modes; i++) {
|
||||||
|
printf("Mode %d: %s\n", i, context.mode_names[i]);
|
||||||
|
for (int j = 0; j < context.temps; j++) {
|
||||||
|
printf("\tTemp %d: %d frames\n", j,
|
||||||
|
context.frame_counts[i * context.temps + j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < context.temps; i++) {
|
||||||
|
printf("Temp %d: %d degC\n", i, context.temp_ranges[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(context.modes < 100);
|
||||||
|
assert(context.temps < 100);
|
||||||
|
|
||||||
|
// Load actual waveform
|
||||||
|
char *dir = dirname(input_fn); // Return val of dirname shall not be free()d
|
||||||
|
size_t dirlen = strlen(dir);
|
||||||
|
context.luts = malloc(context.modes * sizeof(uint8_t**));
|
||||||
|
assert(context.luts);
|
||||||
|
for (int i = 0; i < context.modes; i++) {
|
||||||
|
context.luts[i] = malloc(context.temps * sizeof(uint8_t*));
|
||||||
|
assert(context.luts[i]);
|
||||||
|
for (int j = 0; j < context.temps; j++) {
|
||||||
|
int frame_count = context.frame_counts[i * context.temps + j];
|
||||||
|
context.luts[i][j] = malloc(frame_count * 256); // LUT always in 8b
|
||||||
|
assert(context.luts[i][j]);
|
||||||
|
char* fn = malloc(dirlen + strlen(context.prefix) + 14);
|
||||||
|
assert(fn);
|
||||||
|
sprintf(fn, "%s/%s_M%d_T%d.csv", dir, context.prefix, i, j);
|
||||||
|
printf("Loading %s...\n", fn);
|
||||||
|
load_waveform_csv(fn, frame_count, context.luts[i][j]);
|
||||||
|
free(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate file size and offset
|
||||||
|
uint64_t header_size = sizeof(waveform_data_header_t);
|
||||||
|
uint64_t temp_table_size = sizeof(uint8_t) * context.temps;
|
||||||
|
uint64_t mode_offset_table_size = sizeof(uint64_t) * context.modes;
|
||||||
|
uint64_t temp_offset_table_size = sizeof(uint64_t) * context.temps;
|
||||||
|
|
||||||
|
// 1st level mode offset table
|
||||||
|
uint64_t* mode_offset_table = malloc(mode_offset_table_size);
|
||||||
|
// global offset table
|
||||||
|
uint64_t* data_offset_table =
|
||||||
|
malloc(sizeof(uint64_t) * context.modes * context.temps);
|
||||||
|
uint64_t total_size = 0;
|
||||||
|
uint64_t data_region_offset = temp_table_size + 1;
|
||||||
|
total_size += mode_offset_table_size;
|
||||||
|
uint64_t lut_size = (ver == 1) ? 256 : 128;
|
||||||
|
|
||||||
|
for (int i = 0; i < context.modes; i++) {
|
||||||
|
// Set the offset of the current mode
|
||||||
|
mode_offset_table[i] = total_size;
|
||||||
|
total_size += temp_offset_table_size;
|
||||||
|
for (int j = 0; j < context.temps; j++) {
|
||||||
|
uint64_t data_size = context.frame_counts[i * context.temps + j]
|
||||||
|
* lut_size + sizeof(uint64_t);
|
||||||
|
data_offset_table[i * context.temps + j] = total_size;
|
||||||
|
printf("Mode %d Temp %d data offset %08"PRIx64" (%"PRId64")\n",
|
||||||
|
i, j, total_size, total_size);
|
||||||
|
total_size += data_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total_size += header_size + temp_table_size + 1;
|
||||||
|
printf("Total file size %"PRId64"\n", total_size);
|
||||||
|
|
||||||
|
// Allocate memory for waveform buffer
|
||||||
|
waveform_data_file_t* pwvfm_file = malloc(total_size);
|
||||||
|
assert(pwvfm_file);
|
||||||
|
|
||||||
|
// Fill waveform header
|
||||||
|
memset(&pwvfm_file->wdh, 0, sizeof(waveform_data_header_t));
|
||||||
|
pwvfm_file->wdh.trc = context.temps - 1;
|
||||||
|
pwvfm_file->wdh.mc = context.modes - 1;
|
||||||
|
// Other fields (including checksums) are generally directly imported from
|
||||||
|
// wbf file. They are not used in MXC EPDC driver. No need to fill them.
|
||||||
|
|
||||||
|
// Fill temperature table
|
||||||
|
for (int i = 0; i < context.temps; i++) {
|
||||||
|
pwvfm_file->data[i] = context.temp_ranges[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 1 byte padding
|
||||||
|
pwvfm_file->data[context.temps] = 0;
|
||||||
|
|
||||||
|
// Fill waveform offset table and temp offset table
|
||||||
|
uint8_t* wvfm_data_region = &pwvfm_file->data[data_region_offset];
|
||||||
|
for (int i = 0; i < context.modes; i++) {
|
||||||
|
write_uint64_le(&wvfm_data_region[i * 8],
|
||||||
|
mode_offset_table[i]);
|
||||||
|
for (int j = 0; j < context.temps; j++) {
|
||||||
|
write_uint64_le(&wvfm_data_region[mode_offset_table[i] + j * 8],
|
||||||
|
data_offset_table[i * context.temps + j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill waveform data
|
||||||
|
for (int i = 0; i < context.modes; i++) {
|
||||||
|
for (int j = 0; j < context.temps; j++) {
|
||||||
|
size_t index = i * context.temps + j;
|
||||||
|
uint8_t* wvfm_wr_ptr = &wvfm_data_region[data_offset_table[index]];
|
||||||
|
int frame_count = context.frame_counts[index];
|
||||||
|
write_uint64_le(wvfm_wr_ptr, frame_count);
|
||||||
|
wvfm_wr_ptr += 8;
|
||||||
|
copy_lut(wvfm_wr_ptr, context.luts[i][j], frame_count * 256, ver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write waveform file
|
||||||
|
FILE *outFile = fopen(output_fn, "wb");
|
||||||
|
assert(outFile);
|
||||||
|
|
||||||
|
size_t written = fwrite((uint8_t *)pwvfm_file, total_size, 1, outFile);
|
||||||
|
assert(written == 1);
|
||||||
|
|
||||||
|
fclose(outFile);
|
||||||
|
|
||||||
|
printf("Finished.\n");
|
||||||
|
|
||||||
|
// Free buffers
|
||||||
|
free(pwvfm_file);
|
||||||
|
free(context.prefix);
|
||||||
|
for (int i = 0; i < context.modes; i++) {
|
||||||
|
free(context.mode_names[i]);
|
||||||
|
for (int j = 0; j < context.temps; j++) {
|
||||||
|
free(context.luts[i][j]);
|
||||||
|
}
|
||||||
|
free(context.luts[i]);
|
||||||
|
}
|
||||||
|
free(context.mode_names);
|
||||||
|
free(context.luts);
|
||||||
|
free(context.frame_counts);
|
||||||
|
free(context.temp_ranges);
|
||||||
|
free(mode_offset_table);
|
||||||
|
free(data_offset_table);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
85
utils/mxc_waveform_asm/split.c
Normal file
85
utils/mxc_waveform_asm/split.c
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a string which might contain unescaped newlines, split it up into
|
||||||
|
* lines which do not contain unescaped newlines, returned as a
|
||||||
|
* NULL-terminated array of malloc'd strings.
|
||||||
|
*/
|
||||||
|
char **split_on_unescaped_newlines(const char *txt) {
|
||||||
|
const char *ptr, *lineStart;
|
||||||
|
char **buf, **bptr;
|
||||||
|
int fQuote, nLines;
|
||||||
|
|
||||||
|
/* First pass: count how many lines we will need */
|
||||||
|
for ( nLines = 1, ptr = txt, fQuote = 0; *ptr; ptr++ ) {
|
||||||
|
if ( fQuote ) {
|
||||||
|
if ( *ptr == '\"' ) {
|
||||||
|
if ( ptr[1] == '\"' ) {
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fQuote = 0;
|
||||||
|
}
|
||||||
|
} else if ( *ptr == '\"' ) {
|
||||||
|
fQuote = 1;
|
||||||
|
} else if ( *ptr == '\n' ) {
|
||||||
|
nLines++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc( sizeof(char*) * (nLines+1) );
|
||||||
|
|
||||||
|
if ( !buf ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Second pass: populate results */
|
||||||
|
lineStart = txt;
|
||||||
|
for ( bptr = buf, ptr = txt, fQuote = 0; ; ptr++ ) {
|
||||||
|
if ( fQuote ) {
|
||||||
|
if ( *ptr == '\"' ) {
|
||||||
|
if ( ptr[1] == '\"' ) {
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fQuote = 0;
|
||||||
|
continue;
|
||||||
|
} else if ( *ptr ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( *ptr == '\"' ) {
|
||||||
|
fQuote = 1;
|
||||||
|
} else if ( *ptr == '\n' || !*ptr ) {
|
||||||
|
size_t len = ptr - lineStart;
|
||||||
|
|
||||||
|
if ( len == 0 ) {
|
||||||
|
*bptr = NULL;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bptr = malloc( len + 1 );
|
||||||
|
|
||||||
|
if ( !*bptr ) {
|
||||||
|
for ( bptr--; bptr >= buf; bptr-- ) {
|
||||||
|
free( *bptr );
|
||||||
|
}
|
||||||
|
free( buf );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy( *bptr, lineStart, len );
|
||||||
|
(*bptr)[len] = '\0';
|
||||||
|
|
||||||
|
if ( *ptr ) {
|
||||||
|
lineStart = ptr + 1;
|
||||||
|
bptr++;
|
||||||
|
} else {
|
||||||
|
bptr[1] = NULL;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
utils/mxc_waveform_dump/Makefile
Normal file
6
utils/mxc_waveform_dump/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
all: mxc_wvfm_dump
|
||||||
|
|
||||||
|
mxc_wvfm_dump: main.c
|
||||||
|
gcc -O1 -g main.c -o mxc_wvfm_dump
|
||||||
|
clean:
|
||||||
|
rm -f mxc_wvfm_dump
|
309
utils/mxc_waveform_dump/main.c
Normal file
309
utils/mxc_waveform_dump/main.c
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Freescale/NXP EPDC waveform firmware dumper
|
||||||
|
* Based on https://github.com/julbouln/ice40_eink_controller
|
||||||
|
*
|
||||||
|
* This 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 2 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* This software 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
|
||||||
|
* the software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file is partially derived from Linux kernel driver, with the following
|
||||||
|
* copyright information:
|
||||||
|
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
|
||||||
|
* Copyright 2017 NXP
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
struct waveform_data_header {
|
||||||
|
unsigned int wi0;
|
||||||
|
unsigned int wi1;
|
||||||
|
unsigned int wi2;
|
||||||
|
unsigned int wi3;
|
||||||
|
unsigned int wi4;
|
||||||
|
unsigned int wi5;
|
||||||
|
unsigned int wi6;
|
||||||
|
|
||||||
|
unsigned int xwia: 24;
|
||||||
|
unsigned int cs1: 8;
|
||||||
|
|
||||||
|
unsigned int wmta: 24;
|
||||||
|
unsigned int fvsn: 8;
|
||||||
|
unsigned int luts: 8;
|
||||||
|
unsigned int mc: 8;
|
||||||
|
unsigned int trc: 8;
|
||||||
|
unsigned int advanced_wfm_flags: 8;
|
||||||
|
unsigned int eb: 8;
|
||||||
|
unsigned int sb: 8;
|
||||||
|
unsigned int reserved0_1: 8;
|
||||||
|
unsigned int reserved0_2: 8;
|
||||||
|
unsigned int reserved0_3: 8;
|
||||||
|
unsigned int reserved0_4: 8;
|
||||||
|
unsigned int reserved0_5: 8;
|
||||||
|
unsigned int cs2: 8;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mxcfb_waveform_data_file {
|
||||||
|
struct waveform_data_header wdh;
|
||||||
|
uint32_t *data; /* Temperature Range Table + Waveform Data */
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint64_t read_uint64_le(uint8_t* src) {
|
||||||
|
return ((uint64_t)src[7] << 56) |
|
||||||
|
((uint64_t)src[6] << 48) |
|
||||||
|
((uint64_t)src[5] << 40) |
|
||||||
|
((uint64_t)src[4] << 32) |
|
||||||
|
((uint64_t)src[3] << 24) |
|
||||||
|
((uint64_t)src[2] << 16) |
|
||||||
|
((uint64_t)src[1] << 8) |
|
||||||
|
(uint64_t)src[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t read_uint4(uint8_t* src, size_t addr) {
|
||||||
|
uint8_t val = src[addr >> 1];
|
||||||
|
if (addr & 1)
|
||||||
|
val = (val >> 4) & 0xf;
|
||||||
|
else
|
||||||
|
val = val & 0xf;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_phases(FILE* fp, uint8_t* buffer, int phases, int ver) {
|
||||||
|
int i, j, k, l, x, y;
|
||||||
|
|
||||||
|
uint8_t luts[phases][16][16];
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
for (i = 0; i < phases * 256; i += 256) {
|
||||||
|
j = 0;
|
||||||
|
for (x = 0; x < 16; x++) {
|
||||||
|
for (y = 0; y < 16; y++) {
|
||||||
|
uint8_t val;
|
||||||
|
if (ver == 2)
|
||||||
|
val = read_uint4(buffer + 8, i + j);
|
||||||
|
else
|
||||||
|
val = buffer[8 + i + j];
|
||||||
|
luts[k][y][x] = val;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
for (j = 0; j < 16; j++) {
|
||||||
|
fprintf(fp, "%d,%d,", i, j);
|
||||||
|
for (k = 0; k < phases; k++) {
|
||||||
|
fprintf(fp, "%d,", luts[k][i][j]);
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
fprintf(stderr, "MXC EPDC waveform dumper\n");
|
||||||
|
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr, "Usage: mxc_wvfm_dump version input_file output_prefix\n");
|
||||||
|
fprintf(stderr, "version: EPDC version, possible values: v1, v2\n");
|
||||||
|
fprintf(stderr, "input_file: MXC EPDC firmware file, in .fw format\n");
|
||||||
|
fprintf(stderr, "output_prefix: Prefix for output file name, without extension\n");
|
||||||
|
fprintf(stderr, "Example: mxc_wvfm_dump v1 epdc_E060SCM.fw e060scm\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ver_string = argv[1];
|
||||||
|
char *fw = argv[2];
|
||||||
|
char *prefix = argv[3];
|
||||||
|
int ver;
|
||||||
|
|
||||||
|
if (strcmp(ver_string, "v1") == 0) {
|
||||||
|
ver = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(ver_string, "v2") == 0) {
|
||||||
|
ver = 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Invalid EPDC version %s\n", ver_string);
|
||||||
|
fprintf(stderr, "Possible values: v1, v2.\n");
|
||||||
|
fprintf(stderr, "i.MX6DL/SL uses EPDCv1, i.MX7D uses EPDCv2. i.MX5 EPDC is not supported.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE * fp;
|
||||||
|
size_t file_size;
|
||||||
|
char * file_buffer;
|
||||||
|
size_t result;
|
||||||
|
|
||||||
|
fp = fopen(fw, "rb");
|
||||||
|
assert(fp);
|
||||||
|
|
||||||
|
// obtain file size:
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
file_size = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
// allocate memory to contain the whole file:
|
||||||
|
file_buffer = (char*)malloc(sizeof(char) * file_size);
|
||||||
|
assert(file_buffer);
|
||||||
|
|
||||||
|
// copy the file into the buffer:
|
||||||
|
assert(fread(file_buffer, file_size, 1, fp) == 1);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/* the whole file is now loaded in the memory buffer. */
|
||||||
|
|
||||||
|
struct mxcfb_waveform_data_file *wv_file;
|
||||||
|
wv_file = (struct mxcfb_waveform_data_file *)file_buffer;
|
||||||
|
|
||||||
|
printf("wi0: %08x\n", wv_file->wdh.wi0);
|
||||||
|
printf("wi1: %08x\n", wv_file->wdh.wi1);
|
||||||
|
printf("wi2: %08x\n", wv_file->wdh.wi2);
|
||||||
|
printf("wi3: %08x\n", wv_file->wdh.wi3);
|
||||||
|
printf("wi4: %08x\n", wv_file->wdh.wi4);
|
||||||
|
printf("wi5: %08x\n", wv_file->wdh.wi5);
|
||||||
|
printf("wi6: %08x\n", wv_file->wdh.wi6);
|
||||||
|
|
||||||
|
printf("xwia: %d\n", wv_file->wdh.xwia);
|
||||||
|
printf("cs1: %d\n", wv_file->wdh.cs1);
|
||||||
|
|
||||||
|
printf("wmta: %d\n", wv_file->wdh.wmta);
|
||||||
|
printf("fvsn: %d\n", wv_file->wdh.fvsn);
|
||||||
|
printf("luts: %d\n", wv_file->wdh.luts);
|
||||||
|
printf("mc: %d\n", wv_file->wdh.mc);
|
||||||
|
printf("trc: %d\n", wv_file->wdh.trc);
|
||||||
|
printf("advanced_wfm_flags: %d\n", wv_file->wdh.advanced_wfm_flags);
|
||||||
|
printf("eb: %d\n", wv_file->wdh.eb);
|
||||||
|
printf("sb: %d\n", wv_file->wdh.sb);
|
||||||
|
printf("reserved0_1: %d\n", wv_file->wdh.reserved0_1);
|
||||||
|
printf("reserved0_2: %d\n", wv_file->wdh.reserved0_2);
|
||||||
|
printf("reserved0_3: %d\n", wv_file->wdh.reserved0_3);
|
||||||
|
printf("reserved0_4: %d\n", wv_file->wdh.reserved0_4);
|
||||||
|
printf("reserved0_5: %d\n", wv_file->wdh.reserved0_5);
|
||||||
|
printf("cs2: %d\n", wv_file->wdh.cs2);
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
int trt_entries; // temperature range table
|
||||||
|
int wv_data_offs; // offset for waveform data
|
||||||
|
int waveform_buffer_size; // size for waveform data
|
||||||
|
int mode_count = wv_file->wdh.mc + 1;
|
||||||
|
|
||||||
|
uint8_t *temp_range_bounds;
|
||||||
|
trt_entries = wv_file->wdh.trc + 1;
|
||||||
|
|
||||||
|
printf("Temperatures count: %d\n", trt_entries);
|
||||||
|
|
||||||
|
temp_range_bounds = (uint8_t*)malloc(trt_entries);
|
||||||
|
|
||||||
|
memcpy(temp_range_bounds, &wv_file->data, trt_entries);
|
||||||
|
|
||||||
|
for (i = 0; i < trt_entries; i++) {
|
||||||
|
printf("Temperature %d = %d°C\n", i, temp_range_bounds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
wv_data_offs = sizeof(wv_file->wdh) + trt_entries + 1;
|
||||||
|
waveform_buffer_size = file_size - wv_data_offs;
|
||||||
|
|
||||||
|
printf("Waveform data offset: %d, size: %d\n", wv_data_offs, waveform_buffer_size);
|
||||||
|
|
||||||
|
if ((wv_file->wdh.luts & 0xC) == 0x4) {
|
||||||
|
printf("waveform 5bit\n");
|
||||||
|
} else {
|
||||||
|
printf("waveform 4bit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *waveform_buffer;
|
||||||
|
waveform_buffer = (uint8_t *)malloc(waveform_buffer_size);
|
||||||
|
memcpy(waveform_buffer, (uint8_t *)(file_buffer) + wv_data_offs,
|
||||||
|
waveform_buffer_size);
|
||||||
|
|
||||||
|
uint64_t* wv_modes = malloc(sizeof(uint64_t) * mode_count);
|
||||||
|
|
||||||
|
uint64_t addr;
|
||||||
|
// get modes addr
|
||||||
|
for (i = 0; i < mode_count; i++) {
|
||||||
|
addr = read_uint64_le(&waveform_buffer[i * 8]);
|
||||||
|
printf("wave #%d addr: %08"PRIx64"\n", i, addr);
|
||||||
|
wv_modes[i] = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get modes temp addr
|
||||||
|
uint64_t* wv_modes_temps = malloc(sizeof(uint64_t) * mode_count * trt_entries);
|
||||||
|
uint64_t* frame_counts = malloc(sizeof(uint64_t) * mode_count * trt_entries);
|
||||||
|
uint64_t last_addr = wv_modes[0];
|
||||||
|
for (i = 0; i < mode_count; i++) {
|
||||||
|
uint64_t m = wv_modes[i];
|
||||||
|
for (j = 0; j < trt_entries; j++) {
|
||||||
|
addr = read_uint64_le(&waveform_buffer[m + j * 8]);
|
||||||
|
wv_modes_temps[i * trt_entries + j] = addr;
|
||||||
|
uint64_t frame_count = read_uint64_le(&waveform_buffer[addr]);
|
||||||
|
frame_counts[i * trt_entries + j] = frame_count;
|
||||||
|
printf("wave #%d, temp #%d addr: %08"PRIx64", %"PRId64" phases (Addr diff = %"PRId64", Size = %"PRId64")\n", i, j,
|
||||||
|
addr, frame_count, addr - last_addr, frame_count * 256);
|
||||||
|
last_addr = addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* fn = malloc(strlen(prefix) + 14);
|
||||||
|
sprintf(fn, "%s_desc.iwf", prefix);
|
||||||
|
fp = fopen(fn, "w");
|
||||||
|
assert(fp);
|
||||||
|
fprintf(fp, "[WAVEFORM]\n");
|
||||||
|
fprintf(fp, "VERSION = 1.0\n");
|
||||||
|
fprintf(fp, "PREFIX = %s\n", prefix);
|
||||||
|
fprintf(fp, "MODES = %d\n", mode_count);
|
||||||
|
fprintf(fp, "TEMPS = %d\n", trt_entries);
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
for (int i = 0; i < trt_entries; i++) {
|
||||||
|
fprintf(fp, "T%dRANGE = %d\n", i, temp_range_bounds[i]);
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
for (int i = 0; i < mode_count; i++) {
|
||||||
|
fprintf(fp, "[MODE%d]\n", i);
|
||||||
|
for (int j = 0; j < trt_entries; j++) {
|
||||||
|
fprintf(fp, "T%dFC = %"PRId64"\n", j, frame_counts[i * trt_entries + j]);
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
for (int i = 0; i < mode_count; i++) {
|
||||||
|
for (int j = 0; j < trt_entries; j++) {
|
||||||
|
sprintf(fn, "%s_M%d_T%d.csv", prefix, i, j);
|
||||||
|
fp = fopen(fn, "w");
|
||||||
|
assert(fp);
|
||||||
|
size_t index = i * trt_entries + j;
|
||||||
|
uint64_t frame_count = frame_counts[index];
|
||||||
|
uint8_t* lut = &waveform_buffer[wv_modes_temps[index]];
|
||||||
|
dump_phases(fp, lut, frame_count, ver);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(fn);
|
||||||
|
free(temp_range_bounds);
|
||||||
|
free(waveform_buffer);
|
||||||
|
free(frame_counts);
|
||||||
|
free(wv_modes);
|
||||||
|
free(wv_modes_temps);
|
||||||
|
|
||||||
|
free(file_buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
6
utils/wbf_flash_decompress/Makefile
Normal file
6
utils/wbf_flash_decompress/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
all: wbf_flash_decompress
|
||||||
|
|
||||||
|
wbf_flash_decompress: main.c
|
||||||
|
gcc -O1 -g main.c -o wbf_flash_decompress
|
||||||
|
clean:
|
||||||
|
rm -f wbf_flash_decompress
|
143
utils/wbf_flash_decompress/main.c
Normal file
143
utils/wbf_flash_decompress/main.c
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
// Eink waveform flash decompressor
|
||||||
|
// Copyright 2024 Wenting Zhang
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define MAX_DECOMP_SIZE (0x100000)
|
||||||
|
|
||||||
|
static uint32_t read_uint16_be(uint8_t *ptr) {
|
||||||
|
uint32_t b0 = (uint32_t)(*ptr++) << 8;
|
||||||
|
uint32_t b1 = (uint32_t)(*ptr++);
|
||||||
|
return b0 | b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t read_uint16_le(uint8_t *ptr) {
|
||||||
|
uint32_t b0 = (uint32_t)(*ptr++);
|
||||||
|
uint32_t b1 = (uint32_t)(*ptr++) << 8;
|
||||||
|
return b0 | b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t read_uint32_be(uint8_t *ptr) {
|
||||||
|
uint32_t b0 = (uint32_t)(*ptr++) << 24;
|
||||||
|
uint32_t b1 = (uint32_t)(*ptr++) << 16;
|
||||||
|
uint32_t b2 = (uint32_t)(*ptr++) << 8;
|
||||||
|
uint32_t b3 = (uint32_t)(*ptr++);
|
||||||
|
return b0 | b1 | b2 | b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
fprintf(stderr, "Eink waveform flash decompressor\n");
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
fprintf(stderr, "Usage: wbf_flash_decompress input_file output_file\n");
|
||||||
|
fprintf(stderr, "input_file: Compressed flash ROM (like a flash dump)\n");
|
||||||
|
fprintf(stderr, "output_file: WBF file name\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *bin = argv[1];
|
||||||
|
char *wbf = argv[2];
|
||||||
|
|
||||||
|
FILE * fp;
|
||||||
|
size_t file_size;
|
||||||
|
char * file_buffer;
|
||||||
|
size_t result;
|
||||||
|
|
||||||
|
fp = fopen(bin, "rb");
|
||||||
|
assert(fp);
|
||||||
|
|
||||||
|
// obtain file size:
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
file_size = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
// allocate memory to contain the whole file:
|
||||||
|
file_buffer = (char*)malloc(sizeof(char) * file_size);
|
||||||
|
assert(file_buffer);
|
||||||
|
|
||||||
|
// copy the file into the buffer:
|
||||||
|
assert(fread(file_buffer, file_size, 1, fp) == 1);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
printf("File size: %zu bytes\n", file_size);
|
||||||
|
|
||||||
|
uint8_t *ptr = (uint8_t *)file_buffer;
|
||||||
|
uint32_t compressed_len = read_uint32_be(ptr);
|
||||||
|
ptr += 4;
|
||||||
|
uint32_t header_version = read_uint32_be(ptr);
|
||||||
|
|
||||||
|
printf("Compressed length: %d bytes\n", compressed_len);
|
||||||
|
|
||||||
|
if ((compressed_len + 16) > file_size) {
|
||||||
|
printf("Error: file size smaller than expected.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if ((compressed_len + 16) < file_size) {
|
||||||
|
printf("Warning: file size larger than expected.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Header version: %d\n", header_version);
|
||||||
|
|
||||||
|
if (header_version != 1) {
|
||||||
|
printf("Unsupported file version\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start decompress
|
||||||
|
ptr = (uint8_t *)file_buffer + 16; // skip header
|
||||||
|
static uint8_t decomp_buffer[MAX_DECOMP_SIZE];
|
||||||
|
uint32_t wrptr = 0;
|
||||||
|
uint8_t *ptr_end = ptr + compressed_len;
|
||||||
|
while (ptr < ptr_end) {
|
||||||
|
uint32_t wrptr_snapshot = wrptr;
|
||||||
|
uint32_t offset = (uint32_t)read_uint16_le(ptr);
|
||||||
|
ptr += 2;
|
||||||
|
uint8_t len = *ptr++;
|
||||||
|
uint8_t byte = *ptr++;
|
||||||
|
printf("Ptr %d, Offset %d, Len %d, Byte 0x%02x\n", wrptr_snapshot, offset, len, byte);
|
||||||
|
if (len != 0) {
|
||||||
|
printf("Src: %d\n", wrptr_snapshot - offset);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
// len could be zero, in this case the loop doesn't execute
|
||||||
|
uint8_t rd = decomp_buffer[wrptr_snapshot - offset + i];
|
||||||
|
decomp_buffer[wrptr++] = rd;
|
||||||
|
}
|
||||||
|
decomp_buffer[wrptr++] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Decompressed size: %d bytes\n", wrptr);
|
||||||
|
|
||||||
|
fp = fopen(wbf, "wb");
|
||||||
|
fwrite(decomp_buffer, wrptr, 1, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("Done\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
6
utils/wbf_waveform_dump/Makefile
Normal file
6
utils/wbf_waveform_dump/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
all: wbf_wvfm_dump
|
||||||
|
|
||||||
|
wbf_wvfm_dump: main.c
|
||||||
|
gcc -O1 -g main.c -o wbf_wvfm_dump
|
||||||
|
clean:
|
||||||
|
rm -f wbf_wvfm_dump
|
568
utils/wbf_waveform_dump/main.c
Normal file
568
utils/wbf_waveform_dump/main.c
Normal file
|
@ -0,0 +1,568 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Eink waveform firmware dumper
|
||||||
|
* Based on https://github.com/fread-ink/inkwave and Linux kernel
|
||||||
|
*
|
||||||
|
* This 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 2 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* This software 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
|
||||||
|
* the software. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file is partially derived from Linux kernel driver, with the following
|
||||||
|
* copyright information:
|
||||||
|
* Copyright 2004-2013 Freescale Semiconductor, Inc.
|
||||||
|
* Copyright 2005-2017 Amazon Technologies, Inc.
|
||||||
|
* Copyright 2018, 2021 Marc Juul
|
||||||
|
* Copyright (C) 2022 Samuel Holland <samuel@sholland.org>
|
||||||
|
* Copyright 2024 Wenting Zhang
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
struct waveform_data_header {
|
||||||
|
uint32_t checksum:32; // 0
|
||||||
|
uint32_t filesize:32; // 4
|
||||||
|
uint32_t serial:32; // 8 serial number
|
||||||
|
uint32_t run_type:8; // 12
|
||||||
|
uint32_t fpl_platform:8; // 13
|
||||||
|
uint32_t fpl_lot:16; // 14
|
||||||
|
uint32_t mode_version_or_adhesive_run_num:8; // 16
|
||||||
|
uint32_t waveform_version:8; // 17
|
||||||
|
uint32_t waveform_subversion:8; // 18
|
||||||
|
uint32_t waveform_type:8; // 19
|
||||||
|
uint32_t fpl_size:8; // 20 (aka panel_size)
|
||||||
|
uint32_t mfg_code:8; // 21 (aka amepd_part_number)
|
||||||
|
uint32_t waveform_tuning_bias_or_rev:8; // 22
|
||||||
|
uint32_t fpl_rate:8; // 23 (aka frame_rate)
|
||||||
|
uint32_t unknown0:8;
|
||||||
|
uint32_t vcom_shifted:8;
|
||||||
|
uint32_t unknown1:16;
|
||||||
|
uint32_t xwia:24; // address of extra waveform information
|
||||||
|
uint32_t cs1:8; // checksum 1
|
||||||
|
uint32_t wmta:24;
|
||||||
|
uint32_t fvsn:8;
|
||||||
|
uint32_t luts:8;
|
||||||
|
uint32_t mc:8; // mode count (length of mode table - 1)
|
||||||
|
uint32_t trc:8; // temperature range count (length of temperature table - 1)
|
||||||
|
uint32_t advanced_wfm_flags:8;
|
||||||
|
uint32_t eb:8;
|
||||||
|
uint32_t sb:8;
|
||||||
|
uint32_t reserved0_1:8;
|
||||||
|
uint32_t reserved0_2:8;
|
||||||
|
uint32_t reserved0_3:8;
|
||||||
|
uint32_t reserved0_4:8;
|
||||||
|
uint32_t reserved0_5:8;
|
||||||
|
uint32_t cs2:8; // checksum 2
|
||||||
|
}__attribute__((packed));
|
||||||
|
|
||||||
|
#define HEADER_SIZE sizeof(struct waveform_data_header)
|
||||||
|
|
||||||
|
#define MODE_MAX 10
|
||||||
|
|
||||||
|
// Mode version to mode string
|
||||||
|
struct mode_name_lut_t {
|
||||||
|
uint8_t versions[2];
|
||||||
|
char *mode_strings[MODE_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Partially derived from linux kernel drm_epd_helper.c
|
||||||
|
// All GL series (GL GLR GLD) are marked as GL as the underlying wavetable seems to be identical
|
||||||
|
// Thus it's impossible to identify the correct ordering
|
||||||
|
// -R/ -D ghosting reduction is outside of the scope of this tool
|
||||||
|
const struct mode_name_lut_t mode_name_lut[] = {
|
||||||
|
{
|
||||||
|
// Example: ED050SC3
|
||||||
|
.versions = {0x01},
|
||||||
|
.mode_strings = {"INIT", "DU", "GL8", "GC8"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Example: ED097TC1
|
||||||
|
.versions = {0x03},
|
||||||
|
.mode_strings = {"INIT", "DU", "GL16", "GL16", "A2"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Example: ET073TC1
|
||||||
|
.versions = {0x09},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "A2"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Untested
|
||||||
|
.versions = {0x12},
|
||||||
|
.mode_strings = {"INIT", "DU", "NULL", "GC16", "A2", "GL16", "GL16", "DU4"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Example: ES133UT1
|
||||||
|
.versions = {0x15},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "GL16", "A2", "DU4", "GC4"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Untested
|
||||||
|
.versions = {0x16},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "GL16", "GL16", "GC16", "A2"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Example: ES108FC1
|
||||||
|
.versions = {0x18, 0x20},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "GL16", "GL16", "GL16", "A2"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Example: ES103TC1
|
||||||
|
.versions = {0x19, 0x43},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "GL16", "GL16", "GL16", "A2", "DU4"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Untested
|
||||||
|
.versions = {0x23},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "GL16", "A2", "DU4"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Untested
|
||||||
|
.versions = {0x54},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "GL16", "GL16", "A2"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Example: ES120MC1
|
||||||
|
.versions = {0x48},
|
||||||
|
.mode_strings = {"INIT", "DU", "GC16", "GL16", "GL16", "NULL", "A2"}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#define MODE_NAME_LUTS (sizeof(mode_name_lut) / sizeof(*mode_name_lut))
|
||||||
|
|
||||||
|
#define MAX_TABLE_LENGTH (1024*1024)
|
||||||
|
|
||||||
|
static uint32_t read_pointer(uint8_t *ptr) {
|
||||||
|
uint32_t addr = ((uint32_t)ptr[2] << 16) | ((uint32_t)ptr[1] << 8) | ((uint32_t)ptr[0]);
|
||||||
|
uint8_t checksum = ptr[0] + ptr[1] + ptr[2];
|
||||||
|
if (checksum != ptr[3]) {
|
||||||
|
printf("Pointer checksum mismatch\n");
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t read_uint4(uint8_t* src, size_t addr) {
|
||||||
|
uint8_t val = src[addr >> 1];
|
||||||
|
if (addr & 1)
|
||||||
|
val = (val >> 4) & 0xf;
|
||||||
|
else
|
||||||
|
val = val & 0xf;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void compute_crc_table(unsigned int* crc_table) {
|
||||||
|
unsigned c;
|
||||||
|
int n, k;
|
||||||
|
for (n = 0; n < 256; n++) {
|
||||||
|
c = (unsigned) n;
|
||||||
|
for (k = 0; k < 8; k++) {
|
||||||
|
if (c & 1) {
|
||||||
|
c = 0xedb88320L ^ (c >> 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c = c >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crc_table[n] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int update_crc(unsigned int* crc_table, unsigned crc,
|
||||||
|
unsigned char *buf, int len) {
|
||||||
|
|
||||||
|
char b;
|
||||||
|
unsigned c = crc ^ 0xffffffff;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0; i < len; i++) {
|
||||||
|
if(!buf) {
|
||||||
|
b = 0;
|
||||||
|
} else {
|
||||||
|
b = buf[i];
|
||||||
|
}
|
||||||
|
c = crc_table[(c ^ b) & 0xff] ^ (c >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c ^ 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned crc32(unsigned char *buf, int len) {
|
||||||
|
static unsigned int crc_table[256];
|
||||||
|
|
||||||
|
compute_crc_table(crc_table);
|
||||||
|
|
||||||
|
return update_crc(crc_table, 0, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_phases(FILE* fp, uint8_t* buffer, int bpp, int phases, int phase_per_byte) {
|
||||||
|
int states;
|
||||||
|
int i, j, k, x, y;
|
||||||
|
|
||||||
|
states = (bpp == 4) ? 16 : 32;
|
||||||
|
uint8_t luts[phases][states][states];
|
||||||
|
uint8_t *ptr = buffer;
|
||||||
|
|
||||||
|
for (i = 0; i < phases; i ++) {
|
||||||
|
for (x = 0; x < states; x++) {
|
||||||
|
for (y = 0; y < states; y+=phase_per_byte) {
|
||||||
|
uint8_t val = *ptr++;
|
||||||
|
if (phase_per_byte == 4) {
|
||||||
|
luts[i][y+0][x] = (val >> 0) & 0x3;
|
||||||
|
luts[i][y+1][x] = (val >> 2) & 0x3;
|
||||||
|
luts[i][y+2][x] = (val >> 4) & 0x3;
|
||||||
|
luts[i][y+3][x] = (val >> 6) & 0x3;
|
||||||
|
}
|
||||||
|
else if (phase_per_byte == 2) {
|
||||||
|
luts[i][y+0][x] = (val >> 0) & 0xf;
|
||||||
|
luts[i][y+1][x] = (val >> 4) & 0xf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < states; i++) {
|
||||||
|
for (j = 0; j < states; j++) {
|
||||||
|
fprintf(fp, "%d,%d,", i, j);
|
||||||
|
for (k = 0; k < phases; k++) {
|
||||||
|
fprintf(fp, "%d,", luts[k][i][j]);
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
fprintf(stderr, "Eink wbf waveform dumper\n");
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
fprintf(stderr, "Usage: wbf_wvfm_dump input_file output_prefix\n");
|
||||||
|
fprintf(stderr, "input_file: MXC EPDC firmware file, in .wbf format\n");
|
||||||
|
fprintf(stderr, "output_prefix: Prefix for output file name, without extension\n");
|
||||||
|
fprintf(stderr, "Example: wbf_wvfm_dump E060SCM.wbf e060scm\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *fw = argv[1];
|
||||||
|
char *prefix = argv[2];
|
||||||
|
|
||||||
|
FILE * fp;
|
||||||
|
size_t file_size;
|
||||||
|
char * file_buffer;
|
||||||
|
size_t result;
|
||||||
|
|
||||||
|
fp = fopen(fw, "rb");
|
||||||
|
assert(fp);
|
||||||
|
|
||||||
|
// obtain file size:
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
file_size = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
// allocate memory to contain the whole file:
|
||||||
|
file_buffer = (char*)malloc(sizeof(char) * file_size);
|
||||||
|
assert(file_buffer);
|
||||||
|
|
||||||
|
// copy the file into the buffer:
|
||||||
|
assert(fread(file_buffer, file_size, 1, fp) == 1);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/* the whole file is now loaded in the memory buffer. */
|
||||||
|
|
||||||
|
struct waveform_data_header *header;
|
||||||
|
header = (struct waveform_data_header *)file_buffer;
|
||||||
|
|
||||||
|
printf("File size: %zu bytes.\n", file_size);
|
||||||
|
|
||||||
|
printf("Reported size: %d bytes.\n", header->filesize);
|
||||||
|
printf("Serial number: %d\n", header->serial);
|
||||||
|
printf("Run type: 0x%x\n", header->run_type);
|
||||||
|
printf("Mfg code: 0x%x\n", header->mfg_code);
|
||||||
|
|
||||||
|
printf("FPL platform: 0x%x\n", header->fpl_platform);
|
||||||
|
printf("FPL lot: 0x%x\n", header->fpl_lot);
|
||||||
|
printf("FPL size: 0x%x\n", header->fpl_size);
|
||||||
|
printf("FPL rate: 0x%x\n", header->fpl_rate);
|
||||||
|
|
||||||
|
printf("Mode version: 0x%x\n", header->mode_version_or_adhesive_run_num);
|
||||||
|
printf("Waveform version: %d\n", header->waveform_version);
|
||||||
|
printf("Waveform sub-version: %d\n", header->waveform_subversion);
|
||||||
|
|
||||||
|
printf("Waveform type: %d\n", header->waveform_type);
|
||||||
|
|
||||||
|
printf("Number of modes: %d\n", header->mc + 1);
|
||||||
|
printf("Number of temperature ranges: %d\n", header->trc + 1);
|
||||||
|
|
||||||
|
int bpp = ((header->luts & 0xC) == 0x4) ? 5: 4;
|
||||||
|
printf("BPP: %d (LUTS = 0x%02x)\n", bpp, header->luts);
|
||||||
|
bool acep = false;
|
||||||
|
if (header->luts == 0x15) {
|
||||||
|
printf("Looks like you've supplied a waveform for ACeP screens\n");
|
||||||
|
printf("Expect the checksum for the header to fail.\n");
|
||||||
|
bpp = 5;
|
||||||
|
acep = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare checksum
|
||||||
|
static unsigned int crc_table[256];
|
||||||
|
compute_crc_table(crc_table);
|
||||||
|
unsigned int crc;
|
||||||
|
if (header->filesize != 0) {
|
||||||
|
crc = update_crc(crc_table, 0, NULL, 4);
|
||||||
|
crc = update_crc(crc_table, crc, (unsigned char *)file_buffer + 4, header->filesize-4);
|
||||||
|
|
||||||
|
printf("Checksum: 0x%08x\n", crc);
|
||||||
|
if (crc == header->checksum) {
|
||||||
|
printf("Checksum match.\n");
|
||||||
|
} else {
|
||||||
|
printf("Checksum mismatch! Expected: 0x%08x\n", header->checksum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("File size reported to be 0 in the header. Checksum check skipped.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find mode description
|
||||||
|
char **mode_string = NULL;
|
||||||
|
uint8_t mode_version = header->mode_version_or_adhesive_run_num;
|
||||||
|
for (int i = 0; i < MODE_NAME_LUTS; i++) {
|
||||||
|
if ((mode_name_lut[i].versions[0] == mode_version) ||
|
||||||
|
(mode_name_lut[i].versions[1] == mode_version)) {
|
||||||
|
mode_string = (char **)(mode_name_lut[i].mode_strings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mode_string) {
|
||||||
|
printf("Known mode version, mode names available.\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Unknown mode version, mode names won't be available.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right following the header is the temperature range table
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
int trt_entries; // temperature range table
|
||||||
|
|
||||||
|
uint8_t *temp_range_bounds;
|
||||||
|
trt_entries = header->trc + 1;
|
||||||
|
|
||||||
|
printf("Temperatures count: %d\n", trt_entries);
|
||||||
|
|
||||||
|
temp_range_bounds = (uint8_t*)malloc(trt_entries + 2);
|
||||||
|
|
||||||
|
memcpy(temp_range_bounds, file_buffer + HEADER_SIZE, trt_entries + 2);
|
||||||
|
|
||||||
|
for (i = 0; i < trt_entries; i++) {
|
||||||
|
printf("Temperature %d = %d°C\n", i, temp_range_bounds[i]);
|
||||||
|
checksum += temp_range_bounds[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("End bound: %d°C\n", temp_range_bounds[trt_entries]);
|
||||||
|
checksum += temp_range_bounds[trt_entries];
|
||||||
|
|
||||||
|
if (checksum != temp_range_bounds[trt_entries + 1]) {
|
||||||
|
printf("Temperature table checksum mismatch\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
char xwia_buffer[128];
|
||||||
|
uint8_t xwia_len = 0;
|
||||||
|
if (header->xwia != 0) {
|
||||||
|
printf("Extra waveform information present:\n");
|
||||||
|
uint8_t *ptr = (uint8_t *)file_buffer + header->xwia;
|
||||||
|
xwia_len = *ptr++;
|
||||||
|
checksum = xwia_len;
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
for (i = 0; i < xwia_len; i++) {
|
||||||
|
char c = *ptr++;
|
||||||
|
if (isprint(c)) {
|
||||||
|
xwia_buffer[j++] = c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
xwia_buffer[j++] = '\\';
|
||||||
|
xwia_buffer[j++] = 'x';
|
||||||
|
xwia_buffer[j++] = (c / 16) + '0';
|
||||||
|
xwia_buffer[j++] = (c % 16) + '0';
|
||||||
|
}
|
||||||
|
checksum += c;
|
||||||
|
}
|
||||||
|
xwia_buffer[j++] = '\0';
|
||||||
|
printf("%s", xwia_buffer);
|
||||||
|
printf("\n");
|
||||||
|
if (checksum != *ptr++) {
|
||||||
|
printf("XWIA checksum mismatch\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int wv_data_offs; // offset for waveform data
|
||||||
|
int waveform_buffer_size; // size for waveform data
|
||||||
|
int mode_count = header->mc + 1;
|
||||||
|
|
||||||
|
wv_data_offs = HEADER_SIZE + trt_entries + 2 + 1 + xwia_len + 1;
|
||||||
|
// This should yield the same result
|
||||||
|
if ((header->xwia) && (wv_data_offs != header->xwia + 1 + xwia_len + 1))
|
||||||
|
printf("Warning: data offset calculation mismatch\n");
|
||||||
|
printf("Waveform data offset: %d\n", wv_data_offs);
|
||||||
|
|
||||||
|
uint8_t *ptr = (uint8_t *)file_buffer + wv_data_offs;
|
||||||
|
|
||||||
|
uint32_t* wv_modes = malloc(sizeof(uint32_t) * mode_count);
|
||||||
|
|
||||||
|
uint32_t addr;
|
||||||
|
// get modes addr
|
||||||
|
for (i = 0; i < mode_count; i++) {
|
||||||
|
addr = read_pointer(ptr);
|
||||||
|
printf("wave #%d addr: %u\n", i, addr);
|
||||||
|
wv_modes[i] = addr;
|
||||||
|
ptr += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get modes temp addr
|
||||||
|
// Table offsets, ordered as first shown in the wbf, may not use up all space
|
||||||
|
uint32_t *table_offsets = malloc(sizeof(uint32_t) * mode_count * trt_entries + 1);
|
||||||
|
int *frame_counts = malloc(sizeof(uint32_t) * mode_count * trt_entries);
|
||||||
|
int tables = 0;
|
||||||
|
// Table index for each mode x temp
|
||||||
|
int *wv_modes_temps = malloc(sizeof(int) * mode_count * trt_entries);
|
||||||
|
|
||||||
|
for (i = 0; i < mode_count; i++) {
|
||||||
|
ptr = (uint8_t *)file_buffer + wv_modes[i];
|
||||||
|
for (j = 0; j < trt_entries; j++) {
|
||||||
|
addr = read_pointer(ptr);
|
||||||
|
|
||||||
|
// Find the table ID correspond to the address
|
||||||
|
// Ideally this should be an hash table, but given the extremely small
|
||||||
|
// problem size here, linear search is more than good enough.
|
||||||
|
int id = -1;
|
||||||
|
for (int k = 0; k < tables; k++)
|
||||||
|
if (table_offsets[k] == addr) {
|
||||||
|
id = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == -1) {
|
||||||
|
// New table
|
||||||
|
id = tables; // Assign ID
|
||||||
|
tables++;
|
||||||
|
table_offsets[id] = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
wv_modes_temps[i * trt_entries + j] = id;
|
||||||
|
printf("wave #%d, temp #%d: wavetable %d\n", i, j, id);
|
||||||
|
ptr += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* fn = malloc(strlen(prefix) + 14);
|
||||||
|
|
||||||
|
static uint8_t derle_buffer[MAX_TABLE_LENGTH];
|
||||||
|
|
||||||
|
for (i = 0; i < tables; i++) {
|
||||||
|
printf("Parsing table %d, addr %d (0x%06x)\n", i, table_offsets[i], table_offsets[i]);
|
||||||
|
|
||||||
|
ptr = (uint8_t *)file_buffer + table_offsets[i];
|
||||||
|
// Parse table
|
||||||
|
bool rle_mode = true;
|
||||||
|
uint32_t idx = 0;
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
while(1) {
|
||||||
|
uint8_t chr = *ptr++;
|
||||||
|
checksum += chr;
|
||||||
|
if (chr == 0xfc) {
|
||||||
|
// Toggle RLE mode
|
||||||
|
rle_mode = !rle_mode;
|
||||||
|
}
|
||||||
|
else if (chr == 0xff) {
|
||||||
|
// End of block
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!rle_mode) {
|
||||||
|
derle_buffer[idx++] = chr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint8_t len = *ptr++;
|
||||||
|
checksum += len;
|
||||||
|
for (j = 0; j < (int)len + 1; j++) {
|
||||||
|
derle_buffer[idx++] = chr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Total %d bytes. Checksum: 0x%02x\n", idx, checksum);
|
||||||
|
if (*ptr++ != checksum) {
|
||||||
|
printf("Checksum mismatch!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int phase_per_byte = acep ? 2 : 4;
|
||||||
|
int transitions = ((bpp == 4) ? (16 * 16) : (32 * 32));
|
||||||
|
int phases = idx * phase_per_byte / transitions;
|
||||||
|
frame_counts[i] = phases;
|
||||||
|
|
||||||
|
// Dump phases
|
||||||
|
sprintf(fn, "%s_TB%d.csv", prefix, i);
|
||||||
|
fp = fopen(fn, "w");
|
||||||
|
assert(fp);
|
||||||
|
size_t index = i * trt_entries + j;
|
||||||
|
dump_phases(fp, derle_buffer, bpp, phases, phase_per_byte);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(fn, "%s_desc.iwf", prefix);
|
||||||
|
fp = fopen(fn, "w");
|
||||||
|
assert(fp);
|
||||||
|
fprintf(fp, "[WAVEFORM]\n");
|
||||||
|
fprintf(fp, "VERSION = 2.0\n");
|
||||||
|
fprintf(fp, "PREFIX = %s\n", prefix);
|
||||||
|
fprintf(fp, "NAME = %s\n", xwia_buffer);
|
||||||
|
fprintf(fp, "BPP = %d\n", bpp);
|
||||||
|
fprintf(fp, "MODES = %d\n", mode_count);
|
||||||
|
fprintf(fp, "TEMPS = %d\n", trt_entries);
|
||||||
|
fprintf(fp, "TABLES = %d\n", tables);
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
for (int i = 0; i < trt_entries; i++) {
|
||||||
|
fprintf(fp, "T%dRANGE = %d\n", i, temp_range_bounds[i]);
|
||||||
|
}
|
||||||
|
fprintf(fp, "TUPBOUND = %d\n", temp_range_bounds[trt_entries]);
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
for (int i = 0; i < tables; i++) {
|
||||||
|
fprintf(fp, "TB%dFC = %d\n", i, frame_counts[i]);
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
for (int i = 0; i < mode_count; i++) {
|
||||||
|
fprintf(fp, "[MODE%d]\n", i);
|
||||||
|
if (mode_string) {
|
||||||
|
fprintf(fp, "NAME = %s\n", mode_string[i]);
|
||||||
|
}
|
||||||
|
for (int j = 0; j < trt_entries; j++) {
|
||||||
|
fprintf(fp, "T%dTABLE = %d\n", j, wv_modes_temps[i * trt_entries + j]);
|
||||||
|
}
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
printf("All done!\n");
|
||||||
|
|
||||||
|
free(fn);
|
||||||
|
free(temp_range_bounds);
|
||||||
|
free(table_offsets);
|
||||||
|
free(frame_counts);
|
||||||
|
free(wv_modes);
|
||||||
|
free(wv_modes_temps);
|
||||||
|
|
||||||
|
free(file_buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue