]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/io/disk/v2_0/src/disk.c
f5d9c02f10389c627791f128e48ba9252a27ca62
[karo-tx-redboot.git] / packages / io / disk / v2_0 / src / disk.c
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 // ---------------------------------------------------------------------------
60
61 //#define DEBUG 1
62
63 #ifdef DEBUG
64 # define D(_args_) diag_printf _args_
65 #else
66 # define D(_args_)
67 #endif
68
69 // ---------------------------------------------------------------------------
70
71 // Master Boot Record defines
72 #define MBR_SIG_ADDR  0x1FE   // signature address 
73 #define MBR_SIG_BYTE0 0x55    // signature first byte value
74 #define MBR_SIG_BYTE1 0xAA    // signature second byte value
75 #define MBR_PART_ADDR 0x1BE   // first partition address
76 #define MBR_PART_SIZE 0x10    // partition size
77 #define MBR_PART_NUM  4       // number of partitions
78
79 // Get cylinders, heads and sectors from data (MBR partition format)
80 #define READ_CHS(_data_, _c_, _h_, _s_)                     \
81     do {                                                    \
82         _h_ = (*((cyg_uint8 *)_data_));                     \
83         _s_ = (*(((cyg_uint8 *)_data_)+1) &  0x3F);         \
84         _c_ = (*(((cyg_uint8 *)_data_)+1) & ~0x3F) << 2 |   \
85               (*(((cyg_uint8 *)_data_)+2));                 \
86     } while (0)
87
88 // Get double word from data (MBR partition format)
89 #define READ_DWORD(_data_, _val_)                           \
90     do {                                                    \
91         _val_ = *((cyg_uint8 *)_data_)           |          \
92                 *(((cyg_uint8 *)_data_)+1) << 8  |          \
93                 *(((cyg_uint8 *)_data_)+2) << 16 |          \
94                 *(((cyg_uint8 *)_data_)+3) << 24;           \
95     } while (0)
96
97 // Convert cylinders, heads and sectors to LBA sectors 
98 #define CHS_TO_LBA(_info_, _c_, _h_, _s_, _lba_) \
99     (_lba_=(((_c_)*(_info_)->heads_num+(_h_))*(_info_)->sectors_num)+(_s_)-1)
100     
101 // ---------------------------------------------------------------------------
102
103 static Cyg_ErrNo disk_bread(cyg_io_handle_t  handle, 
104                             void            *buf, 
105                             cyg_uint32      *len, 
106                             cyg_uint32       pos);
107
108 static Cyg_ErrNo disk_bwrite(cyg_io_handle_t  handle, 
109                              const void      *buf, 
110                              cyg_uint32      *len, 
111                              cyg_uint32       pos);
112
113 static Cyg_ErrNo disk_select(cyg_io_handle_t handle, 
114                              cyg_uint32      which, 
115                              CYG_ADDRWORD    info);
116
117 static Cyg_ErrNo disk_get_config(cyg_io_handle_t  handle, 
118                                  cyg_uint32       key, 
119                                  void            *buf, 
120                                  cyg_uint32      *len);
121
122 static Cyg_ErrNo disk_set_config(cyg_io_handle_t  handle, 
123                                  cyg_uint32       key, 
124                                  const void      *buf, 
125                                  cyg_uint32      *len);
126
127 BLOCK_DEVIO_TABLE(cyg_io_disk_devio,
128                   disk_bwrite,
129                   disk_bread,
130                   disk_select,
131                   disk_get_config,
132                   disk_set_config
133 );
134
135 static cyg_bool disk_init(struct cyg_devtab_entry *tab);
136
137 static Cyg_ErrNo disk_connected(struct cyg_devtab_entry *tab,
138                                 cyg_disk_identify_t     *ident);
139
140 static Cyg_ErrNo disk_disconnected(struct cyg_devtab_entry *tab);
141
142 static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab,
143                              struct cyg_devtab_entry  *sub_tab,
144                              const char               *name);
145
146 DISK_CALLBACKS(cyg_io_disk_callbacks, 
147                disk_init,
148                disk_connected,
149                disk_disconnected,
150                disk_lookup
151 ); 
152
153 // ---------------------------------------------------------------------------
154
155 //
156 // Read partition from data
157 // 
158 static void 
159 read_partition(cyg_uint8            *data,
160                cyg_disk_info_t      *info,
161                cyg_disk_partition_t *part)
162 {
163     cyg_uint16 c, h, s;
164
165     part->type  = data[4];
166     part->state = data[0];
167
168     READ_CHS(&data[1], c, h, s);
169     CHS_TO_LBA(&info->ident, c, h, s, part->start);
170
171     READ_CHS(&data[5], c, h, s);
172     CHS_TO_LBA(&info->ident, c, h, s, part->end);
173     
174     READ_DWORD(&data[12], part->size);
175 }
176
177 //
178 // Read Master Boot Record (partitions)
179 //
180 static Cyg_ErrNo 
181 read_mbr(disk_channel *chan)
182 {
183     cyg_disk_info_t *info = chan->info;
184     disk_funs       *funs = chan->funs;
185     cyg_uint8 buf[512];
186     Cyg_ErrNo res = ENOERR;
187     int i;
188  
189     for (i = 0; i < info->partitions_num; i++)
190         info->partitions[i].type = 0x00;    
191    
192     res = (funs->read)(chan, (void *)buf, 512, 0);
193     if (ENOERR != res)
194         return res;
195
196     if (MBR_SIG_BYTE0 == buf[MBR_SIG_ADDR+0] && MBR_SIG_BYTE1 == buf[MBR_SIG_ADDR+1])
197     {
198         int npart;
199
200         D(("disk MBR found\n")); 
201  
202         npart = info->partitions_num < MBR_PART_NUM ? 
203             info->partitions_num : MBR_PART_NUM;
204
205         for (i = 0; i < npart; i++)
206         {
207             cyg_disk_partition_t *part = &info->partitions[i];
208             
209             read_partition(&buf[MBR_PART_ADDR+MBR_PART_SIZE*i], info, part); 
210 #ifdef DEBUG
211             if (0x00 != part->type)
212             {
213                 D(("\ndisk MBR partition %d:\n", i));
214                 D(("      type  = %02X\n", part->type));
215                 D(("      state = %02X\n", part->state));
216                 D(("      start = %d\n",   part->start));
217                 D(("      end   = %d\n",   part->end));
218                 D(("      size  = %d\n\n", part->size));
219             }
220 #endif
221         } 
222     }
223     return ENOERR;
224 }
225
226 static cyg_bool 
227 disk_init(struct cyg_devtab_entry *tab)
228 {
229     disk_channel    *chan = (disk_channel *) tab->priv;
230     cyg_disk_info_t *info = chan->info;
231     int i;
232
233     if (!chan->init)
234     {
235         info->connected = false;
236
237         // clear partition data
238         for (i = 0; i < info->partitions_num; i++)
239             info->partitions[i].type = 0x00;
240
241         chan->init = true;
242     }
243     return true;
244 }
245
246 static Cyg_ErrNo
247 disk_connected(struct cyg_devtab_entry *tab,
248                cyg_disk_identify_t     *ident)
249 {
250     disk_channel    *chan = (disk_channel *) tab->priv;
251     cyg_disk_info_t *info = chan->info;
252     Cyg_ErrNo res = ENOERR;
253  
254     if (!chan->init)
255         return -EINVAL;
256     
257     info->ident      = *ident;
258     info->block_size = 512;
259     info->blocks_num = ident->lba_sectors_num;
260  
261     D(("disk connected\n")); 
262     D(("    serial       = '%s'\n", ident->serial)); 
263     D(("    firmware rev = '%s'\n", ident->firmware_rev)); 
264     D(("    model num    = '%s'\n", ident->model_num)); 
265     D(("    block_size   = %d\n",   info->block_size));
266     D(("    blocks_num   = %d\n",   info->blocks_num));
267
268     if (chan->mbr_support)
269     {    
270         // read disk master boot record
271         res = read_mbr(chan);
272     }
273
274     if (ENOERR == res)
275     {    
276         // now declare that we are connected
277         info->connected = true;
278         chan->valid     = true; 
279     }
280     return res;
281 }
282
283 static Cyg_ErrNo
284 disk_disconnected(struct cyg_devtab_entry *tab)
285 {
286     disk_channel    *chan = (disk_channel *) tab->priv;
287     cyg_disk_info_t *info = chan->info;
288     int i;
289
290     if (!chan->init)
291         return -EINVAL;
292     
293     info->connected = false;
294     chan->valid     = false;
295      
296     // clear partition data and invalidate partition devices 
297     for (i = 0; i < info->partitions_num; i++)
298     {
299         info->partitions[i].type  = 0x00;
300         chan->pdevs_chan[i].valid = false;
301     }
302
303     
304     D(("disk disconnected\n")); 
305
306     return ENOERR;    
307 }
308     
309 static Cyg_ErrNo
310 disk_lookup(struct cyg_devtab_entry **tab,
311             struct cyg_devtab_entry  *sub_tab,
312             const char *name)
313 {
314     disk_channel    *chan = (disk_channel *) (*tab)->priv;
315     cyg_disk_info_t *info = chan->info;
316     struct cyg_devtab_entry *new_tab;
317     disk_channel            *new_chan;
318     int dev_num;
319     
320     if (!info->connected)
321         return -EINVAL;
322
323     dev_num = 0;
324
325     while ('\0' != *name)
326     {
327         if (*name < '0' || *name > '9')
328             return -EINVAL;
329
330         dev_num = 10 * dev_num + (*name - '0');
331         name++;
332     }
333    
334     if (dev_num > info->partitions_num)
335         return -EINVAL;
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     new_tab  = &chan->pdevs_dev[dev_num-1];
351     new_chan = &chan->pdevs_chan[dev_num-1];
352     
353     // copy device data from parent
354     *new_tab  = **tab; 
355     *new_chan = *chan;
356
357     new_tab->priv = (void *)new_chan;
358
359     // set partition ptr
360     new_chan->partition = &info->partitions[dev_num-1];
361
362     // return device tab 
363     *tab = new_tab;
364         
365     return ENOERR;
366 }
367
368 // ---------------------------------------------------------------------------
369
370 static Cyg_ErrNo 
371 disk_bread(cyg_io_handle_t  handle, 
372            void            *buf, 
373            cyg_uint32      *len,
374            cyg_uint32       pos)
375 {
376     cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
377     disk_channel       *chan = (disk_channel *) t->priv;
378     disk_funs          *funs = chan->funs;
379     cyg_disk_info_t    *info = chan->info;
380     cyg_uint32  size = *len;
381     cyg_uint8  *bbuf = (cyg_uint8  *)buf;
382     Cyg_ErrNo   res  = ENOERR;
383     cyg_uint32  last;
384
385     if (!info->connected || !chan->valid)
386         return -EINVAL;
387     
388     if (NULL != chan->partition)
389     {
390         pos += chan->partition->start;
391         last = chan->partition->end;
392     }
393     else
394     {
395         last = info->blocks_num-1;
396     }
397  
398     D(("disk read block=%d len=%d buf=%p\n", pos, *len, buf)); 
399  
400     while (size > 0)
401     {
402         if (pos > last)
403         {
404             res = -EIO;
405             break;
406         }
407         
408         res = (funs->read)(chan, (void*)bbuf, info->block_size, pos);
409         if (ENOERR != res)
410             break;
411
412         if (!info->connected)
413         {
414             res = -EINVAL;
415             break;
416         }
417             
418         bbuf += info->block_size;
419         pos++;
420         size--;
421     }
422     *len -= size;
423     return res;
424 }
425
426 // ---------------------------------------------------------------------------
427
428 static Cyg_ErrNo 
429 disk_bwrite(cyg_io_handle_t  handle, 
430             const void      *buf, 
431             cyg_uint32      *len,
432             cyg_uint32       pos)
433 {
434     cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
435     disk_channel       *chan = (disk_channel *) t->priv;
436     disk_funs          *funs = chan->funs;
437     cyg_disk_info_t    *info = chan->info;
438     cyg_uint32  size = *len;
439     cyg_uint8  *bbuf = (cyg_uint8 * const) buf;
440     Cyg_ErrNo   res  = ENOERR;
441     cyg_uint32  last;
442
443     if (!info->connected || !chan->valid)
444         return -EINVAL;
445  
446     if (NULL != chan->partition)
447     {
448         pos += chan->partition->start;
449         last = chan->partition->end;
450     }
451     else
452     {
453         last = info->blocks_num-1;
454     }
455     
456     D(("disk write block=%d len=%d buf=%p\n", pos, *len, buf)); 
457    
458     while (size > 0)
459     {
460         if (pos > last)
461         {
462             res = -EIO;
463             break;
464         }
465         
466         res = (funs->write)(chan, (void*)bbuf, info->block_size, pos);
467         if (ENOERR != res)
468             break;
469  
470         if (!info->connected)
471         {
472             res = -EINVAL;
473             break;
474         }
475  
476         bbuf += info->block_size;
477         pos++;
478         size--;
479     }
480     *len -= size;
481     return res;
482 }
483
484 // ---------------------------------------------------------------------------
485
486 static cyg_bool
487 disk_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
488 {
489     cyg_devtab_entry_t *t     = (cyg_devtab_entry_t *) handle;
490     disk_channel       *chan  = (disk_channel *) t->priv;
491     cyg_disk_info_t    *cinfo = chan->info;
492  
493     if (!cinfo->connected || !chan->valid)
494         return false;
495     else
496         return true;
497 }
498
499 // ---------------------------------------------------------------------------
500
501 static Cyg_ErrNo 
502 disk_get_config(cyg_io_handle_t  handle, 
503                 cyg_uint32       key, 
504                 void            *xbuf,
505                 cyg_uint32      *len)
506 {
507     cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
508     disk_channel       *chan = (disk_channel *) t->priv;
509     cyg_disk_info_t    *info = chan->info;
510     cyg_disk_info_t    *buf  = (cyg_disk_info_t *) xbuf;
511     disk_funs          *funs = chan->funs;
512     Cyg_ErrNo res = ENOERR;
513  
514     if (!info->connected || !chan->valid)
515         return -EINVAL;
516
517     D(("disk get config key=%d\n", key)); 
518     
519     switch (key) {
520     case CYG_IO_GET_CONFIG_DISK_INFO:
521         if (*len < sizeof(cyg_disk_info_t)) {
522             return -EINVAL;
523         }
524         *buf = *chan->info;
525         *len = sizeof(cyg_disk_info_t);
526         break;       
527
528     default:
529         // pass down to lower layers
530         res = (funs->get_config)(chan, key, xbuf, len);
531     }
532    
533     return res;
534 }
535
536 // ---------------------------------------------------------------------------
537
538 static Cyg_ErrNo 
539 disk_set_config(cyg_io_handle_t  handle, 
540                 cyg_uint32       key, 
541                 const void      *xbuf, 
542                 cyg_uint32      *len)
543 {
544     cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
545     disk_channel       *chan = (disk_channel *) t->priv;
546     cyg_disk_info_t    *info = chan->info;
547     disk_funs          *funs = chan->funs;
548
549     if (!info->connected || !chan->valid)
550         return -EINVAL;
551
552     D(("disk set config key=%d\n", key)); 
553  
554     // pass down to lower layers
555     return (funs->set_config)(chan, key, xbuf, len);
556 }
557
558 // ---------------------------------------------------------------------------
559 // EOF disk.c