1 //==========================================================================
5 // High level disk driver
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 2003. 2004, 2005, 2006 eCosCentric Limited
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.
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
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.
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.
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.
36 // -------------------------------------------
37 //####ECOSGPLCOPYRIGHTEND####
38 //==========================================================================
39 //#####DESCRIPTIONBEGIN####
43 // Purpose: Top level disk driver
46 //####DESCRIPTIONEND####
48 //==========================================================================
50 #include <pkgconf/io.h>
51 #include <pkgconf/io_disk.h>
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
59 // ---------------------------------------------------------------------------
61 #ifdef CYGDBG_IO_DISK_DEBUG
66 # define D(_args_) diag_printf _args_
71 // ---------------------------------------------------------------------------
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
81 // Get cylinders, heads and sectors from data (MBR partition format)
82 #define READ_CHS(_data_, _c_, _h_, _s_) \
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)); \
90 // Get double word from data (MBR partition format)
91 #define READ_DWORD(_data_, _val_) \
93 _val_ = *((cyg_uint8 *)_data_) | \
94 *(((cyg_uint8 *)_data_)+1) << 8 | \
95 *(((cyg_uint8 *)_data_)+2) << 16 | \
96 *(((cyg_uint8 *)_data_)+3) << 24; \
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)
103 // ---------------------------------------------------------------------------
105 static Cyg_ErrNo disk_bread(cyg_io_handle_t handle,
110 static Cyg_ErrNo disk_bwrite(cyg_io_handle_t handle,
115 static Cyg_ErrNo disk_select(cyg_io_handle_t handle,
119 static Cyg_ErrNo disk_get_config(cyg_io_handle_t handle,
124 static Cyg_ErrNo disk_set_config(cyg_io_handle_t handle,
129 BLOCK_DEVIO_TABLE(cyg_io_disk_devio,
137 static cyg_bool disk_init(struct cyg_devtab_entry *tab);
139 static Cyg_ErrNo disk_connected(struct cyg_devtab_entry *tab,
140 cyg_disk_identify_t *ident);
142 static Cyg_ErrNo disk_disconnected(struct disk_channel *chan);
144 static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab,
145 struct cyg_devtab_entry *sub_tab,
148 static void disk_transfer_done(struct disk_channel *chan, Cyg_ErrNo res);
150 DISK_CALLBACKS(cyg_io_disk_callbacks,
158 // ---------------------------------------------------------------------------
160 // Read partition from data
164 read_partition(cyg_uint8 *data,
165 cyg_disk_info_t *info,
166 cyg_disk_partition_t *part)
168 cyg_disk_identify_t *ident = &info->ident;
170 cyg_uint32 start, end, size;
173 diag_printf("Partition data:\n");
174 diag_dump_buf( data, 16 );
175 diag_printf("Disk geometry: %d/%d/%d\n",info->ident.cylinders_num,
176 info->ident.heads_num, info->ident.sectors_num );
179 // Retrieve basic information
180 part->type = data[4];
181 part->state = data[0];
182 READ_DWORD(&data[12], part->size);
184 READ_DWORD(&data[8], start);
185 READ_DWORD(&data[12], size);
187 // Use the LBA start and size fields if they are valid. Otherwise
190 if( start > 0 && size > 0 )
192 READ_DWORD(&data[8], start);
193 end = start + size - 1;
196 diag_printf("Using LBA partition parameters\n");
197 diag_printf(" LBA start %d\n",start);
198 diag_printf(" LBA size %d\n",size);
199 diag_printf(" LBA end %d\n",end);
205 READ_CHS(&data[1], c, h, s);
206 CHS_TO_LBA(ident, c, h, s, start);
208 diag_printf("Using CHS partition parameters\n");
209 diag_printf(" CHS start %d/%d/%d => %d\n",c,h,s,start);
212 READ_CHS(&data[5], c, h, s);
213 CHS_TO_LBA(ident, c, h, s, end);
215 diag_printf(" CHS end %d/%d/%d => %d\n",c,h,s,end);
216 diag_printf(" CHS size %d\n",size);
226 // ---------------------------------------------------------------------------
228 // Read Master Boot Record (partitions)
232 read_mbr(disk_channel *chan)
234 cyg_disk_info_t *info = chan->info;
235 disk_funs *funs = chan->funs;
236 disk_controller *ctlr = chan->controller;
238 Cyg_ErrNo res = ENOERR;
243 for (i = 0; i < info->partitions_num; i++)
244 info->partitions[i].type = 0x00;
248 cyg_drv_mutex_lock( &ctlr->lock );
251 cyg_drv_cond_wait( &ctlr->queue );
255 ctlr->result = -EWOULDBLOCK;
257 for( i = 0; i < sizeof(buf); i++ )
260 res = (funs->read)(chan, (void *)buf, 1, 0);
262 if( res == -EWOULDBLOCK )
264 // If the driver replys EWOULDBLOCK, then the transfer is
265 // being handled asynchronously and when it is finished it
266 // will call disk_transfer_done(). This will wake us up here
269 while( ctlr->result == -EWOULDBLOCK )
270 cyg_drv_cond_wait( &ctlr->async );
277 cyg_drv_mutex_unlock( &ctlr->lock );
283 diag_dump_buf_with_offset( buf, 512, buf );
286 if (MBR_SIG_BYTE0 == buf[MBR_SIG_ADDR+0] && MBR_SIG_BYTE1 == buf[MBR_SIG_ADDR+1])
290 D(("disk MBR found\n"));
292 npart = info->partitions_num < MBR_PART_NUM ?
293 info->partitions_num : MBR_PART_NUM;
295 for (i = 0; i < npart; i++)
297 cyg_disk_partition_t *part = &info->partitions[i];
299 read_partition(&buf[MBR_PART_ADDR+MBR_PART_SIZE*i], info, part);
301 if (0x00 != part->type)
303 D(("\ndisk MBR partition %d:\n", i));
304 D((" type = %02X\n", part->type));
305 D((" state = %02X\n", part->state));
306 D((" start = %d\n", part->start));
307 D((" end = %d\n", part->end));
308 D((" size = %d\n\n", part->size));
316 // ---------------------------------------------------------------------------
319 disk_init(struct cyg_devtab_entry *tab)
321 disk_channel *chan = (disk_channel *) tab->priv;
322 cyg_disk_info_t *info = chan->info;
327 disk_controller *controller = chan->controller;
329 if( !controller->init )
331 cyg_drv_mutex_init( &controller->lock );
332 cyg_drv_cond_init( &controller->queue, &controller->lock );
333 cyg_drv_cond_init( &controller->async, &controller->lock );
335 controller->init = true;
338 info->connected = false;
340 // clear partition data
341 for (i = 0; i < info->partitions_num; i++)
342 info->partitions[i].type = 0x00;
349 // ---------------------------------------------------------------------------
352 disk_connected(struct cyg_devtab_entry *tab,
353 cyg_disk_identify_t *ident)
355 disk_channel *chan = (disk_channel *) tab->priv;
356 cyg_disk_info_t *info = chan->info;
357 Cyg_ErrNo res = ENOERR;
362 // If the device is already connected, nothing more to do
363 if( info->connected )
366 // If any of these assertions fire, it is probable that the
367 // hardware driver has not been updated to match the current disk
369 CYG_ASSERT( ident->lba_sectors_num > 0, "Bad LBA sector count" );
370 CYG_ASSERT( ident->phys_block_size > 0, "Bad physical block size");
371 CYG_ASSERT( ident->max_transfer > 0, "Bad max transfer size");
373 info->ident = *ident;
374 info->block_size = 512;
375 info->blocks_num = ident->lba_sectors_num;
376 info->phys_block_size = ident->phys_block_size;
378 D(("disk connected\n"));
379 D((" serial = '%s'\n", ident->serial));
380 D((" firmware rev = '%s'\n", ident->firmware_rev));
381 D((" model num = '%s'\n", ident->model_num));
382 D((" block_size = %d\n", info->block_size));
383 D((" blocks_num = %u\n", info->blocks_num));
384 D((" phys_block_size = %d\n", info->phys_block_size));
386 if (chan->mbr_support)
388 // read disk master boot record
389 res = read_mbr(chan);
394 // now declare that we are connected
395 info->connected = true;
401 // ---------------------------------------------------------------------------
404 disk_disconnected(disk_channel *chan)
406 cyg_disk_info_t *info = chan->info;
412 info->connected = false;
415 // clear partition data and invalidate partition devices
416 for (i = 0; i < info->partitions_num; i++)
418 info->partitions[i].type = 0x00;
419 chan->pdevs_chan[i].valid = false;
423 D(("disk disconnected\n"));
428 // ---------------------------------------------------------------------------
431 disk_lookup(struct cyg_devtab_entry **tab,
432 struct cyg_devtab_entry *sub_tab,
435 disk_channel *chan = (disk_channel *) (*tab)->priv;
436 cyg_disk_info_t *info = chan->info;
437 struct cyg_devtab_entry *new_tab;
438 disk_channel *new_chan;
441 if (!info->connected)
446 while ('\0' != *name)
448 if (*name < '0' || *name > '9')
451 dev_num = 10 * dev_num + (*name - '0');
455 if (dev_num > info->partitions_num)
458 D(("disk lookup dev number = %d\n", dev_num));
462 // 0 is the root device number
465 if (0x00 == info->partitions[dev_num-1].type)
467 D(("disk NO partition for dev\n"));
471 new_tab = &chan->pdevs_dev[dev_num-1];
472 new_chan = &chan->pdevs_chan[dev_num-1];
474 // copy device data from parent
478 new_tab->priv = (void *)new_chan;
481 new_chan->partition = &info->partitions[dev_num-1];
489 // ---------------------------------------------------------------------------
492 disk_bread(cyg_io_handle_t handle,
494 cyg_uint32 *len, // In blocks
495 cyg_uint32 pos) // In blocks
497 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
498 disk_channel *chan = (disk_channel *) t->priv;
499 disk_controller *ctlr = chan->controller;
500 disk_funs *funs = chan->funs;
501 cyg_disk_info_t *info = chan->info;
502 cyg_uint32 size = *len;
503 cyg_uint8 *bbuf = (cyg_uint8 *)buf;
504 Cyg_ErrNo res = ENOERR;
507 cyg_drv_mutex_lock( &ctlr->lock );
510 cyg_drv_cond_wait( &ctlr->queue );
512 if (info->connected && chan->valid)
516 if (NULL != chan->partition)
518 pos += chan->partition->start;
519 last = chan->partition->end;
523 last = info->blocks_num-1;
526 D(("disk read block=%d len=%d buf=%p\n", pos, *len, buf));
530 cyg_uint32 tfr = size;
538 if( tfr > info->ident.max_transfer )
539 tfr = info->ident.max_transfer;
541 ctlr->result = -EWOULDBLOCK;
545 res = (funs->read)(chan, (void*)bbuf, tfr, pos);
547 if( res == -EWOULDBLOCK )
549 // If the driver replys EWOULDBLOCK, then the transfer is
550 // being handled asynchronously and when it is finished it
551 // will call disk_transfer_done(). This will wake us up here
554 while( ctlr->result == -EWOULDBLOCK )
555 cyg_drv_cond_wait( &ctlr->async );
560 cyg_drv_dsr_unlock();
565 if (!info->connected)
571 bbuf += tfr * info->block_size;
577 cyg_drv_cond_signal( &ctlr->queue );
584 cyg_drv_mutex_unlock( &ctlr->lock );
593 // ---------------------------------------------------------------------------
596 disk_bwrite(cyg_io_handle_t handle,
598 cyg_uint32 *len, // In blocks
599 cyg_uint32 pos) // In blocks
601 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
602 disk_channel *chan = (disk_channel *) t->priv;
603 disk_controller *ctlr = chan->controller;
604 disk_funs *funs = chan->funs;
605 cyg_disk_info_t *info = chan->info;
606 cyg_uint32 size = *len;
607 cyg_uint8 *bbuf = (cyg_uint8 * const) buf;
608 Cyg_ErrNo res = ENOERR;
611 cyg_drv_mutex_lock( &ctlr->lock );
614 cyg_drv_cond_wait( &ctlr->queue );
616 if (info->connected && chan->valid)
620 if (NULL != chan->partition)
622 pos += chan->partition->start;
623 last = chan->partition->end;
627 last = info->blocks_num-1;
630 D(("disk write block=%d len=%d buf=%p\n", pos, *len, buf));
634 cyg_uint32 tfr = size;
642 if( tfr > info->ident.max_transfer )
643 tfr = info->ident.max_transfer;
645 ctlr->result = -EWOULDBLOCK;
649 res = (funs->write)(chan, (void*)bbuf, tfr, pos);
651 if( res == -EWOULDBLOCK )
653 // If the driver replys EWOULDBLOCK, then the transfer is
654 // being handled asynchronously and when it is finished it
655 // will call disk_transfer_done(). This will wake us up here
658 while( ctlr->result == -EWOULDBLOCK )
659 cyg_drv_cond_wait( &ctlr->async );
664 cyg_drv_dsr_unlock();
669 if (!info->connected)
675 bbuf += tfr * info->block_size;
682 cyg_drv_cond_signal( &ctlr->queue );
689 cyg_drv_mutex_unlock( &ctlr->lock );
698 // ---------------------------------------------------------------------------
700 static void disk_transfer_done(struct disk_channel *chan, Cyg_ErrNo res)
702 disk_controller *ctlr = chan->controller;
706 cyg_drv_cond_signal( &ctlr->async );
709 // ---------------------------------------------------------------------------
712 disk_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
714 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
715 disk_channel *chan = (disk_channel *) t->priv;
716 cyg_disk_info_t *cinfo = chan->info;
718 if (!cinfo->connected || !chan->valid)
724 // ---------------------------------------------------------------------------
727 disk_get_config(cyg_io_handle_t handle,
732 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
733 disk_channel *chan = (disk_channel *) t->priv;
734 disk_controller *ctlr = chan->controller;
735 cyg_disk_info_t *info = chan->info;
736 cyg_disk_info_t *buf = (cyg_disk_info_t *) xbuf;
737 disk_funs *funs = chan->funs;
738 Cyg_ErrNo res = ENOERR;
740 cyg_drv_mutex_lock( &ctlr->lock );
743 cyg_drv_cond_wait( &ctlr->queue );
745 if (info->connected && chan->valid)
749 D(("disk get config key=%d\n", key));
752 case CYG_IO_GET_CONFIG_DISK_INFO:
753 if (*len < sizeof(cyg_disk_info_t)) {
757 D(("chan->info->block_size %u\n", chan->info->block_size ));
758 D(("chan->info->blocks_num %u\n", chan->info->blocks_num ));
759 D(("chan->info->phys_block_size %u\n", chan->info->phys_block_size ));
761 *len = sizeof(cyg_disk_info_t);
765 // pass down to lower layers
766 res = (funs->get_config)(chan, key, xbuf, len);
770 cyg_drv_cond_signal( &ctlr->queue );
775 cyg_drv_mutex_unlock( &ctlr->lock );
780 // ---------------------------------------------------------------------------
783 disk_set_config(cyg_io_handle_t handle,
788 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle;
789 disk_channel *chan = (disk_channel *) t->priv;
790 disk_controller *ctlr = chan->controller;
791 cyg_disk_info_t *info = chan->info;
792 disk_funs *funs = chan->funs;
793 Cyg_ErrNo res = ENOERR;
795 cyg_drv_mutex_lock( &ctlr->lock );
798 cyg_drv_cond_wait( &ctlr->queue );
800 if (info->connected && chan->valid)
804 D(("disk set config key=%d\n", key));
808 case CYG_IO_SET_CONFIG_DISK_MOUNT:
811 D(("disk mount: chan %d disk %d\n",chan->mounts, info->mounts));
814 case CYG_IO_SET_CONFIG_DISK_UMOUNT:
817 D(("disk umount: chan %d disk %d\n",chan->mounts, info->mounts));
824 // pass down to lower layers
825 res = (funs->set_config)(chan, key, xbuf, len);
828 cyg_drv_cond_signal( &ctlr->queue );
833 cyg_drv_mutex_unlock( &ctlr->lock );
839 // ---------------------------------------------------------------------------