comparison packages/io/disk/current/src/disk.c @ 1476:f2453fae08d4

Added support for FAT filesystem. Several packages, see the individual ChangeLogs for details.
author nickg
date Mon, 19 Jan 2004 14:35:01 +0000
parents
children b1d2762b2eec
comparison
equal deleted inserted replaced
1475:c9e09b1c18cb 1476:f2453fae08d4
1 //==========================================================================
2 //
3 // io/disk/disk.c
4 //
5 // High level disk driver
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2003 Savin Zlobec
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // -------------------------------------------
37 //####ECOSGPLCOPYRIGHTEND####
38 //==========================================================================
39 //#####DESCRIPTIONBEGIN####
40 //
41 // Author(s): savin
42 // Date: 2003-06-10
43 // Purpose: Top level disk driver
44 // Description:
45 //
46 //####DESCRIPTIONEND####
47 //
48 //==========================================================================
49
50 #include <pkgconf/io.h>
51 #include <pkgconf/io_disk.h>
52
53 #include <cyg/io/io.h>
54 #include <cyg/io/devtab.h>
55 #include <cyg/io/disk.h>
56 #include <cyg/infra/cyg_ass.h> // assertion support
57 #include <cyg/infra/diag.h> // diagnostic output
58
59 #include <stdlib.h> // malloc
60
61 // ---------------------------------------------------------------------------
62
63 //#define DEBUG 1
64
65 #ifdef DEBUG
66 # define D(_args_) diag_printf _args_
67 #else
68 # define D(_args_)
69 #endif
70
71 // ---------------------------------------------------------------------------
72
73 // Master Boot Record defines
74 #define MBR_SIG_ADDR 0x1FE // signature address
75 #define MBR_SIG_BYTE0 0x55 // signature first byte value
76 #define MBR_SIG_BYTE1 0xAA // signature second byte value
77 #define MBR_PART_ADDR 0x1BE // first partition address
78 #define MBR_PART_SIZE 0x10 // partition size
79 #define MBR_PART_NUM 4 // number of partitions
80
81 // Get cylinders, heads and sectors from data (MBR partition format)
82 #define READ_CHS(_data_, _c_, _h_, _s_) \
83 do { \
84 _h_ = (*((cyg_uint8 *)_data_)); \
85 _s_ = (*(((cyg_uint8 *)_data_)+1) & 0x3F); \
86 _c_ = (*(((cyg_uint8 *)_data_)+1) & ~0x3F) << 2 | \
87 (*(((cyg_uint8 *)_data_)+2)); \
88 } while (0)
89
90 // Get double word from data (MBR partition format)
91 #define READ_DWORD(_data_, _val_) \
92 do { \
93 _val_ = *((cyg_uint8 *)_data_) | \
94 *(((cyg_uint8 *)_data_)+1) << 8 | \
95 *(((cyg_uint8 *)_data_)+2) << 16 | \
96 *(((cyg_uint8 *)_data_)+3) << 24; \
97 } while (0)
98
99 // Convert cylinders, heads and sectors to LBA sectors
100 #define CHS_TO_LBA(_info_, _c_, _h_, _s_, _lba_) \
101 (_lba_=(((_c_)*(_info_)->heads_num+(_h_))*(_info_)->sectors_num)+(_s_)-1)
102
103 // ---------------------------------------------------------------------------
104
105 static Cyg_ErrNo disk_bread(cyg_io_handle_t handle,
106 void *buf,
107 cyg_uint32 *len,
108 cyg_uint32 pos);
109
110 static Cyg_ErrNo disk_bwrite(cyg_io_handle_t handle,
111 const void *buf,
112 cyg_uint32 *len,
113 cyg_uint32 pos);
114
115 static Cyg_ErrNo disk_select(cyg_io_handle_t handle,
116 cyg_uint32 which,
117 CYG_ADDRWORD info);
118
119 static Cyg_ErrNo disk_get_config(cyg_io_handle_t handle,
120 cyg_uint32 key,
121 void *buf,
122 cyg_uint32 *len);
123
124 static Cyg_ErrNo disk_set_config(cyg_io_handle_t handle,
125 cyg_uint32 key,
126 const void *buf,
127 cyg_uint32 *len);
128
129 BLOCK_DEVIO_TABLE(cyg_io_disk_devio,
130 disk_bwrite,
131 disk_bread,
132 disk_select,
133 disk_get_config,
134 disk_set_config
135 );
136
137 static cyg_bool disk_init(struct cyg_devtab_entry *tab);
138
139 static Cyg_ErrNo disk_connected(struct cyg_devtab_entry *tab,
140 cyg_disk_identify_t *ident);
141
142 static Cyg_ErrNo disk_disconnected(struct cyg_devtab_entry *tab);
143
144 static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab,
145 struct cyg_devtab_entry *sub_tab,
146 const char *name);
147
148 DISK_CALLBACKS(cyg_io_disk_callbacks,
149 disk_init,
150 disk_connected,
151 disk_disconnected,
152 disk_lookup
153 );
154
155 // ---------------------------------------------------------------------------
156
157 //
158 // Read partition from data
159 //
160 static void
161 read_partition(cyg_uint8 *data,
162 cyg_disk_info_t *info,
163 cyg_disk_partition_t *part)
164 {
165 cyg_uint16 c, h, s;
166
167 part->type = data[4];
168 part->state = data[0];
169
170 READ_CHS(&data[1], c, h, s);
171 CHS_TO_LBA(&info->ident, c, h, s, part->start);
172
173 READ_CHS(&data[5], c, h, s);
174 CHS_TO_LBA(&info->ident, c, h, s, part->end);
175
176 READ_DWORD(&data[12], part->size);
177 }
178
179 //
180 // Read Master Boot Record (partitions)
181 //
182 static Cyg_ErrNo
183 read_mbr(disk_channel *chan)
184 {
185 cyg_disk_info_t *info = chan->info;
186 disk_funs *funs = chan->funs;
187 cyg_uint8 buf[512];
188 Cyg_ErrNo res = ENOERR;
189 int i;
190
191 for (i = 0; i < MBR_PART_NUM; i++)
192 info->partitions[i].type = 0x00;
193
194 res = (funs->read)(chan, (void *)buf, 512, 0);
195 if (ENOERR != res)
196 return res;
197
198 if (MBR_SIG_BYTE0 == buf[MBR_SIG_ADDR+0] && MBR_SIG_BYTE1 == buf[MBR_SIG_ADDR+1])
199 {
200 D(("disk MBR found\n"));
201
202 for (i = 0; i < MBR_PART_NUM; i++)
203 {
204 cyg_disk_partition_t *part = &info->partitions[i];
205
206 read_partition(&buf[MBR_PART_ADDR+MBR_PART_SIZE*i], info, part);
207 #ifdef DEBUG
208 if (0x00 != part->type)
209 {
210 D(("\ndisk MBR partition %d:\n", i));
211 D((" type = %02X\n", part->type));
212 D((" state = %02X\n", part->state));
213 D((" start = %d\n", part->start));
214 D((" end = %d\n", part->end));
215 D((" size = %d\n\n", part->size));
216 }
217 #endif
218 }
219 }
220 return ENOERR;
221 }
222
223 static cyg_bool
224 disk_init(struct cyg_devtab_entry *tab)
225 {
226 disk_channel *chan = (disk_channel *) tab->priv;
227 cyg_disk_info_t *info = chan->info;
228 int i;
229
230 if (!chan->init)
231 {
232 info->connected = false;
233
234 // clear devices array (one per partition)
235 // and partition data
236 for (i = 0; i < MBR_PART_NUM; i++)
237 {
238 info->devs[i] = (cyg_addrword_t) 0;
239 info->partitions[i].type = 0x00;
240 }
241
242 chan->init = true;
243 }
244 return true;
245 }
246
247 static Cyg_ErrNo
248 disk_connected(struct cyg_devtab_entry *tab,
249 cyg_disk_identify_t *ident)
250 {
251 disk_channel *chan = (disk_channel *) tab->priv;
252 cyg_disk_info_t *info = chan->info;
253 Cyg_ErrNo res = ENOERR;
254
255 if (!chan->init)
256 return -EINVAL;
257
258 info->ident = *ident;
259 info->block_size = 512;
260 info->blocks_num = ident->lba_sectors_num;
261
262 D(("disk connected\n"));
263 D((" serial = '%s'\n", ident->serial));
264 D((" firmware rev = '%s'\n", ident->firmware_rev));
265 D((" model num = '%s'\n", ident->model_num));
266 D((" block_size = %d\n", info->block_size));
267 D((" blocks_num = %d\n", info->blocks_num));
268
269 if (chan->mbr_support)
270 {
271 // read disk master boot record
272 res = read_mbr(chan);
273 }
274
275 if (ENOERR == res)
276 {
277 // now declare that we are connected
278 info->connected = true;
279 chan->valid = true;
280 }
281 return res;
282 }
283
284 static Cyg_ErrNo
285 disk_disconnected(struct cyg_devtab_entry *tab)
286 {
287 disk_channel *chan = (disk_channel *) tab->priv;
288 cyg_disk_info_t *info = chan->info;
289 int i;
290
291 if (!chan->init)
292 return -EINVAL;
293
294 info->connected = false;
295 chan->valid = false;
296
297 // clear partition data and invalidate
298 // any allocated devices
299 for (i = 0; i < MBR_PART_NUM; i++)
300 {
301 info->partitions[i].type = 0x00;
302
303 if (0 != info->devs[i])
304 {
305 struct cyg_devtab_entry *dtab;
306 disk_channel *dchan;
307
308 dtab = (struct cyg_devtab_entry *)info->devs[i];
309 dchan = (disk_channel *) dtab->priv;
310
311 dchan->valid = false;
312 }
313 }
314
315
316 D(("disk disconnected\n"));
317
318 return ENOERR;
319 }
320
321 static Cyg_ErrNo
322 disk_lookup(struct cyg_devtab_entry **tab,
323 struct cyg_devtab_entry *sub_tab,
324 const char *name)
325 {
326 disk_channel *chan = (disk_channel *) (*tab)->priv;
327 cyg_disk_info_t *info = chan->info;
328 struct cyg_devtab_entry *new_tab;
329 disk_channel *new_chan;
330 int dev_num;
331
332 if (!info->connected || name[0] < '0' || name[0] > '4' || '\0' != name[1])
333 return -EINVAL;
334
335 dev_num = name[0] - '0';
336
337 D(("disk lookup dev number = %d\n", dev_num));
338
339 if (0 == dev_num)
340 {
341 // 0 is the root device number
342 return ENOERR;
343 }
344 if (0x00 == info->partitions[dev_num-1].type)
345 {
346 D(("disk NO partition for dev\n"));
347 return -EINVAL;
348 }
349
350 if (0 == info->devs[dev_num-1])
351 {
352 D(("disk creating new devtab entry\n"));
353
354 // alloc mem for new device
355 new_tab = (struct cyg_devtab_entry *)
356 malloc(sizeof(struct cyg_devtab_entry));
357 if (NULL == new_tab)
358 return -ENOMEM;
359
360 // alloc mem for new device private data
361 new_chan = (disk_channel *)malloc(sizeof(disk_channel));
362 if (NULL == new_chan)
363 {
364 free(new_tab);
365 return -ENOMEM;
366 }
367 }
368 else
369 {
370 new_tab = (struct cyg_devtab_entry *) info->devs[dev_num-1];
371 new_chan = (disk_channel *) new_tab->priv;
372 }
373
374 // copy device data from parent
375 *new_tab = **tab;
376 *new_chan = *chan;
377
378 new_tab->priv = (void *)new_chan;
379
380 // set partition ptr and put this device into devices array
381 new_chan->partition = &info->partitions[dev_num-1];
382 chan->info->devs[dev_num-1] = (cyg_addrword_t) new_tab;
383
384 // return device tab
385 *tab = new_tab;
386
387 return ENOERR;
388 }
389
390 // ---------------------------------------------------------------------------
391
392 static Cyg_ErrNo
393 disk_bread(cyg_io_handle_t handle,
394 void *buf,
395 cyg_uint32 *len,
396 cyg_uint32 pos)
397 {
398 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
399 disk_channel *chan = (disk_channel *) t->priv;
400 disk_funs *funs = chan->funs;
401 cyg_disk_info_t *info = chan->info;
402 cyg_uint32 size = *len;
403 cyg_uint8 *bbuf = (cyg_uint8 *)buf;
404 Cyg_ErrNo res = ENOERR;
405 cyg_uint32 last;
406
407 if (!info->connected || !chan->valid)
408 return -EINVAL;
409
410 if (NULL != chan->partition)
411 {
412 pos += chan->partition->start;
413 last = chan->partition->end;
414 }
415 else
416 {
417 last = info->blocks_num-1;
418 }
419
420 D(("disk read block=%d len=%d buf=%p\n", pos, *len, buf));
421
422 while (size > 0)
423 {
424 if (pos > last)
425 {
426 res = -EIO;
427 break;
428 }
429
430 res = (funs->read)(chan, (void*)bbuf, info->block_size, pos);
431 if (ENOERR != res)
432 break;
433
434 if (!info->connected)
435 {
436 res = -EINVAL;
437 break;
438 }
439
440 bbuf += info->block_size;
441 pos++;
442 size--;
443 }
444 *len -= size;
445 return res;
446 }
447
448 // ---------------------------------------------------------------------------
449
450 static Cyg_ErrNo
451 disk_bwrite(cyg_io_handle_t handle,
452 const void *buf,
453 cyg_uint32 *len,
454 cyg_uint32 pos)
455 {
456 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
457 disk_channel *chan = (disk_channel *) t->priv;
458 disk_funs *funs = chan->funs;
459 cyg_disk_info_t *info = chan->info;
460 cyg_uint32 size = *len;
461 cyg_uint8 *bbuf = (cyg_uint8 * const) buf;
462 Cyg_ErrNo res = ENOERR;
463 cyg_uint32 last;
464
465 if (!info->connected || !chan->valid)
466 return -EINVAL;
467
468 if (NULL != chan->partition)
469 {
470 pos += chan->partition->start;
471 last = chan->partition->end;
472 }
473 else
474 {
475 last = info->blocks_num-1;
476 }
477
478 D(("disk write block=%d len=%d buf=%p\n", pos, *len, buf));
479
480 while (size > 0)
481 {
482 if (pos > last)
483 {
484 res = -EIO;
485 break;
486 }
487
488 res = (funs->write)(chan, (void*)bbuf, info->block_size, pos);
489 if (ENOERR != res)
490 break;
491
492 if (!info->connected)
493 {
494 res = -EINVAL;
495 break;
496 }
497
498 bbuf += info->block_size;
499 pos++;
500 size--;
501 }
502 *len -= size;
503 return res;
504 }
505
506 // ---------------------------------------------------------------------------
507
508 static cyg_bool
509 disk_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
510 {
511 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
512 disk_channel *chan = (disk_channel *) t->priv;
513 cyg_disk_info_t *cinfo = chan->info;
514
515 if (!cinfo->connected || !chan->valid)
516 return false;
517 else
518 return true;
519 }
520
521 // ---------------------------------------------------------------------------
522
523 static Cyg_ErrNo
524 disk_get_config(cyg_io_handle_t handle,
525 cyg_uint32 key,
526 void *xbuf,
527 cyg_uint32 *len)
528 {
529 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
530 disk_channel *chan = (disk_channel *) t->priv;
531 cyg_disk_info_t *info = chan->info;
532 cyg_disk_info_t *buf = (cyg_disk_info_t *) xbuf;
533 disk_funs *funs = chan->funs;
534 Cyg_ErrNo res = ENOERR;
535
536 if (!info->connected || !chan->valid)
537 return -EINVAL;
538
539 D(("disk get config key=%d\n", key));
540
541 switch (key) {
542 case CYG_IO_GET_CONFIG_DISK_INFO:
543 if (*len < sizeof(cyg_disk_info_t)) {
544 return -EINVAL;
545 }
546 *buf = *chan->info;
547 *len = sizeof(cyg_disk_info_t);
548 break;
549
550 default:
551 // pass down to lower layers
552 res = (funs->get_config)(chan, key, xbuf, len);
553 }
554
555 return res;
556 }
557
558 // ---------------------------------------------------------------------------
559
560 static Cyg_ErrNo
561 disk_set_config(cyg_io_handle_t handle,
562 cyg_uint32 key,
563 const void *xbuf,
564 cyg_uint32 *len)
565 {
566 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
567 disk_channel *chan = (disk_channel *) t->priv;
568 cyg_disk_info_t *info = chan->info;
569 disk_funs *funs = chan->funs;
570
571 if (!info->connected || !chan->valid)
572 return -EINVAL;
573
574 D(("disk set config key=%d\n", key));
575
576 // pass down to lower layers
577 return (funs->set_config)(chan, key, xbuf, len);
578 }
579
580 // ---------------------------------------------------------------------------
581 // EOF disk.c