xref: /openbmc/u-boot/api/api_storage.c (revision 13ca6305)
1 /*
2  * (C) Copyright 2007-2008 Semihalf
3  *
4  * Written by: Rafal Jaworowski <raj@semihalf.com>
5  *
6  * See file CREDITS for list of people who contributed to this
7  * project.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  * MA 02111-1307 USA
23  *
24  */
25 
26 #include <config.h>
27 
28 #if defined(CONFIG_API)
29 
30 #include <common.h>
31 #include <api_public.h>
32 
33 #define DEBUG
34 #undef DEBUG
35 
36 #ifdef DEBUG
37 #define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt, ##args); } while (0)
38 #else
39 #define debugf(fmt, args...)
40 #endif
41 
42 #define errf(fmt, args...) do { printf("ERROR @ %s(): ", __func__); printf(fmt, ##args); } while (0)
43 
44 
45 #define ENUM_IDE	0
46 #define ENUM_USB	1
47 #define ENUM_SCSI	2
48 #define ENUM_MMC	3
49 #define ENUM_MAX	4
50 
51 struct stor_spec {
52 	int		max_dev;
53 	int		enum_started;
54 	int		enum_ended;
55 	int		type;		/* "external" type: DT_STOR_{IDE,USB,etc} */
56 	char		*name;
57 };
58 
59 static struct stor_spec specs[ENUM_MAX] = { { 0, 0, 0, 0, "" }, };
60 
61 
62 void dev_stor_init(void)
63 {
64 #if defined(CONFIG_CMD_IDE)
65 	specs[ENUM_IDE].max_dev = CFG_IDE_MAXDEVICE;
66 	specs[ENUM_IDE].enum_started = 0;
67 	specs[ENUM_IDE].enum_ended = 0;
68 	specs[ENUM_IDE].type = DEV_TYP_STOR | DT_STOR_IDE;
69 	specs[ENUM_IDE].name = "ide";
70 #endif
71 #if defined(CONFIG_CMD_USB)
72 	specs[ENUM_USB].max_dev = USB_MAX_STOR_DEV;
73 	specs[ENUM_USB].enum_started = 0;
74 	specs[ENUM_USB].enum_ended = 0;
75 	specs[ENUM_USB].type = DEV_TYP_STOR | DT_STOR_USB;
76 	specs[ENUM_USB].name = "usb";
77 #endif
78 #if defined(CONFIG_CMD_SCSI)
79 	specs[ENUM_SCSI].max_dev = CFG_SCSI_MAX_DEVICE;
80 	specs[ENUM_SCSI].enum_started = 0;
81 	specs[ENUM_SCSI].enum_ended = 0;
82 	specs[ENUM_SCSI].type = DEV_TYP_STOR | DT_STOR_SCSI;
83 	specs[ENUM_SCSI].name = "scsi";
84 #endif
85 }
86 
87 /*
88  * Finds next available device in the storage group
89  *
90  * type:	storage group type - ENUM_IDE, ENUM_SCSI etc.
91  *
92  * first:	if 1 the first device in the storage group is returned (if
93  *              exists), if 0 the next available device is searched
94  *
95  * more:	returns 0/1 depending if there are more devices in this group
96  *		available (for future iterations)
97  *
98  * returns:	0/1 depending if device found in this iteration
99  */
100 static int dev_stor_get(int type, int first, int *more, struct device_info *di)
101 {
102 	int found = 0;
103 	*more = 0;
104 
105 	int i;
106 
107 	block_dev_desc_t *dd;
108 
109 	if (first) {
110 		di->cookie = (void *)get_dev(specs[type].name, 0);
111 		if (di->cookie == NULL)
112 			return 0;
113 		else
114 			found = 1;
115 
116 	} else {
117 		for (i = 0; i < specs[type].max_dev; i++)
118 			if (di->cookie == (void *)get_dev(specs[type].name, i)) {
119 				/* previous cookie found -- advance to the
120 				 * next device, if possible */
121 
122 				if (++i >= specs[type].max_dev) {
123 					/* out of range, no more to enum */
124 					di->cookie = NULL;
125 					break;
126 				}
127 
128 				di->cookie = (void *)get_dev(specs[type].name, i);
129 				if (di->cookie == NULL)
130 					return 0;
131 				else
132 					found = 1;
133 
134 				/* provide hint if there are more devices in
135 				 * this group to enumerate */
136 				if ((i + 1) < specs[type].max_dev)
137 					*more = 1;
138 
139 				break;
140 			}
141 	}
142 
143 	if (found) {
144 		di->type = specs[type].type;
145 
146 		if (di->cookie != NULL) {
147 			dd = (block_dev_desc_t *)di->cookie;
148 			if (dd->type == DEV_TYPE_UNKNOWN) {
149 				debugf("device instance exists, but is not active..");
150 				found = 0;
151 			} else {
152 				di->di_stor.block_count = dd->lba;
153 				di->di_stor.block_size = dd->blksz;
154 			}
155 		}
156 
157 	} else
158 		di->cookie = NULL;
159 
160 	return found;
161 }
162 
163 
164 /*
165  * returns:	ENUM_IDE, ENUM_USB etc. based on block_dev_desc_t
166  */
167 static int dev_stor_type(block_dev_desc_t *dd)
168 {
169 	int i, j;
170 
171 	for (i = ENUM_IDE; i < ENUM_MAX; i++)
172 		for (j = 0; j < specs[i].max_dev; j++)
173 			if (dd == get_dev(specs[i].name, j))
174 				return i;
175 
176 	return ENUM_MAX;
177 }
178 
179 
180 /*
181  * returns:	0/1 whether cookie points to some device in this group
182  */
183 static int dev_is_stor(int type, struct device_info *di)
184 {
185 	return (dev_stor_type(di->cookie) == type) ? 1 : 0;
186 }
187 
188 
189 static int dev_enum_stor(int type, struct device_info *di)
190 {
191 	int found = 0, more = 0;
192 
193 	debugf("called, type %d\n", type);
194 
195 	/*
196 	 * Formulae for enumerating storage devices:
197 	 * 1. if cookie (hint from previous enum call) is NULL we start again
198 	 *    with enumeration, so return the first available device, done.
199 	 *
200 	 * 2. if cookie is not NULL, check if it identifies some device in
201 	 *    this group:
202 	 *
203 	 * 2a. if cookie is a storage device from our group (IDE, USB etc.),
204 	 *     return next available (if exists) in this group
205 	 *
206 	 * 2b. if it isn't device from our group, check if such devices were
207 	 *     ever enumerated before:
208 	 *     - if not, return the first available device from this group
209 	 *     - else return 0
210 	 */
211 
212 	if (di->cookie == NULL) {
213 
214 		debugf("group%d - enum restart\n", type);
215 
216 		/*
217 		 * 1. Enumeration (re-)started: take the first available
218 		 * device, if exists
219 		 */
220 		found = dev_stor_get(type, 1, &more, di);
221 		specs[type].enum_started = 1;
222 
223 	} else if (dev_is_stor(type, di)) {
224 
225 		debugf("group%d - enum continued for the next device\n", type);
226 
227 		if (specs[type].enum_ended) {
228 			debugf("group%d - nothing more to enum!\n", type);
229 			return 0;
230 		}
231 
232 		/* 2a. Attempt to take a next available device in the group */
233 		found = dev_stor_get(type, 0, &more, di);
234 
235 	} else {
236 
237 		if (specs[type].enum_ended) {
238 			debugf("group %d - already enumerated, skipping\n", type);
239 			return 0;
240 		}
241 
242 		debugf("group%d - first time enum\n", type);
243 
244 		if (specs[type].enum_started == 0) {
245 			/*
246 			 * 2b.  If enumerating devices in this group did not
247 			 * happen before, it means the cookie pointed to a
248 			 * device frome some other group (another storage
249 			 * group, or network); in this case try to take the
250 			 * first available device from our group
251 			 */
252 			specs[type].enum_started = 1;
253 
254 			/*
255 			 * Attempt to take the first device in this group:
256 			 *'first element' flag is set
257 			 */
258 			found = dev_stor_get(type, 1, &more, di);
259 
260 		} else {
261 			errf("group%d - out of order iteration\n", type);
262 			found = 0;
263 			more = 0;
264 		}
265 	}
266 
267 	/*
268 	 * If there are no more devices in this group, consider its
269 	 * enumeration finished
270 	 */
271 	specs[type].enum_ended = (!more) ? 1 : 0;
272 
273 	if (found)
274 		debugf("device found, returning cookie 0x%08x\n",
275 			(u_int32_t)di->cookie);
276 	else
277 		debugf("no device found\n");
278 
279 	return found;
280 }
281 
282 void dev_enum_reset(void)
283 {
284 	int i;
285 
286 	for (i = 0; i < ENUM_MAX; i ++) {
287 		specs[i].enum_started = 0;
288 		specs[i].enum_ended = 0;
289 	}
290 }
291 
292 int dev_enum_storage(struct device_info *di)
293 {
294 	int i;
295 
296 	/*
297 	 * check: ide, usb, scsi, mmc
298 	 */
299 	for (i = ENUM_IDE; i < ENUM_MAX; i ++) {
300 		if (dev_enum_stor(i, di))
301 			return 1;
302 	}
303 
304 	return 0;
305 }
306 
307 static int dev_stor_is_valid(int type, block_dev_desc_t *dd)
308 {
309 	int i;
310 
311 	for (i = 0; i < specs[type].max_dev; i++)
312 		if (dd == get_dev(specs[type].name, i))
313 			if (dd->type != DEV_TYPE_UNKNOWN)
314 				return 1;
315 
316 	return 0;
317 }
318 
319 
320 int dev_open_stor(void *cookie)
321 {
322 	int type = dev_stor_type(cookie);
323 
324 	if (type == ENUM_MAX)
325 		return API_ENODEV;
326 
327 	if (dev_stor_is_valid(type, (block_dev_desc_t *)cookie))
328 		return 0;
329 
330 	return API_ENODEV;
331 }
332 
333 
334 int dev_close_stor(void *cookie)
335 {
336 	/*
337 	 * Not much to do as we actually do not alter storage devices upon
338 	 * close
339 	 */
340 	return 0;
341 }
342 
343 
344 static int dev_stor_index(block_dev_desc_t *dd)
345 {
346 	int i, type;
347 
348 	type = dev_stor_type(dd);
349 	for (i = 0; i < specs[type].max_dev; i++)
350 		if (dd == get_dev(specs[type].name, i))
351 			return i;
352 
353 	return (specs[type].max_dev);
354 }
355 
356 
357 lbasize_t dev_read_stor(void *cookie, void *buf, lbasize_t len, lbastart_t start)
358 {
359 	int type;
360 	block_dev_desc_t *dd = (block_dev_desc_t *)cookie;
361 
362 	if ((type = dev_stor_type(dd)) == ENUM_MAX)
363 		return 0;
364 
365 	if (!dev_stor_is_valid(type, dd))
366 		return 0;
367 
368 	if ((dd->block_read) == NULL) {
369 		debugf("no block_read() for device 0x%08x\n", cookie);
370 		return 0;
371 	}
372 
373 	return (dd->block_read(dev_stor_index(dd), start, len, buf));
374 }
375 
376 #endif /* CONFIG_API */
377