1/*
2 * Copyright 2009-2010, Colin G��nther, coling@gmx.de.
3 * All Rights Reserved. Distributed under the terms of the MIT License.
4 *
5 */
6
7
8#include <posix/sys/mman.h>
9
10#include <compat/sys/param.h>
11#include <compat/sys/firmware.h>
12#include <compat/sys/haiku-module.h>
13
14#include <stdlib.h>
15#include <string.h>
16
17#include <FindDirectory.h>
18#include <StorageDefs.h>
19#include <SupportDefs.h>
20
21#include <device.h>
22
23
24#define MAX_FBSD_FIRMWARE_NAME_CHARS 64
25	// For strndup, beeing cautious in kernel code is a good thing.
26	// NB: This constant doesn't exist in FreeBSD.
27
28
29static const char*
30getHaikuFirmwareName(const char* fbsdFirmwareName,
31	const char* unknownFirmwareName)
32{
33	int i;
34
35	if (__haiku_firmware_name_map == NULL)
36		return unknownFirmwareName;
37
38	for (i = 0; i < __haiku_firmware_parts_count; i++) {
39		if (strcmp(__haiku_firmware_name_map[i][0], fbsdFirmwareName) == 0)
40			return __haiku_firmware_name_map[i][1];
41	}
42	return unknownFirmwareName;
43}
44
45
46const struct firmware*
47firmware_get(const char* fbsdFirmwareName)
48{
49	char*				fbsdFirmwareNameCopy = NULL;
50	int					fileDescriptor = -1;
51	struct firmware*	firmware = NULL;
52	int32				firmwareFileSize;
53	char*				firmwarePath = NULL;
54	const char*			haikuFirmwareName = NULL;
55	ssize_t				readCount = 0;
56	directory_which		checkDirs[] = { B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
57							B_SYSTEM_DATA_DIRECTORY };
58	size_t				numCheckDirs
59							= sizeof(checkDirs) / sizeof(checkDirs[0]);
60	size_t				i = 0;
61
62	haikuFirmwareName = getHaikuFirmwareName(fbsdFirmwareName,
63		fbsdFirmwareName);
64
65	firmwarePath = (char*)malloc(B_PATH_NAME_LENGTH);
66	if (firmwarePath == NULL)
67		goto cleanup;
68
69
70	for (; i < numCheckDirs; i++) {
71		if (find_directory(checkDirs[i], -1, false, firmwarePath,
72				B_PATH_NAME_LENGTH) != B_OK) {
73			continue;
74		}
75
76		strlcat(firmwarePath, "/firmware/", B_PATH_NAME_LENGTH);
77		strlcat(firmwarePath, gDriverName, B_PATH_NAME_LENGTH);
78		strlcat(firmwarePath, "/", B_PATH_NAME_LENGTH);
79		strlcat(firmwarePath, haikuFirmwareName, B_PATH_NAME_LENGTH);
80
81		fileDescriptor = open(firmwarePath, B_READ_ONLY);
82		if (fileDescriptor >= 0)
83			break;
84	}
85
86	if (fileDescriptor < 0)
87		goto cleanup;
88
89	firmwareFileSize = lseek(fileDescriptor, 0, SEEK_END);
90	if (firmwareFileSize == -1)
91		goto cleanup;
92
93	lseek(fileDescriptor, 0, SEEK_SET);
94
95	fbsdFirmwareNameCopy = strndup(fbsdFirmwareName,
96		MAX_FBSD_FIRMWARE_NAME_CHARS);
97	if (fbsdFirmwareNameCopy == NULL)
98		goto cleanup;
99
100	firmware = (struct firmware*)malloc(sizeof(struct firmware));
101	if (firmware == NULL)
102		goto cleanup;
103
104	firmware->data = malloc(firmwareFileSize);
105	if (firmware->data == NULL)
106		goto cleanup;
107
108	readCount = read(fileDescriptor, (void*)firmware->data, firmwareFileSize);
109	if (readCount == -1 || readCount < firmwareFileSize) {
110		free((void*)firmware->data);
111		goto cleanup;
112	}
113
114	firmware->datasize = firmwareFileSize;
115	firmware->name = fbsdFirmwareNameCopy;
116	firmware->version = __haiku_firmware_version;
117
118	close(fileDescriptor);
119	free(firmwarePath);
120	return firmware;
121
122cleanup:
123	if (firmware)
124		free(firmware);
125	if (fbsdFirmwareNameCopy)
126		free(fbsdFirmwareNameCopy);
127	if (firmwarePath)
128		free(firmwarePath);
129	if (fileDescriptor >= 0)
130		close(fileDescriptor);
131	return NULL;
132}
133
134
135void
136firmware_put(const struct firmware* firmware, int flags)
137{
138	if (firmware == NULL)
139		return;
140
141	if (firmware->data)
142		free((void*)firmware->data);
143	if (firmware->name)
144		free((void*)firmware->name);
145	free((void*)firmware);
146}
147