blob: 84f55d7a9fe4a44a2bf20d73ef8410f33d96afcc [file] [log] [blame]
Alexey Kodanev775d2262013-07-10 17:34:43 +04001/*
2 * Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved.
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
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU 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 the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author:
19 * Alexey Kodanev <alexey.kodanev@oracle.com>
20 *
21 * Test checks device firmware loading.
22 */
23
24#define _GNU_SOURCE
25#include <sys/utsname.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <string.h>
30
31#include "test.h"
Alexey Kodanev775d2262013-07-10 17:34:43 +040032#include "safe_macros.h"
33#include "tst_module.h"
34
35/* number of test firmware files */
36#define FW_FILES 5
37
38char *TCID = "fw_load";
39int TST_TOTAL = FW_FILES;
40
41static int fw_size = 0x1000;
42
43static const char fw_name[] = "load_tst.fw";
44static const char module_name[] = "ltp_fw_load.ko";
45
46/* paths to module's sysfs files */
47static const char dev_fwnum[] = "/sys/devices/ltp_fw_load/fwnum";
48static const char dev_result[] = "/sys/devices/ltp_fw_load/result";
49
50struct fw_file_info {
51 char *file;
52 char *dir;
53 int fake;
54 int remove_dir;
55 int remove_file;
56};
57
58static struct fw_file_info fw[FW_FILES];
59static int fw_num;
60
61/* test options */
62static char *narg;
63static int nflag;
64static int skip_cleanup;
65static int verbose;
66static const option_t options[] = {
67 {"n:", &nflag, &narg},
68 {"s", &skip_cleanup, NULL},
69 {"v", &verbose, NULL},
70 {NULL, NULL, NULL}
71};
72
73static void help(void);
74static void setup(int argc, char *argv[]);
75static void test_run(void);
76static void cleanup(void);
77
78/*
79 * create firmware files in the fw_paths
80 * @fw_paths: it must be termintated by a NULL pointer
81 */
82static void create_firmware(char *const fw_paths[]);
83
84int main(int argc, char *argv[])
85{
86 setup(argc, argv);
87
88 test_run();
89
90 cleanup();
91
92 tst_exit();
93}
94
95static void help(void)
96{
97 printf(" -n x Write x bytes to firmware file, default is %d\n",
98 fw_size);
99 printf(" -s Skip cleanup\n");
100 printf(" -v Verbose\n");
101}
102
103void setup(int argc, char *argv[])
104{
Cyril Hrubis0b9589f2014-05-27 17:40:33 +0200105 const char *msg;
Alexey Kodanev775d2262013-07-10 17:34:43 +0400106 msg = parse_opts(argc, argv, options, help);
107 if (msg != NULL)
108 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
109
110 if (nflag) {
111 if (sscanf(narg, "%i", &fw_size) != 1)
112 tst_brkm(TBROK, NULL, "-n option arg is not a number");
113 if (fw_size < 0)
114 tst_brkm(TBROK, NULL, "-n option arg is less than 0");
115 }
116
117 tst_require_root(NULL);
118
119 if (tst_kvercmp(3, 7, 0) < 0) {
120 tst_brkm(TCONF, NULL,
121 "Test must be run with kernel 3.7 or newer");
122 }
123
124 char fw_size_param[19];
125 snprintf(fw_size_param, 19, "fw_size=%d", fw_size);
126 char *const mod_params[2] = { fw_size_param, NULL };
127 tst_module_load(NULL, module_name, mod_params);
128
129 tst_sig(FORK, DEF_HANDLER, cleanup);
130
131 /* get current Linux version and make firmware paths */
132 struct utsname uts_name;
133 uname(&uts_name);
134
135 /* 4 firmware paths + NULL */
136 char *fw_paths[5] = { "/lib/firmware", "/lib/firmware/updates" };
Cyril Hrubisd65102a2013-07-11 18:03:38 +0200137 SAFE_ASPRINTF(cleanup, &fw_paths[2], "%s/%s", fw_paths[0], uts_name.release);
138 SAFE_ASPRINTF(cleanup, &fw_paths[3], "%s/%s", fw_paths[1], uts_name.release);
Alexey Kodanev775d2262013-07-10 17:34:43 +0400139
140 /* create firmware in the hard coded firmware search paths */
141 create_firmware(fw_paths);
142
143 free(fw_paths[2]);
144 free(fw_paths[3]);
145
146 /* make non-existent firmware file */
Cyril Hrubisd65102a2013-07-11 18:03:38 +0200147 SAFE_ASPRINTF(cleanup, &fw[fw_num].file, "/n%d_%s", fw_num, fw_name);
Alexey Kodanev775d2262013-07-10 17:34:43 +0400148 fw[fw_num].fake = 1;
149 ++fw_num;
150}
151
152static void test_run(void)
153{
154 /* initiate firmware requests */
155 SAFE_FILE_PRINTF(cleanup, dev_fwnum, "%d", fw_num);
156
157 /* get module results by reading result bit mask */
158 int result = 0;
159 SAFE_FILE_SCANF(cleanup, dev_result, "%d", &result);
160
161 int i, fail, offset;
162 for (i = 0; i < fw_num; ++i) {
163 fail = (result & (1 << i)) == 0 && !fw[i].fake;
164 offset = (fw[i].dir) ? strlen(fw[i].dir) : 0;
165 tst_resm((fail) ? TFAIL : TPASS,
166 "Expect: %s load firmware '...%s'",
167 (fw[i].fake) ? "can't" : "can",
168 fw[i].file + offset);
169 }
170}
171
172static void cleanup(void)
173{
174 if (skip_cleanup)
175 return;
176
177 int i;
178 /* remove subdirs first and then upper level dirs */
179 for (i = fw_num - 1; i >= 0; --i) {
180 if (fw[i].remove_file && remove(fw[i].file) == -1)
181 tst_resm(TWARN, "Can't remove: %s", fw[i].file);
182 free(fw[i].file);
183
184 if (fw[i].remove_dir && remove(fw[i].dir) == -1)
185 tst_resm(TWARN, "Can't remove %s", fw[i].dir);
186 free(fw[i].dir);
187 }
188
189 tst_module_unload(NULL, module_name);
Alexey Kodanev775d2262013-07-10 17:34:43 +0400190}
191
192static void create_firmware(char *const fw_paths[])
193{
194 int i = 0;
195 while (fw_paths[i] != NULL) {
196 struct fw_file_info *fi = &fw[fw_num];
197 fi->dir = strdup(fw_paths[i]);
198 if (access(fi->dir, X_OK) == -1) {
199 /* create dir */
200 SAFE_MKDIR(cleanup, fi->dir, 0755);
201 fi->remove_dir = 1;
202 }
203
204 /* create test firmware file */
Cyril Hrubisd65102a2013-07-11 18:03:38 +0200205 SAFE_ASPRINTF(cleanup, &fi->file, "%s/n%d_%s", fi->dir, fw_num, fw_name);
Alexey Kodanev775d2262013-07-10 17:34:43 +0400206
207 FILE *f = SAFE_FOPEN(cleanup, fi->file, "w");
208 fi->remove_file = 1;
209 int k, byte = fw_num;
210 ++fw_num;
211 for (k = 0; k < fw_size; ++k)
212 fputc(byte, f);
213 SAFE_FCLOSE(cleanup, f);
214 ++i;
215 }
216}