blob: 6e7103dbee9374aad685a5de8da2c70f481ba8d3 [file] [log] [blame]
Harry Liebel561cd332014-02-14 14:42:48 +00001/*
Antonio Nino Diaz5f73afb2018-02-14 11:41:26 +00002 * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved.
Harry Liebel561cd332014-02-14 14:42:48 +00003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Harry Liebel561cd332014-02-14 14:42:48 +00005 */
6
Harry Liebel561cd332014-02-14 14:42:48 +00007#include <assert.h>
Dan Handleyed6ff952014-05-14 17:44:19 +01008#include <bl_common.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +01009#include <debug.h>
10#include <errno.h>
Dan Handley714a0d22014-04-09 13:13:04 +010011#include <firmware_image_package.h>
Dan Handley714a0d22014-04-09 13:13:04 +010012#include <io_driver.h>
13#include <io_fip.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +010014#include <io_storage.h>
15#include <platform.h>
Dan Handleyed6ff952014-05-14 17:44:19 +010016#include <platform_def.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +010017#include <stdint.h>
18#include <string.h>
Douglas Raillarda8954fc2017-01-26 15:54:44 +000019#include <utils.h>
Dan Handley2bd4ef22014-04-09 13:14:54 +010020#include <uuid.h>
Harry Liebel561cd332014-02-14 14:42:48 +000021
Ruchika Gupta246e45b2018-06-27 12:18:22 +053022#ifndef MAX_FIP_DEVICES
23#define MAX_FIP_DEVICES 1
24#endif
25
Harry Liebel561cd332014-02-14 14:42:48 +000026/* Useful for printing UUIDs when debugging.*/
27#define PRINT_UUID2(x) \
28 "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", \
29 x.time_low, x.time_mid, x.time_hi_and_version, \
30 x.clock_seq_hi_and_reserved, x.clock_seq_low, \
31 x.node[0], x.node[1], x.node[2], x.node[3], \
32 x.node[4], x.node[5]
33
34typedef struct {
Harry Liebel561cd332014-02-14 14:42:48 +000035 unsigned int file_pos;
Dan Handleye2712bc2014-04-10 15:37:22 +010036 fip_toc_entry_t entry;
37} file_state_t;
Harry Liebel561cd332014-02-14 14:42:48 +000038
Ruchika Gupta246e45b2018-06-27 12:18:22 +053039/*
40 * Maintain dev_spec per FIP Device
41 * TODO - Add backend handles and file state
42 * per FIP device here once backends like io_memmap
43 * can support multiple open files
44 */
45typedef struct {
46 uintptr_t dev_spec;
47} fip_dev_state_t;
48
Roberto Vargaseace8f12018-04-26 13:36:53 +010049static const uuid_t uuid_null = { {0} };
Ruchika Gupta246e45b2018-06-27 12:18:22 +053050/*
51 * Only one file can be open across all FIP device
52 * as backends like io_memmap don't support
53 * multiple open files. The file state and
54 * backend handle should be maintained per FIP device
55 * if the same support is available in the backend
56 */
Dan Handleye2712bc2014-04-10 15:37:22 +010057static file_state_t current_file = {0};
Dan Handleya4cb68e2014-04-23 13:47:06 +010058static uintptr_t backend_dev_handle;
59static uintptr_t backend_image_spec;
Harry Liebel561cd332014-02-14 14:42:48 +000060
Ruchika Gupta246e45b2018-06-27 12:18:22 +053061static fip_dev_state_t state_pool[MAX_FIP_DEVICES];
62static io_dev_info_t dev_info_pool[MAX_FIP_DEVICES];
63
64/* Track number of allocated fip devices */
65static unsigned int fip_dev_count;
Harry Liebel561cd332014-02-14 14:42:48 +000066
67/* Firmware Image Package driver functions */
Dan Handleya4cb68e2014-04-23 13:47:06 +010068static int fip_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
69static int fip_file_open(io_dev_info_t *dev_info, const uintptr_t spec,
Dan Handleye2712bc2014-04-10 15:37:22 +010070 io_entity_t *entity);
71static int fip_file_len(io_entity_t *entity, size_t *length);
Dan Handleya4cb68e2014-04-23 13:47:06 +010072static int fip_file_read(io_entity_t *entity, uintptr_t buffer, size_t length,
Harry Liebel561cd332014-02-14 14:42:48 +000073 size_t *length_read);
Dan Handleye2712bc2014-04-10 15:37:22 +010074static int fip_file_close(io_entity_t *entity);
Dan Handleya4cb68e2014-04-23 13:47:06 +010075static int fip_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params);
Dan Handleye2712bc2014-04-10 15:37:22 +010076static int fip_dev_close(io_dev_info_t *dev_info);
Harry Liebel561cd332014-02-14 14:42:48 +000077
Harry Liebel561cd332014-02-14 14:42:48 +000078
79/* Return 0 for equal uuids. */
80static inline int compare_uuids(const uuid_t *uuid1, const uuid_t *uuid2)
81{
82 return memcmp(uuid1, uuid2, sizeof(uuid_t));
83}
84
85
86/* TODO: We could check version numbers or do a package checksum? */
Dan Handleye2712bc2014-04-10 15:37:22 +010087static inline int is_valid_header(fip_toc_header_t *header)
Harry Liebel561cd332014-02-14 14:42:48 +000088{
89 if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0)) {
90 return 1;
91 } else {
92 return 0;
93 }
94}
95
96
Harry Liebel561cd332014-02-14 14:42:48 +000097/* Identify the device type as a virtual driver */
Roberto Vargas05712702018-02-12 12:36:17 +000098static io_type_t device_type_fip(void)
Harry Liebel561cd332014-02-14 14:42:48 +000099{
100 return IO_TYPE_FIRMWARE_IMAGE_PACKAGE;
101}
102
103
Dan Handleya4cb68e2014-04-23 13:47:06 +0100104static const io_dev_connector_t fip_dev_connector = {
Harry Liebel561cd332014-02-14 14:42:48 +0000105 .dev_open = fip_dev_open
106};
107
108
Dan Handleya4cb68e2014-04-23 13:47:06 +0100109static const io_dev_funcs_t fip_dev_funcs = {
Harry Liebel561cd332014-02-14 14:42:48 +0000110 .type = device_type_fip,
111 .open = fip_file_open,
112 .seek = NULL,
113 .size = fip_file_len,
114 .read = fip_file_read,
115 .write = NULL,
116 .close = fip_file_close,
117 .dev_init = fip_dev_init,
118 .dev_close = fip_dev_close,
119};
120
Ruchika Gupta246e45b2018-06-27 12:18:22 +0530121/* Locate a file state in the pool, specified by address */
122static int find_first_fip_state(const uintptr_t dev_spec,
123 unsigned int *index_out)
124{
125 int result = -ENOENT;
126 unsigned int index;
Harry Liebel561cd332014-02-14 14:42:48 +0000127
Ruchika Gupta246e45b2018-06-27 12:18:22 +0530128 for (index = 0; index < (unsigned int)MAX_FIP_DEVICES; ++index) {
129 /* dev_spec is used as identifier since it's unique */
130 if (state_pool[index].dev_spec == dev_spec) {
131 result = 0;
132 *index_out = index;
133 break;
134 }
135 }
136 return result;
137}
Harry Liebel561cd332014-02-14 14:42:48 +0000138
139
Ruchika Gupta246e45b2018-06-27 12:18:22 +0530140/* Allocate a device info from the pool and return a pointer to it */
141static int allocate_dev_info(io_dev_info_t **dev_info)
142{
143 int result = -ENOMEM;
144
145 assert(dev_info != NULL);
146
147 if (fip_dev_count < (unsigned int)MAX_FIP_DEVICES) {
148 unsigned int index = 0;
149
150 result = find_first_fip_state(0, &index);
151 assert(result == 0);
152 /* initialize dev_info */
153 dev_info_pool[index].funcs = &fip_dev_funcs;
154 dev_info_pool[index].info =
155 (uintptr_t)&state_pool[index];
156 *dev_info = &dev_info_pool[index];
157 ++fip_dev_count;
158 }
159
160 return result;
161}
162
163/* Release a device info to the pool */
164static int free_dev_info(io_dev_info_t *dev_info)
165{
166 int result;
167 unsigned int index = 0;
168 fip_dev_state_t *state;
169
170 assert(dev_info != NULL);
171
172 state = (fip_dev_state_t *)dev_info->info;
173 result = find_first_fip_state(state->dev_spec, &index);
174 if (result == 0) {
175 /* free if device info is valid */
176 zeromem(state, sizeof(fip_dev_state_t));
177 --fip_dev_count;
178 }
179
180 return result;
181}
182
183/*
184 * Multiple FIP devices can be opened depending on the value of
185 * MAX_FIP_DEVICES. Given that there is only one backend, only a
186 * single file can be open at a time by any FIP device.
187 */
188static int fip_dev_open(const uintptr_t dev_spec,
Dan Handleye2712bc2014-04-10 15:37:22 +0100189 io_dev_info_t **dev_info)
Harry Liebel561cd332014-02-14 14:42:48 +0000190{
Ruchika Gupta246e45b2018-06-27 12:18:22 +0530191 int result;
192 io_dev_info_t *info;
193 fip_dev_state_t *state;
194
Harry Liebel561cd332014-02-14 14:42:48 +0000195 assert(dev_info != NULL);
Ruchika Gupta246e45b2018-06-27 12:18:22 +0530196#if MAX_FIP_DEVICES > 1
197 assert(dev_spec != (uintptr_t)NULL);
198#endif
199
200 result = allocate_dev_info(&info);
201 if (result != 0)
202 return -ENOMEM;
203
204 state = (fip_dev_state_t *)info->info;
205
206 state->dev_spec = dev_spec;
207
208 *dev_info = info;
Harry Liebel561cd332014-02-14 14:42:48 +0000209
Juan Castillo6e762062015-11-02 10:47:01 +0000210 return 0;
Harry Liebel561cd332014-02-14 14:42:48 +0000211}
212
213
214/* Do some basic package checks. */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100215static int fip_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params)
Harry Liebel561cd332014-02-14 14:42:48 +0000216{
Juan Castillo6e762062015-11-02 10:47:01 +0000217 int result;
Juan Castillo3a66aca2015-04-13 17:36:19 +0100218 unsigned int image_id = (unsigned int)init_params;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100219 uintptr_t backend_handle;
Dan Handleye2712bc2014-04-10 15:37:22 +0100220 fip_toc_header_t header;
Harry Liebel561cd332014-02-14 14:42:48 +0000221 size_t bytes_read;
222
223 /* Obtain a reference to the image by querying the platform layer */
Juan Castillo3a66aca2015-04-13 17:36:19 +0100224 result = plat_get_image_source(image_id, &backend_dev_handle,
Harry Liebel561cd332014-02-14 14:42:48 +0000225 &backend_image_spec);
Juan Castillo6e762062015-11-02 10:47:01 +0000226 if (result != 0) {
Juan Castillo3a66aca2015-04-13 17:36:19 +0100227 WARN("Failed to obtain reference to image id=%u (%i)\n",
228 image_id, result);
Juan Castillo6e762062015-11-02 10:47:01 +0000229 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000230 goto fip_dev_init_exit;
231 }
232
233 /* Attempt to access the FIP image */
234 result = io_open(backend_dev_handle, backend_image_spec,
235 &backend_handle);
Juan Castillo6e762062015-11-02 10:47:01 +0000236 if (result != 0) {
Juan Castillo3a66aca2015-04-13 17:36:19 +0100237 WARN("Failed to access image id=%u (%i)\n", image_id, result);
Juan Castillo6e762062015-11-02 10:47:01 +0000238 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000239 goto fip_dev_init_exit;
240 }
241
Dan Handleya4cb68e2014-04-23 13:47:06 +0100242 result = io_read(backend_handle, (uintptr_t)&header, sizeof(header),
243 &bytes_read);
Juan Castillo6e762062015-11-02 10:47:01 +0000244 if (result == 0) {
Harry Liebel561cd332014-02-14 14:42:48 +0000245 if (!is_valid_header(&header)) {
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000246 WARN("Firmware Image Package header check failed.\n");
Juan Castillo6e762062015-11-02 10:47:01 +0000247 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000248 } else {
Dan Handley91b624e2014-07-29 17:14:00 +0100249 VERBOSE("FIP header looks OK.\n");
Harry Liebel561cd332014-02-14 14:42:48 +0000250 }
251 }
252
253 io_close(backend_handle);
254
255 fip_dev_init_exit:
256 return result;
257}
258
259/* Close a connection to the FIP device */
Dan Handleye2712bc2014-04-10 15:37:22 +0100260static int fip_dev_close(io_dev_info_t *dev_info)
Harry Liebel561cd332014-02-14 14:42:48 +0000261{
262 /* TODO: Consider tracking open files and cleaning them up here */
263
264 /* Clear the backend. */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100265 backend_dev_handle = (uintptr_t)NULL;
266 backend_image_spec = (uintptr_t)NULL;
Harry Liebel561cd332014-02-14 14:42:48 +0000267
Ruchika Gupta246e45b2018-06-27 12:18:22 +0530268 return free_dev_info(dev_info);
Harry Liebel561cd332014-02-14 14:42:48 +0000269}
270
271
272/* Open a file for access from package. */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100273static int fip_file_open(io_dev_info_t *dev_info, const uintptr_t spec,
Dan Handleye2712bc2014-04-10 15:37:22 +0100274 io_entity_t *entity)
Harry Liebel561cd332014-02-14 14:42:48 +0000275{
Juan Castillo6e762062015-11-02 10:47:01 +0000276 int result;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100277 uintptr_t backend_handle;
Juan Castillo3a66aca2015-04-13 17:36:19 +0100278 const io_uuid_spec_t *uuid_spec = (io_uuid_spec_t *)spec;
Harry Liebel561cd332014-02-14 14:42:48 +0000279 size_t bytes_read;
280 int found_file = 0;
281
Juan Castillo3a66aca2015-04-13 17:36:19 +0100282 assert(uuid_spec != NULL);
Harry Liebel561cd332014-02-14 14:42:48 +0000283 assert(entity != NULL);
284
285 /* Can only have one file open at a time for the moment. We need to
286 * track state like file cursor position. We know the header lives at
287 * offset zero, so this entry should never be zero for an active file.
288 * When the system supports dynamic memory allocation we can allow more
289 * than one open file at a time if needed.
290 */
291 if (current_file.entry.offset_address != 0) {
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000292 WARN("fip_file_open : Only one open file at a time.\n");
Juan Castillo6e762062015-11-02 10:47:01 +0000293 return -ENOMEM;
Harry Liebel561cd332014-02-14 14:42:48 +0000294 }
295
296 /* Attempt to access the FIP image */
297 result = io_open(backend_dev_handle, backend_image_spec,
298 &backend_handle);
Juan Castillo6e762062015-11-02 10:47:01 +0000299 if (result != 0) {
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000300 WARN("Failed to open Firmware Image Package (%i)\n", result);
Juan Castillo6e762062015-11-02 10:47:01 +0000301 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000302 goto fip_file_open_exit;
303 }
304
305 /* Seek past the FIP header into the Table of Contents */
Dan Handleye2712bc2014-04-10 15:37:22 +0100306 result = io_seek(backend_handle, IO_SEEK_SET, sizeof(fip_toc_header_t));
Juan Castillo6e762062015-11-02 10:47:01 +0000307 if (result != 0) {
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000308 WARN("fip_file_open: failed to seek\n");
Juan Castillo6e762062015-11-02 10:47:01 +0000309 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000310 goto fip_file_open_close;
311 }
312
Harry Liebel561cd332014-02-14 14:42:48 +0000313 found_file = 0;
314 do {
Dan Handleya4cb68e2014-04-23 13:47:06 +0100315 result = io_read(backend_handle,
316 (uintptr_t)&current_file.entry,
Harry Liebel561cd332014-02-14 14:42:48 +0000317 sizeof(current_file.entry),
318 &bytes_read);
Juan Castillo6e762062015-11-02 10:47:01 +0000319 if (result == 0) {
Harry Liebel561cd332014-02-14 14:42:48 +0000320 if (compare_uuids(&current_file.entry.uuid,
Juan Castillo3a66aca2015-04-13 17:36:19 +0100321 &uuid_spec->uuid) == 0) {
Harry Liebel561cd332014-02-14 14:42:48 +0000322 found_file = 1;
323 break;
324 }
325 } else {
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000326 WARN("Failed to read FIP (%i)\n", result);
Harry Liebel561cd332014-02-14 14:42:48 +0000327 goto fip_file_open_close;
328 }
329 } while (compare_uuids(&current_file.entry.uuid, &uuid_null) != 0);
330
331 if (found_file == 1) {
332 /* All fine. Update entity info with file state and return. Set
333 * the file position to 0. The 'current_file.entry' holds the
334 * base and size of the file.
335 */
336 current_file.file_pos = 0;
337 entity->info = (uintptr_t)&current_file;
338 } else {
339 /* Did not find the file in the FIP. */
Jeenu Viswambharandd3dc322014-02-20 11:51:00 +0000340 current_file.entry.offset_address = 0;
Juan Castillo6e762062015-11-02 10:47:01 +0000341 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000342 }
343
344 fip_file_open_close:
345 io_close(backend_handle);
346
347 fip_file_open_exit:
348 return result;
349}
350
351
352/* Return the size of a file in package */
Dan Handleye2712bc2014-04-10 15:37:22 +0100353static int fip_file_len(io_entity_t *entity, size_t *length)
Harry Liebel561cd332014-02-14 14:42:48 +0000354{
355 assert(entity != NULL);
356 assert(length != NULL);
357
Dan Handleye2712bc2014-04-10 15:37:22 +0100358 *length = ((file_state_t *)entity->info)->entry.size;
Harry Liebel561cd332014-02-14 14:42:48 +0000359
Juan Castillo6e762062015-11-02 10:47:01 +0000360 return 0;
Harry Liebel561cd332014-02-14 14:42:48 +0000361}
362
363
364/* Read data from a file in package */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100365static int fip_file_read(io_entity_t *entity, uintptr_t buffer, size_t length,
Harry Liebel561cd332014-02-14 14:42:48 +0000366 size_t *length_read)
367{
Juan Castillo6e762062015-11-02 10:47:01 +0000368 int result;
Dan Handleye2712bc2014-04-10 15:37:22 +0100369 file_state_t *fp;
Harry Liebel561cd332014-02-14 14:42:48 +0000370 size_t file_offset;
371 size_t bytes_read;
Dan Handleya4cb68e2014-04-23 13:47:06 +0100372 uintptr_t backend_handle;
Harry Liebel561cd332014-02-14 14:42:48 +0000373
374 assert(entity != NULL);
Dan Handleya4cb68e2014-04-23 13:47:06 +0100375 assert(buffer != (uintptr_t)NULL);
Harry Liebel561cd332014-02-14 14:42:48 +0000376 assert(length_read != NULL);
Dan Handleya4cb68e2014-04-23 13:47:06 +0100377 assert(entity->info != (uintptr_t)NULL);
Harry Liebel561cd332014-02-14 14:42:48 +0000378
379 /* Open the backend, attempt to access the blob image */
380 result = io_open(backend_dev_handle, backend_image_spec,
381 &backend_handle);
Juan Castillo6e762062015-11-02 10:47:01 +0000382 if (result != 0) {
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000383 WARN("Failed to open FIP (%i)\n", result);
Juan Castillo6e762062015-11-02 10:47:01 +0000384 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000385 goto fip_file_read_exit;
386 }
387
Dan Handleye2712bc2014-04-10 15:37:22 +0100388 fp = (file_state_t *)entity->info;
Harry Liebel561cd332014-02-14 14:42:48 +0000389
390 /* Seek to the position in the FIP where the payload lives */
391 file_offset = fp->entry.offset_address + fp->file_pos;
392 result = io_seek(backend_handle, IO_SEEK_SET, file_offset);
Juan Castillo6e762062015-11-02 10:47:01 +0000393 if (result != 0) {
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000394 WARN("fip_file_read: failed to seek\n");
Juan Castillo6e762062015-11-02 10:47:01 +0000395 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000396 goto fip_file_read_close;
397 }
398
399 result = io_read(backend_handle, buffer, length, &bytes_read);
Juan Castillo6e762062015-11-02 10:47:01 +0000400 if (result != 0) {
Harry Liebel561cd332014-02-14 14:42:48 +0000401 /* We cannot read our data. Fail. */
Jeenu Viswambharan08c28d52014-02-20 12:03:31 +0000402 WARN("Failed to read payload (%i)\n", result);
Juan Castillo6e762062015-11-02 10:47:01 +0000403 result = -ENOENT;
Harry Liebel561cd332014-02-14 14:42:48 +0000404 goto fip_file_read_close;
405 } else {
406 /* Set caller length and new file position. */
407 *length_read = bytes_read;
408 fp->file_pos += bytes_read;
409 }
410
411/* Close the backend. */
412 fip_file_read_close:
413 io_close(backend_handle);
414
415 fip_file_read_exit:
416 return result;
417}
418
419
420/* Close a file in package */
Dan Handleye2712bc2014-04-10 15:37:22 +0100421static int fip_file_close(io_entity_t *entity)
Harry Liebel561cd332014-02-14 14:42:48 +0000422{
423 /* Clear our current file pointer.
424 * If we had malloc() we would free() here.
425 */
426 if (current_file.entry.offset_address != 0) {
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000427 zeromem(&current_file, sizeof(current_file));
Harry Liebel561cd332014-02-14 14:42:48 +0000428 }
429
430 /* Clear the Entity info. */
431 entity->info = 0;
432
Juan Castillo6e762062015-11-02 10:47:01 +0000433 return 0;
Harry Liebel561cd332014-02-14 14:42:48 +0000434}
435
436/* Exported functions */
437
438/* Register the Firmware Image Package driver with the IO abstraction */
Dan Handleya4cb68e2014-04-23 13:47:06 +0100439int register_io_dev_fip(const io_dev_connector_t **dev_con)
Harry Liebel561cd332014-02-14 14:42:48 +0000440{
Juan Castillo6e762062015-11-02 10:47:01 +0000441 int result;
Harry Liebel561cd332014-02-14 14:42:48 +0000442 assert(dev_con != NULL);
443
Ruchika Gupta246e45b2018-06-27 12:18:22 +0530444 /*
445 * Since dev_info isn't really used in io_register_device, always
446 * use the same device info at here instead.
447 */
448 result = io_register_device(&dev_info_pool[0]);
Juan Castillo6e762062015-11-02 10:47:01 +0000449 if (result == 0)
Harry Liebel561cd332014-02-14 14:42:48 +0000450 *dev_con = &fip_dev_connector;
451
452 return result;
453}