]> git.ipfire.org Git - thirdparty/u-boot.git/blob - drivers/dfu/dfu_mmc.c
dfu: defer parsing of device string to IO backend
[thirdparty/u-boot.git] / drivers / dfu / dfu_mmc.c
1 /*
2 * dfu.c -- DFU back-end routines
3 *
4 * Copyright (C) 2012 Samsung Electronics
5 * author: Lukasz Majewski <l.majewski@samsung.com>
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 */
9
10 #include <common.h>
11 #include <malloc.h>
12 #include <errno.h>
13 #include <div64.h>
14 #include <dfu.h>
15 #include <ext4fs.h>
16 #include <fat.h>
17 #include <mmc.h>
18
19 static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE)
20 dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE];
21 static long dfu_file_buf_len;
22
23 static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part)
24 {
25 int ret;
26
27 if (part == mmc->part_num)
28 return 0;
29
30 ret = mmc_switch_part(dfu->data.mmc.dev_num, part);
31 if (ret) {
32 error("Cannot switch to partition %d\n", part);
33 return ret;
34 }
35 mmc->part_num = part;
36
37 return 0;
38 }
39
40 static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
41 u64 offset, void *buf, long *len)
42 {
43 struct mmc *mmc = find_mmc_device(dfu->data.mmc.dev_num);
44 u32 blk_start, blk_count, n = 0;
45 int ret, part_num_bkp = 0;
46
47 /*
48 * We must ensure that we work in lba_blk_size chunks, so ALIGN
49 * this value.
50 */
51 *len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
52
53 blk_start = dfu->data.mmc.lba_start +
54 (u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
55 blk_count = *len / dfu->data.mmc.lba_blk_size;
56 if (blk_start + blk_count >
57 dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
58 puts("Request would exceed designated area!\n");
59 return -EINVAL;
60 }
61
62 if (dfu->data.mmc.hw_partition >= 0) {
63 part_num_bkp = mmc->part_num;
64 ret = mmc_access_part(dfu, mmc, dfu->data.mmc.hw_partition);
65 if (ret)
66 return ret;
67 }
68
69 debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
70 op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
71 dfu->data.mmc.dev_num, blk_start, blk_count, buf);
72 switch (op) {
73 case DFU_OP_READ:
74 n = mmc->block_dev.block_read(dfu->data.mmc.dev_num, blk_start,
75 blk_count, buf);
76 break;
77 case DFU_OP_WRITE:
78 n = mmc->block_dev.block_write(dfu->data.mmc.dev_num, blk_start,
79 blk_count, buf);
80 break;
81 default:
82 error("Operation not supported\n");
83 }
84
85 if (n != blk_count) {
86 error("MMC operation failed");
87 if (dfu->data.mmc.hw_partition >= 0)
88 mmc_access_part(dfu, mmc, part_num_bkp);
89 return -EIO;
90 }
91
92 if (dfu->data.mmc.hw_partition >= 0) {
93 ret = mmc_access_part(dfu, mmc, part_num_bkp);
94 if (ret)
95 return ret;
96 }
97
98 return 0;
99 }
100
101 static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
102 {
103 if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
104 dfu_file_buf_len = 0;
105 return -EINVAL;
106 }
107
108 /* Add to the current buffer. */
109 memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
110 dfu_file_buf_len += *len;
111
112 return 0;
113 }
114
115 static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
116 void *buf, long *len)
117 {
118 const char *fsname, *opname;
119 char cmd_buf[DFU_CMD_BUF_SIZE];
120 char *str_env;
121 int ret;
122
123 switch (dfu->layout) {
124 case DFU_FS_FAT:
125 fsname = "fat";
126 break;
127 case DFU_FS_EXT4:
128 fsname = "ext4";
129 break;
130 default:
131 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
132 dfu_get_layout(dfu->layout));
133 return -1;
134 }
135
136 switch (op) {
137 case DFU_OP_READ:
138 opname = "load";
139 break;
140 case DFU_OP_WRITE:
141 opname = "write";
142 break;
143 case DFU_OP_SIZE:
144 opname = "size";
145 break;
146 default:
147 return -1;
148 }
149
150 sprintf(cmd_buf, "%s%s mmc %d:%d", fsname, opname,
151 dfu->data.mmc.dev, dfu->data.mmc.part);
152
153 if (op != DFU_OP_SIZE)
154 sprintf(cmd_buf + strlen(cmd_buf), " 0x%x", (unsigned int)buf);
155
156 sprintf(cmd_buf + strlen(cmd_buf), " %s", dfu->name);
157
158 if (op == DFU_OP_WRITE)
159 sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len);
160
161 debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
162
163 ret = run_command(cmd_buf, 0);
164 if (ret) {
165 puts("dfu: Read error!\n");
166 return ret;
167 }
168
169 if (op != DFU_OP_WRITE) {
170 str_env = getenv("filesize");
171 if (str_env == NULL) {
172 puts("dfu: Wrong file size!\n");
173 return -1;
174 }
175 *len = simple_strtoul(str_env, NULL, 16);
176 }
177
178 return ret;
179 }
180
181 int dfu_write_medium_mmc(struct dfu_entity *dfu,
182 u64 offset, void *buf, long *len)
183 {
184 int ret = -1;
185
186 switch (dfu->layout) {
187 case DFU_RAW_ADDR:
188 ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
189 break;
190 case DFU_FS_FAT:
191 case DFU_FS_EXT4:
192 ret = mmc_file_buffer(dfu, buf, len);
193 break;
194 default:
195 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
196 dfu_get_layout(dfu->layout));
197 }
198
199 return ret;
200 }
201
202 int dfu_flush_medium_mmc(struct dfu_entity *dfu)
203 {
204 int ret = 0;
205
206 if (dfu->layout != DFU_RAW_ADDR) {
207 /* Do stuff here. */
208 ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf,
209 &dfu_file_buf_len);
210
211 /* Now that we're done */
212 dfu_file_buf_len = 0;
213 }
214
215 return ret;
216 }
217
218 long dfu_get_medium_size_mmc(struct dfu_entity *dfu)
219 {
220 int ret;
221 long len;
222
223 switch (dfu->layout) {
224 case DFU_RAW_ADDR:
225 return dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size;
226 case DFU_FS_FAT:
227 case DFU_FS_EXT4:
228 ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, &len);
229 if (ret < 0)
230 return ret;
231 return len;
232 default:
233 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
234 dfu_get_layout(dfu->layout));
235 return -1;
236 }
237 }
238
239 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
240 long *len)
241 {
242 int ret = -1;
243
244 switch (dfu->layout) {
245 case DFU_RAW_ADDR:
246 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
247 break;
248 case DFU_FS_FAT:
249 case DFU_FS_EXT4:
250 ret = mmc_file_op(DFU_OP_READ, dfu, buf, len);
251 break;
252 default:
253 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
254 dfu_get_layout(dfu->layout));
255 }
256
257 return ret;
258 }
259
260 /*
261 * @param s Parameter string containing space-separated arguments:
262 * 1st:
263 * raw (raw read/write)
264 * fat (files)
265 * ext4 (^)
266 * part (partition image)
267 * 2nd and 3rd:
268 * lba_start and lba_size, for raw write
269 * mmc_dev and mmc_part, for filesystems and part
270 * 4th (optional):
271 * mmcpart <num> (access to HW eMMC partitions)
272 */
273 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
274 {
275 const char *entity_type;
276 size_t second_arg;
277 size_t third_arg;
278
279 struct mmc *mmc;
280
281 const char *argv[3];
282 const char **parg = argv;
283
284 dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10);
285
286 for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
287 *parg = strsep(&s, " ");
288 if (*parg == NULL) {
289 error("Invalid number of arguments.\n");
290 return -ENODEV;
291 }
292 }
293
294 entity_type = argv[0];
295 /*
296 * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
297 * with default 10.
298 */
299 second_arg = simple_strtoul(argv[1], NULL, 0);
300 third_arg = simple_strtoul(argv[2], NULL, 0);
301
302 mmc = find_mmc_device(dfu->data.mmc.dev_num);
303 if (mmc == NULL) {
304 error("Couldn't find MMC device no. %d.\n",
305 dfu->data.mmc.dev_num);
306 return -ENODEV;
307 }
308
309 if (mmc_init(mmc)) {
310 error("Couldn't init MMC device.\n");
311 return -ENODEV;
312 }
313
314 dfu->data.mmc.hw_partition = -EINVAL;
315 if (!strcmp(entity_type, "raw")) {
316 dfu->layout = DFU_RAW_ADDR;
317 dfu->data.mmc.lba_start = second_arg;
318 dfu->data.mmc.lba_size = third_arg;
319 dfu->data.mmc.lba_blk_size = mmc->read_bl_len;
320
321 /*
322 * Check for an extra entry at dfu_alt_info env variable
323 * specifying the mmc HW defined partition number
324 */
325 if (s)
326 if (!strcmp(strsep(&s, " "), "mmcpart"))
327 dfu->data.mmc.hw_partition =
328 simple_strtoul(s, NULL, 0);
329
330 } else if (!strcmp(entity_type, "part")) {
331 disk_partition_t partinfo;
332 block_dev_desc_t *blk_dev = &mmc->block_dev;
333 int mmcdev = second_arg;
334 int mmcpart = third_arg;
335
336 if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) {
337 error("Couldn't find part #%d on mmc device #%d\n",
338 mmcpart, mmcdev);
339 return -ENODEV;
340 }
341
342 dfu->layout = DFU_RAW_ADDR;
343 dfu->data.mmc.lba_start = partinfo.start;
344 dfu->data.mmc.lba_size = partinfo.size;
345 dfu->data.mmc.lba_blk_size = partinfo.blksz;
346 } else if (!strcmp(entity_type, "fat")) {
347 dfu->layout = DFU_FS_FAT;
348 } else if (!strcmp(entity_type, "ext4")) {
349 dfu->layout = DFU_FS_EXT4;
350 } else {
351 error("Memory layout (%s) not supported!\n", entity_type);
352 return -ENODEV;
353 }
354
355 /* if it's NOT a raw write */
356 if (strcmp(entity_type, "raw")) {
357 dfu->data.mmc.dev = second_arg;
358 dfu->data.mmc.part = third_arg;
359 }
360
361 dfu->dev_type = DFU_DEV_MMC;
362 dfu->get_medium_size = dfu_get_medium_size_mmc;
363 dfu->read_medium = dfu_read_medium_mmc;
364 dfu->write_medium = dfu_write_medium_mmc;
365 dfu->flush_medium = dfu_flush_medium_mmc;
366 dfu->inited = 0;
367
368 return 0;
369 }