]>
Commit | Line | Data |
---|---|---|
cb383cd2 ŁM |
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 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | */ | |
21 | ||
22 | #include <common.h> | |
23 | #include <malloc.h> | |
1b6ca18b | 24 | #include <errno.h> |
ea2453d5 | 25 | #include <div64.h> |
cb383cd2 ŁM |
26 | #include <dfu.h> |
27 | ||
28 | enum dfu_mmc_op { | |
29 | DFU_OP_READ = 1, | |
30 | DFU_OP_WRITE, | |
31 | }; | |
32 | ||
ea2453d5 PA |
33 | static unsigned char __aligned(CONFIG_SYS_CACHELINE_SIZE) |
34 | dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; | |
35 | static long dfu_file_buf_len; | |
36 | ||
cb383cd2 | 37 | static int mmc_block_op(enum dfu_mmc_op op, struct dfu_entity *dfu, |
ea2453d5 | 38 | u64 offset, void *buf, long *len) |
cb383cd2 ŁM |
39 | { |
40 | char cmd_buf[DFU_CMD_BUF_SIZE]; | |
ea2453d5 PA |
41 | u32 blk_start, blk_count; |
42 | ||
43 | /* | |
44 | * We must ensure that we work in lba_blk_size chunks, so ALIGN | |
45 | * this value. | |
46 | */ | |
47 | *len = ALIGN(*len, dfu->data.mmc.lba_blk_size); | |
48 | ||
49 | blk_start = dfu->data.mmc.lba_start + | |
50 | (u32)lldiv(offset, dfu->data.mmc.lba_blk_size); | |
51 | blk_count = *len / dfu->data.mmc.lba_blk_size; | |
52 | if (blk_start + blk_count > | |
53 | dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) { | |
54 | puts("Request would exceed designated area!\n"); | |
55 | return -EINVAL; | |
56 | } | |
cb383cd2 | 57 | |
ea2453d5 | 58 | sprintf(cmd_buf, "mmc %s %p %x %x", |
cb383cd2 | 59 | op == DFU_OP_READ ? "read" : "write", |
ea2453d5 | 60 | buf, blk_start, blk_count); |
cb383cd2 ŁM |
61 | |
62 | debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); | |
63 | return run_command(cmd_buf, 0); | |
64 | } | |
65 | ||
ea2453d5 | 66 | static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len) |
cb383cd2 | 67 | { |
ea2453d5 PA |
68 | if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) { |
69 | dfu_file_buf_len = 0; | |
70 | return -EINVAL; | |
71 | } | |
cb383cd2 | 72 | |
ea2453d5 PA |
73 | /* Add to the current buffer. */ |
74 | memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len); | |
75 | dfu_file_buf_len += *len; | |
76 | ||
77 | return 0; | |
cb383cd2 ŁM |
78 | } |
79 | ||
80 | static int mmc_file_op(enum dfu_mmc_op op, struct dfu_entity *dfu, | |
81 | void *buf, long *len) | |
82 | { | |
83 | char cmd_buf[DFU_CMD_BUF_SIZE]; | |
84 | char *str_env; | |
85 | int ret; | |
86 | ||
43e66272 ŁM |
87 | switch (dfu->layout) { |
88 | case DFU_FS_FAT: | |
ea2453d5 | 89 | sprintf(cmd_buf, "fat%s mmc %d:%d 0x%x %s", |
43e66272 ŁM |
90 | op == DFU_OP_READ ? "load" : "write", |
91 | dfu->data.mmc.dev, dfu->data.mmc.part, | |
ea2453d5 PA |
92 | (unsigned int) buf, dfu->name); |
93 | if (op == DFU_OP_WRITE) | |
94 | sprintf(cmd_buf + strlen(cmd_buf), " %lx", *len); | |
43e66272 ŁM |
95 | break; |
96 | case DFU_FS_EXT4: | |
ea2453d5 | 97 | sprintf(cmd_buf, "ext4%s mmc %d:%d 0x%x /%s", |
43e66272 ŁM |
98 | op == DFU_OP_READ ? "load" : "write", |
99 | dfu->data.mmc.dev, dfu->data.mmc.part, | |
ea2453d5 | 100 | (unsigned int) buf, dfu->name); |
051f9a3e ŁM |
101 | if (op == DFU_OP_WRITE) |
102 | sprintf(cmd_buf + strlen(cmd_buf), " %ld", *len); | |
43e66272 ŁM |
103 | break; |
104 | default: | |
105 | printf("%s: Layout (%s) not (yet) supported!\n", __func__, | |
106 | dfu_get_layout(dfu->layout)); | |
ea2453d5 | 107 | return -1; |
43e66272 | 108 | } |
cb383cd2 ŁM |
109 | |
110 | debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf); | |
111 | ||
112 | ret = run_command(cmd_buf, 0); | |
113 | if (ret) { | |
114 | puts("dfu: Read error!\n"); | |
115 | return ret; | |
116 | } | |
117 | ||
81c1d7b6 | 118 | if (dfu->layout != DFU_RAW_ADDR && op == DFU_OP_READ) { |
cb383cd2 ŁM |
119 | str_env = getenv("filesize"); |
120 | if (str_env == NULL) { | |
121 | puts("dfu: Wrong file size!\n"); | |
122 | return -1; | |
123 | } | |
124 | *len = simple_strtoul(str_env, NULL, 16); | |
125 | } | |
126 | ||
127 | return ret; | |
128 | } | |
129 | ||
ea2453d5 PA |
130 | int dfu_write_medium_mmc(struct dfu_entity *dfu, |
131 | u64 offset, void *buf, long *len) | |
cb383cd2 ŁM |
132 | { |
133 | int ret = -1; | |
134 | ||
135 | switch (dfu->layout) { | |
136 | case DFU_RAW_ADDR: | |
ea2453d5 | 137 | ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len); |
cb383cd2 ŁM |
138 | break; |
139 | case DFU_FS_FAT: | |
43e66272 | 140 | case DFU_FS_EXT4: |
ea2453d5 | 141 | ret = mmc_file_buffer(dfu, buf, len); |
cb383cd2 ŁM |
142 | break; |
143 | default: | |
144 | printf("%s: Layout (%s) not (yet) supported!\n", __func__, | |
145 | dfu_get_layout(dfu->layout)); | |
146 | } | |
147 | ||
148 | return ret; | |
149 | } | |
150 | ||
ea2453d5 PA |
151 | int dfu_flush_medium_mmc(struct dfu_entity *dfu) |
152 | { | |
153 | int ret = 0; | |
154 | ||
155 | if (dfu->layout != DFU_RAW_ADDR) { | |
156 | /* Do stuff here. */ | |
157 | ret = mmc_file_op(DFU_OP_WRITE, dfu, &dfu_file_buf, | |
158 | &dfu_file_buf_len); | |
159 | ||
160 | /* Now that we're done */ | |
161 | dfu_file_buf_len = 0; | |
162 | } | |
163 | ||
164 | return ret; | |
165 | } | |
166 | ||
167 | int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, | |
168 | long *len) | |
cb383cd2 ŁM |
169 | { |
170 | int ret = -1; | |
171 | ||
172 | switch (dfu->layout) { | |
173 | case DFU_RAW_ADDR: | |
ea2453d5 | 174 | ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); |
cb383cd2 ŁM |
175 | break; |
176 | case DFU_FS_FAT: | |
43e66272 | 177 | case DFU_FS_EXT4: |
ea2453d5 | 178 | ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); |
cb383cd2 ŁM |
179 | break; |
180 | default: | |
181 | printf("%s: Layout (%s) not (yet) supported!\n", __func__, | |
182 | dfu_get_layout(dfu->layout)); | |
183 | } | |
184 | ||
185 | return ret; | |
186 | } | |
187 | ||
188 | int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s) | |
189 | { | |
1b6ca18b PA |
190 | int dev, part; |
191 | struct mmc *mmc; | |
192 | block_dev_desc_t *blk_dev; | |
193 | disk_partition_t partinfo; | |
cb383cd2 ŁM |
194 | char *st; |
195 | ||
196 | dfu->dev_type = DFU_DEV_MMC; | |
197 | st = strsep(&s, " "); | |
198 | if (!strcmp(st, "mmc")) { | |
199 | dfu->layout = DFU_RAW_ADDR; | |
200 | dfu->data.mmc.lba_start = simple_strtoul(s, &s, 16); | |
201 | dfu->data.mmc.lba_size = simple_strtoul(++s, &s, 16); | |
202 | dfu->data.mmc.lba_blk_size = get_mmc_blk_size(dfu->dev_num); | |
203 | } else if (!strcmp(st, "fat")) { | |
204 | dfu->layout = DFU_FS_FAT; | |
43e66272 ŁM |
205 | } else if (!strcmp(st, "ext4")) { |
206 | dfu->layout = DFU_FS_EXT4; | |
1b6ca18b PA |
207 | } else if (!strcmp(st, "part")) { |
208 | ||
209 | dfu->layout = DFU_RAW_ADDR; | |
210 | ||
211 | dev = simple_strtoul(s, &s, 10); | |
212 | s++; | |
213 | part = simple_strtoul(s, &s, 10); | |
214 | ||
215 | mmc = find_mmc_device(dev); | |
216 | if (mmc == NULL || mmc_init(mmc)) { | |
ea2453d5 PA |
217 | printf("%s: could not find mmc device #%d!\n", |
218 | __func__, dev); | |
1b6ca18b PA |
219 | return -ENODEV; |
220 | } | |
221 | ||
222 | blk_dev = &mmc->block_dev; | |
223 | if (get_partition_info(blk_dev, part, &partinfo) != 0) { | |
224 | printf("%s: could not find partition #%d on mmc device #%d!\n", | |
ea2453d5 | 225 | __func__, part, dev); |
1b6ca18b PA |
226 | return -ENODEV; |
227 | } | |
228 | ||
229 | dfu->data.mmc.lba_start = partinfo.start; | |
230 | dfu->data.mmc.lba_size = partinfo.size; | |
231 | dfu->data.mmc.lba_blk_size = partinfo.blksz; | |
232 | ||
cb383cd2 ŁM |
233 | } else { |
234 | printf("%s: Memory layout (%s) not supported!\n", __func__, st); | |
1b6ca18b | 235 | return -ENODEV; |
cb383cd2 ŁM |
236 | } |
237 | ||
43e66272 ŁM |
238 | if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { |
239 | dfu->data.mmc.dev = simple_strtoul(s, &s, 10); | |
240 | dfu->data.mmc.part = simple_strtoul(++s, &s, 10); | |
241 | } | |
242 | ||
cb383cd2 ŁM |
243 | dfu->read_medium = dfu_read_medium_mmc; |
244 | dfu->write_medium = dfu_write_medium_mmc; | |
ea2453d5 PA |
245 | dfu->flush_medium = dfu_flush_medium_mmc; |
246 | ||
247 | /* initial state */ | |
248 | dfu->inited = 0; | |
cb383cd2 ŁM |
249 | |
250 | return 0; | |
251 | } |