[fdt] Add ability to parse a MAC address from a flattened device tree
[ipxe.git] / src / image / pnm.c
1 /*
2 * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /** @file
27 *
28 * Portable anymap format (PNM)
29 *
30 */
31
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <ipxe/image.h>
36 #include <ipxe/pixbuf.h>
37 #include <ipxe/pnm.h>
38
39 /**
40 * Extract PNM ASCII value
41 *
42 * @v image PNM image
43 * @v pnm PNM context
44 * @ret value Value, or negative error
45 */
46 static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
47 char buf[ pnm->ascii_len + 1 /* NUL */ ];
48 char *endp;
49 size_t len;
50 int value;
51 int in_comment = 0;
52
53 /* Skip any leading whitespace and comments */
54 for ( ; pnm->offset < image->len ; pnm->offset++ ) {
55 copy_from_user ( &buf[0], image->data, pnm->offset,
56 sizeof ( buf[0] ) );
57 if ( in_comment ) {
58 if ( buf[0] == '\n' )
59 in_comment = 0;
60 } else {
61 if ( buf[0] == '#' ) {
62 in_comment = 1;
63 } else if ( ! isspace ( buf[0] ) ) {
64 break;
65 }
66 }
67 }
68
69 /* Fail if no value is present */
70 len = ( image->len - pnm->offset );
71 if ( len == 0 ) {
72 DBGC ( image, "PNM %s ran out of ASCII data\n", image->name );
73 return -EINVAL;
74 }
75
76 /* Copy ASCII value to buffer and ensure string is NUL-terminated */
77 if ( len > ( sizeof ( buf ) - 1 /* NUL */ ) )
78 len = ( sizeof ( buf ) - 1 /* NUL */ );
79 copy_from_user ( buf, image->data, pnm->offset, len );
80 buf[len] = '\0';
81
82 /* Parse value and update offset */
83 value = strtoul ( buf, &endp, 0 );
84 pnm->offset += ( endp - buf );
85
86 /* Check and skip terminating whitespace character, if present */
87 if ( ( pnm->offset != image->len ) && ( *endp != '\0' ) ) {
88 if ( ! isspace ( *endp ) ) {
89 DBGC ( image, "PNM %s invalid ASCII integer\n",
90 image->name );
91 return -EINVAL;
92 }
93 pnm->offset++;
94 }
95
96 return value;
97 }
98
99 /**
100 * Extract PNM binary value
101 *
102 * @v image PNM image
103 * @v pnm PNM context
104 * @ret value Value, or negative error
105 */
106 static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
107 uint8_t value;
108
109 /* Sanity check */
110 if ( pnm->offset == image->len ) {
111 DBGC ( image, "PNM %s ran out of binary data\n",
112 image->name );
113 return -EINVAL;
114 }
115
116 /* Extract value */
117 copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) );
118 pnm->offset++;
119
120 return value;
121 }
122
123 /**
124 * Scale PNM scalar value
125 *
126 * @v image PNM image
127 * @v pnm PNM context
128 * @v value Raw value
129 * @ret value Scaled value (in range 0-255)
130 */
131 static int pnm_scale ( struct image *image, struct pnm_context *pnm,
132 unsigned int value ) {
133
134 if ( value > pnm->max ) {
135 DBGC ( image, "PNM %s has out-of-range value %d (max %d)\n",
136 image->name, value, pnm->max );
137 return -EINVAL;
138 }
139 return ( ( 255 * value ) / pnm->max );
140 }
141
142 /**
143 * Convert PNM bitmap composite value to RGB
144 *
145 * @v composite Composite value
146 * @v index Pixel index within this composite value
147 * @ret rgb 24-bit RGB value
148 */
149 static uint32_t pnm_bitmap ( uint32_t composite, unsigned int index ) {
150
151 /* Composite value is an 8-bit bitmask */
152 return ( ( ( composite << index ) & 0x80 ) ? 0x000000 : 0xffffff );
153 }
154
155 /**
156 * Convert PNM greymap composite value to RGB
157 *
158 * @v composite Composite value
159 * @v index Pixel index within this composite value
160 * @ret rgb 24-bit RGB value
161 */
162 static uint32_t pnm_greymap ( uint32_t composite, unsigned int index __unused ){
163
164 /* Composite value is an 8-bit greyscale value */
165 return ( ( composite << 16 ) | ( composite << 8 ) | composite );
166 }
167
168 /**
169 * Convert PNM pixmap composite value to RGB
170 *
171 * @v composite Composite value
172 * @v index Pixel index within this composite value
173 * @ret rgb 24-bit RGB value
174 */
175 static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) {
176
177 /* Composite value is already an RGB value */
178 return composite;
179 }
180
181 /**
182 * Extract PNM pixel data
183 *
184 * @v image PNM image
185 * @v pnm PNM context
186 * @v pixbuf Pixel buffer
187 * @ret rc Return status code
188 */
189 static int pnm_data ( struct image *image, struct pnm_context *pnm,
190 struct pixel_buffer *pixbuf ) {
191 struct pnm_type *type = pnm->type;
192 size_t offset = 0;
193 unsigned int xpos = 0;
194 int scalar;
195 uint32_t composite;
196 uint32_t rgb;
197 unsigned int i;
198
199 /* Fill pixel buffer */
200 while ( offset < pixbuf->len ) {
201
202 /* Extract a scaled composite scalar value from the file */
203 composite = 0;
204 for ( i = 0 ; i < type->depth ; i++ ) {
205 scalar = type->scalar ( image, pnm );
206 if ( scalar < 0 )
207 return scalar;
208 scalar = pnm_scale ( image, pnm, scalar );
209 if ( scalar < 0 )
210 return scalar;
211 composite = ( ( composite << 8 ) | scalar );
212 }
213
214 /* Extract 24-bit RGB values from composite value */
215 for ( i = 0 ; i < type->packing ; i++ ) {
216 if ( offset >= pixbuf->len ) {
217 DBGC ( image, "PNM %s has too many pixels\n",
218 image->name );
219 return -EINVAL;
220 }
221 rgb = type->rgb ( composite, i );
222 copy_to_user ( pixbuf->data, offset, &rgb,
223 sizeof ( rgb ) );
224 offset += sizeof ( rgb );
225 if ( ++xpos == pixbuf->width ) {
226 xpos = 0;
227 break;
228 }
229 }
230 }
231
232 return 0;
233 }
234
235 /** PNM image types */
236 static struct pnm_type pnm_types[] = {
237 {
238 .type = '1',
239 .depth = 1,
240 .packing = 1,
241 .flags = PNM_BITMAP,
242 .scalar = pnm_ascii,
243 .rgb = pnm_bitmap,
244 },
245 {
246 .type = '2',
247 .depth = 1,
248 .packing = 1,
249 .scalar = pnm_ascii,
250 .rgb = pnm_greymap,
251 },
252 {
253 .type = '3',
254 .depth = 3,
255 .packing = 1,
256 .scalar = pnm_ascii,
257 .rgb = pnm_pixmap,
258 },
259 {
260 .type = '4',
261 .depth = 1,
262 .packing = 8,
263 .flags = PNM_BITMAP,
264 .scalar = pnm_binary,
265 .rgb = pnm_bitmap,
266 },
267 {
268 .type = '5',
269 .depth = 1,
270 .packing = 1,
271 .scalar = pnm_binary,
272 .rgb = pnm_greymap,
273 },
274 {
275 .type = '6',
276 .depth = 3,
277 .packing = 1,
278 .scalar = pnm_binary,
279 .rgb = pnm_pixmap,
280 },
281 };
282
283 /**
284 * Determine PNM image type
285 *
286 * @v image PNM image
287 * @ret type PNM image type, or NULL if not found
288 */
289 static struct pnm_type * pnm_type ( struct image *image ) {
290 struct pnm_signature signature;
291 struct pnm_type *type;
292 unsigned int i;
293
294 /* Extract signature */
295 assert ( image->len >= sizeof ( signature ) );
296 copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
297
298 /* Check for supported types */
299 for ( i = 0 ; i < ( sizeof ( pnm_types ) /
300 sizeof ( pnm_types[0] ) ) ; i++ ) {
301 type = &pnm_types[i];
302 if ( type->type == signature.type )
303 return type;
304 }
305 return NULL;
306 }
307
308 /**
309 * Convert PNM image to pixel buffer
310 *
311 * @v image PNM image
312 * @v pixbuf Pixel buffer to fill in
313 * @ret rc Return status code
314 */
315 static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
316 struct pnm_context pnm;
317 int width;
318 int height;
319 int max;
320 int rc;
321
322 /* Initialise PNM context */
323 pnm.type = pnm_type ( image );
324 if ( ! pnm.type ) {
325 rc = -ENOTSUP;
326 goto err_type;
327 }
328 pnm.offset = sizeof ( struct pnm_signature );
329 pnm.ascii_len = PNM_ASCII_LEN;
330
331 /* Extract width */
332 if ( ( width = pnm_ascii ( image, &pnm ) ) < 0 ) {
333 rc = width;
334 goto err_width;
335 }
336
337 /* Extract height */
338 if ( ( height = pnm_ascii ( image, &pnm ) ) < 0 ) {
339 rc = height;
340 goto err_height;
341 }
342
343 /* Extract maximum scalar value, if not predefined */
344 if ( pnm.type->flags & PNM_BITMAP ) {
345 pnm.max = ( ( 1 << pnm.type->packing ) - 1 );
346 pnm.ascii_len = 1;
347 } else {
348 if ( ( max = pnm_ascii ( image, &pnm ) ) < 0 ) {
349 rc = max;
350 goto err_max;
351 }
352 pnm.max = max;
353 }
354 if ( pnm.max == 0 ) {
355 DBGC ( image, "PNM %s has invalid maximum value 0\n",
356 image->name );
357 rc = -EINVAL;
358 goto err_max;
359 }
360 DBGC ( image, "PNM %s is type %c width %d height %d max %d\n",
361 image->name, pnm.type->type, width, height, pnm.max );
362
363 /* Allocate pixel buffer */
364 *pixbuf = alloc_pixbuf ( width, height );
365 if ( ! *pixbuf ) {
366 rc = -ENOMEM;
367 goto err_alloc_pixbuf;
368 }
369
370 /* Extract pixel data */
371 if ( ( rc = pnm_data ( image, &pnm, *pixbuf ) ) != 0 )
372 goto err_data;
373
374 return 0;
375
376 err_data:
377 pixbuf_put ( *pixbuf );
378 err_alloc_pixbuf:
379 err_max:
380 err_height:
381 err_width:
382 err_type:
383 return rc;
384 }
385
386 /**
387 * Probe PNM image
388 *
389 * @v image PNM image
390 * @ret rc Return status code
391 */
392 static int pnm_probe ( struct image *image ) {
393 struct pnm_signature signature;
394
395 /* Sanity check */
396 if ( image->len < sizeof ( signature ) ) {
397 DBGC ( image, "PNM %s is too short\n", image->name );
398 return -ENOEXEC;
399 }
400
401 /* Check signature */
402 copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
403 if ( ! ( ( signature.magic == PNM_MAGIC ) &&
404 ( isdigit ( signature.type ) ) &&
405 ( isspace ( signature.space ) ) ) ) {
406 DBGC ( image, "PNM %s has invalid signature\n", image->name );
407 return -ENOEXEC;
408 }
409 DBGC ( image, "PNM %s is type %c\n", image->name, signature.type );
410
411 return 0;
412 }
413
414 /** PNM image type */
415 struct image_type pnm_image_type __image_type ( PROBE_NORMAL ) = {
416 .name = "PNM",
417 .probe = pnm_probe,
418 .pixbuf = pnm_pixbuf,
419 };