more minor cleanups
[hmcfgusb] / firmware.c
1 /* generic firmware-functions for HomeMatic
2 *
3 * Copyright (c) 2014 Michael Gernoth <michael@gernoth.net>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <sys/time.h>
35
36 #include "firmware.h"
37
38 /* This might be wrong, but it works for current fw */
39 #define MAX_BLOCK_LENGTH 512
40
41 static uint8_t ascii_to_nibble(uint8_t a)
42 {
43 uint8_t c = 0x00;
44
45 if ((a >= '0') && (a <= '9')) {
46 c = a - '0';
47 } else if ((a >= 'A') && (a <= 'F')) {
48 c = (a - 'A') + 10;
49 } else if ((a >= 'a') && (a <= 'f')) {
50 c = (a - 'a') + 10;
51 }
52
53 return c;
54 }
55
56 static int validate_nibble(uint8_t a)
57 {
58 if (((a >= '0') && (a <= '9')) ||
59 ((a >= 'A') && (a <= 'F')) ||
60 ((a >= 'a') && (a <= 'f')))
61 return 1;
62
63 return 0;
64 }
65
66 struct firmware* firmware_read_firmware(char *filename, int debug)
67 {
68 struct firmware *fw;
69 struct stat stat_buf;
70 uint8_t buf[4096];
71 uint16_t len;
72 int fd;
73 int r;
74 int i;
75
76 fw = malloc(sizeof(struct firmware));
77 if (!fw) {
78 perror("malloc(fw)");
79 return NULL;
80 }
81
82 memset(fw, 0, sizeof(struct firmware));
83
84 if (stat(filename, &stat_buf) == -1) {
85 fprintf(stderr, "Can't stat %s: %s\n", filename, strerror(errno));
86 exit(EXIT_FAILURE);
87 }
88
89 fd = open(filename, O_RDONLY);
90 if (fd < 0) {
91 fprintf(stderr, "Can't open %s: %s", filename, strerror(errno));
92 exit(EXIT_FAILURE);
93 }
94
95 printf("Reading firmware from %s...\n", filename);
96 do {
97 memset(buf, 0, sizeof(buf));
98 r = read(fd, buf, 4);
99 if (r < 0) {
100 perror("read");
101 exit(EXIT_FAILURE);
102 } else if (r == 0) {
103 break;
104 } else if (r != 4) {
105 printf("can't get length information!\n");
106 exit(EXIT_FAILURE);
107 }
108
109 for (i = 0; i < r; i++) {
110 if (!validate_nibble(buf[i])) {
111 fprintf(stderr, "Firmware file not valid!\n");
112 exit(EXIT_FAILURE);
113 }
114 }
115
116 len = (ascii_to_nibble(buf[0]) & 0xf)<< 4;
117 len |= ascii_to_nibble(buf[1]) & 0xf;
118 len <<= 8;
119 len |= (ascii_to_nibble(buf[2]) & 0xf)<< 4;
120 len |= ascii_to_nibble(buf[3]) & 0xf;
121
122 if (len > MAX_BLOCK_LENGTH) {
123 fprintf(stderr, "Invalid block-length %u > %u for block %d!\n", len, MAX_BLOCK_LENGTH, fw->fw_blocks+1);
124 exit(EXIT_FAILURE);
125 }
126
127 fw->fw = realloc(fw->fw, sizeof(uint8_t*) * (fw->fw_blocks + 1));
128 if (fw->fw == NULL) {
129 perror("Can't reallocate fw->fw-blocklist");
130 exit(EXIT_FAILURE);
131 }
132
133 fw->fw[fw->fw_blocks] = malloc(len + 4);
134 if (fw->fw[fw->fw_blocks] == NULL) {
135 perror("Can't allocate memory for fw->fw-block");
136 exit(EXIT_FAILURE);
137 }
138
139 fw->fw[fw->fw_blocks][0] = (fw->fw_blocks >> 8) & 0xff;
140 fw->fw[fw->fw_blocks][1] = fw->fw_blocks & 0xff;
141 fw->fw[fw->fw_blocks][2] = (len >> 8) & 0xff;
142 fw->fw[fw->fw_blocks][3] = len & 0xff;
143
144 r = read(fd, buf, len * 2);
145 if (r < 0) {
146 perror("read");
147 exit(EXIT_FAILURE);
148 } else if (r < len * 2) {
149 fprintf(stderr, "short read, aborting (%d < %d)\n", r, len * 2);
150 exit(EXIT_FAILURE);
151 }
152
153 for (i = 0; i < r; i+=2) {
154 if ((!validate_nibble(buf[i])) ||
155 (!validate_nibble(buf[i+1]))) {
156 fprintf(stderr, "Firmware file not valid!\n");
157 exit(EXIT_FAILURE);
158 }
159
160 fw->fw[fw->fw_blocks][(i/2) + 4] = (ascii_to_nibble(buf[i]) & 0xf)<< 4;
161 fw->fw[fw->fw_blocks][(i/2) + 4] |= ascii_to_nibble(buf[i+1]) & 0xf;
162 }
163
164 fw->fw_blocks++;
165 if (debug)
166 printf("Firmware block %d with length %u read.\n", fw->fw_blocks, len);
167 } while(r > 0);
168
169 if (fw->fw_blocks == 0) {
170 fprintf(stderr, "Firmware file not valid!\n");
171 exit(EXIT_FAILURE);
172 }
173
174 printf("Firmware with %d blocks successfully read.\n", fw->fw_blocks);
175
176 return fw;
177 }
178
179 void firmware_free(struct firmware *fw)
180 {
181 int i;
182
183 for (i = 0; i < fw->fw_blocks; i++)
184 free(fw->fw[i]);
185
186 free(fw->fw);
187 free(fw);
188 }
Impressum, Datenschutz