Mercurial > flash_v2
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 |
