]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/io/disk/v2_0/src/disk.c
unified MX27, MX25, MX37 trees
[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. 2004, 2005, 2006 eCosCentric Limited
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 #ifdef CYGDBG_IO_DISK_DEBUG
62 #define DEBUG 1
63 #endif
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 disk_channel *chan);
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 static void disk_transfer_done(struct disk_channel *chan, Cyg_ErrNo res);
149
150 DISK_CALLBACKS(cyg_io_disk_callbacks, 
151                disk_init,
152                disk_connected,
153                disk_disconnected,
154                disk_lookup,
155                disk_transfer_done
156 ); 
157
158 // ---------------------------------------------------------------------------
159 //
160 // Read partition from data
161 // 
162
163 static void 
164 read_partition(cyg_uint8            *data,
165                cyg_disk_info_t      *info,
166                cyg_disk_partition_t *part)
167 {
168     cyg_disk_identify_t *ident = &info->ident;
169     cyg_uint16 c, h, s;
170     cyg_uint32 start, end, size;
171
172 #ifdef DEBUG
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 );
177 #endif
178     
179     // Retrieve basic information
180     part->type  = data[4];
181     part->state = data[0];
182     READ_DWORD(&data[12], part->size);
183
184     READ_DWORD(&data[8], start);        
185     READ_DWORD(&data[12], size);
186
187     // Use the LBA start and size fields if they are valid. Otherwise
188     // fall back to CHS.
189     
190     if( start > 0 && size > 0 )
191     {
192         READ_DWORD(&data[8], start);    
193         end = start + size - 1;
194
195 #ifdef DEBUG
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);
200 #endif
201         
202     }
203     else
204     {
205         READ_CHS(&data[1], c, h, s);
206         CHS_TO_LBA(ident, c, h, s, start);
207 #ifdef DEBUG
208         diag_printf("Using CHS partition parameters\n");
209         diag_printf("      CHS start %d/%d/%d => %d\n",c,h,s,start);
210 #endif
211     
212         READ_CHS(&data[5], c, h, s);
213         CHS_TO_LBA(ident, c, h, s, end);
214 #ifdef DEBUG
215         diag_printf("      CHS end %d/%d/%d => %d\n",c,h,s,end);
216         diag_printf("      CHS size %d\n",size);
217 #endif
218
219     }
220
221     part->size = size;
222     part->start = start;
223     part->end = end;
224 }
225
226 // ---------------------------------------------------------------------------
227 //
228 // Read Master Boot Record (partitions)
229 //
230
231 static Cyg_ErrNo 
232 read_mbr(disk_channel *chan)
233 {
234     cyg_disk_info_t *info = chan->info;
235     disk_funs       *funs = chan->funs;
236     disk_controller *ctlr = chan->controller;
237     cyg_uint8 buf[512];
238     Cyg_ErrNo res = ENOERR;
239     int i;
240
241     D(("read MBR\n"));
242     
243     for (i = 0; i < info->partitions_num; i++)
244         info->partitions[i].type = 0x00;    
245
246
247     
248     cyg_drv_mutex_lock( &ctlr->lock );
249
250     while( ctlr->busy )
251         cyg_drv_cond_wait( &ctlr->queue );
252
253     ctlr->busy = true;
254     
255     ctlr->result = -EWOULDBLOCK;
256
257     for( i = 0; i < sizeof(buf); i++ )
258         buf[i] = 0;
259     
260     res = (funs->read)(chan, (void *)buf, 1, 0);
261     
262     if( res == -EWOULDBLOCK )
263     {
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
267         // to continue.
268
269         while( ctlr->result == -EWOULDBLOCK )
270             cyg_drv_cond_wait( &ctlr->async );
271
272         res = ctlr->result;
273     }
274         
275     ctlr->busy = false;
276     
277     cyg_drv_mutex_unlock( &ctlr->lock );
278
279     if (ENOERR != res)
280         return res;
281
282 #ifdef DEBUG
283     diag_dump_buf_with_offset( buf, 512, buf );
284 #endif
285     
286     if (MBR_SIG_BYTE0 == buf[MBR_SIG_ADDR+0] && MBR_SIG_BYTE1 == buf[MBR_SIG_ADDR+1])
287     {
288         int npart;
289
290         D(("disk MBR found\n")); 
291  
292         npart = info->partitions_num < MBR_PART_NUM ? 
293             info->partitions_num : MBR_PART_NUM;
294
295         for (i = 0; i < npart; i++)
296         {
297             cyg_disk_partition_t *part = &info->partitions[i];
298             
299             read_partition(&buf[MBR_PART_ADDR+MBR_PART_SIZE*i], info, part); 
300 #ifdef DEBUG
301             if (0x00 != part->type)
302             {
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));
309             }
310 #endif
311         } 
312     }
313     return ENOERR;
314 }
315
316 // ---------------------------------------------------------------------------
317
318 static cyg_bool 
319 disk_init(struct cyg_devtab_entry *tab)
320 {
321     disk_channel    *chan = (disk_channel *) tab->priv;
322     cyg_disk_info_t *info = chan->info;
323     int i;
324
325     if (!chan->init)
326     {
327         disk_controller *controller = chan->controller;
328         
329         if( !controller->init )
330         {
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 );
334             
335             controller->init = true;
336         }
337         
338         info->connected = false;
339         
340         // clear partition data
341         for (i = 0; i < info->partitions_num; i++)
342             info->partitions[i].type = 0x00;
343
344         chan->init = true;
345     }
346     return true;
347 }
348
349 // ---------------------------------------------------------------------------
350
351 static Cyg_ErrNo
352 disk_connected(struct cyg_devtab_entry *tab,
353                cyg_disk_identify_t     *ident)
354 {
355     disk_channel    *chan = (disk_channel *) tab->priv;
356     cyg_disk_info_t *info = chan->info;
357     Cyg_ErrNo res = ENOERR;
358  
359     if (!chan->init)
360         return -EINVAL;
361
362     // If the device is already connected, nothing more to do
363     if( info->connected )
364         return ENOERR;
365
366     // If any of these assertions fire, it is probable that the
367     // hardware driver has not been updated to match the current disk
368     // API.
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");
372     
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;
377  
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));
385     
386     if (chan->mbr_support)
387     {    
388         // read disk master boot record
389         res = read_mbr(chan);
390     }
391
392     if (ENOERR == res)
393     {    
394         // now declare that we are connected
395         info->connected = true;
396         chan->valid     = true; 
397     }
398     return res;
399 }
400
401 // ---------------------------------------------------------------------------
402
403 static Cyg_ErrNo
404 disk_disconnected(disk_channel *chan)
405 {
406     cyg_disk_info_t *info = chan->info;
407     int i;
408
409     if (!chan->init)
410         return -EINVAL;
411
412     info->connected = false;
413     chan->valid     = false;
414      
415     // clear partition data and invalidate partition devices 
416     for (i = 0; i < info->partitions_num; i++)
417     {
418         info->partitions[i].type  = 0x00;
419         chan->pdevs_chan[i].valid = false;
420     }
421
422     
423     D(("disk disconnected\n")); 
424
425     return ENOERR;    
426 }
427     
428 // ---------------------------------------------------------------------------
429
430 static Cyg_ErrNo
431 disk_lookup(struct cyg_devtab_entry **tab,
432             struct cyg_devtab_entry  *sub_tab,
433             const char *name)
434 {
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;
439     int dev_num;
440     
441     if (!info->connected)
442         return -EINVAL;
443
444     dev_num = 0;
445
446     while ('\0' != *name)
447     {
448         if (*name < '0' || *name > '9')
449             return -EINVAL;
450
451         dev_num = 10 * dev_num + (*name - '0');
452         name++;
453     }
454    
455     if (dev_num > info->partitions_num)
456         return -EINVAL;
457
458     D(("disk lookup dev number = %d\n", dev_num)); 
459
460     if (0 == dev_num)
461     {
462         // 0 is the root device number
463         return ENOERR; 
464     }
465     if (0x00 == info->partitions[dev_num-1].type)
466     {
467         D(("disk NO partition for dev\n")); 
468         return -EINVAL;
469     }
470
471     new_tab  = &chan->pdevs_dev[dev_num-1];
472     new_chan = &chan->pdevs_chan[dev_num-1];
473     
474     // copy device data from parent
475     *new_tab  = **tab; 
476     *new_chan = *chan;
477
478     new_tab->priv = (void *)new_chan;
479
480     // set partition ptr
481     new_chan->partition = &info->partitions[dev_num-1];
482
483     // return device tab 
484     *tab = new_tab;
485         
486     return ENOERR;
487 }
488
489 // ---------------------------------------------------------------------------
490
491 static Cyg_ErrNo 
492 disk_bread(cyg_io_handle_t  handle, 
493            void            *buf, 
494            cyg_uint32      *len,  // In blocks
495            cyg_uint32       pos)  // In blocks
496 {
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;
505     cyg_uint32  last;
506
507     cyg_drv_mutex_lock( &ctlr->lock );
508
509     while( ctlr->busy )
510         cyg_drv_cond_wait( &ctlr->queue );
511
512     if (info->connected && chan->valid)
513     {
514         ctlr->busy = true;
515     
516         if (NULL != chan->partition)
517         {
518             pos += chan->partition->start;
519             last = chan->partition->end;
520         }
521         else
522         {
523             last = info->blocks_num-1;
524         }
525  
526         D(("disk read block=%d len=%d buf=%p\n", pos, *len, buf));
527
528         while( size > 0 )
529         {
530             cyg_uint32 tfr = size;
531             
532             if (pos > last)
533             {
534                 res = -EIO;
535                 break;
536             }
537
538             if( tfr > info->ident.max_transfer )
539                 tfr = info->ident.max_transfer;
540             
541             ctlr->result = -EWOULDBLOCK;
542
543             cyg_drv_dsr_lock();
544             
545             res = (funs->read)(chan, (void*)bbuf, tfr, pos);
546
547             if( res == -EWOULDBLOCK )
548             {
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
552                 // to continue.
553
554                 while( ctlr->result == -EWOULDBLOCK )
555                     cyg_drv_cond_wait( &ctlr->async );
556
557                 res = ctlr->result;
558             }
559
560             cyg_drv_dsr_unlock();
561             
562             if (ENOERR != res)
563                 goto done;
564
565             if (!info->connected)
566             {
567                 res = -EINVAL;
568                 goto done;
569             }
570
571             bbuf        += tfr * info->block_size;
572             pos         += tfr;
573             size        -= tfr;
574         }
575
576         ctlr->busy = false;
577         cyg_drv_cond_signal( &ctlr->queue );
578     }
579     else
580         res = -EINVAL;
581
582 done:
583     
584     cyg_drv_mutex_unlock( &ctlr->lock );
585 #ifdef CYGPKG_KERNEL
586     cyg_thread_yield();
587 #endif
588     
589     *len -= size;
590     return res;
591 }
592
593 // ---------------------------------------------------------------------------
594
595 static Cyg_ErrNo 
596 disk_bwrite(cyg_io_handle_t  handle, 
597             const void      *buf, 
598             cyg_uint32      *len,   // In blocks
599             cyg_uint32       pos)   // In blocks
600 {
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;
609     cyg_uint32  last;
610
611     cyg_drv_mutex_lock( &ctlr->lock );
612
613     while( ctlr->busy )
614         cyg_drv_cond_wait( &ctlr->queue );
615
616     if (info->connected && chan->valid)
617     {
618         ctlr->busy = true;
619         
620         if (NULL != chan->partition)
621         {
622             pos += chan->partition->start;
623             last = chan->partition->end;
624         }
625         else
626         {
627             last = info->blocks_num-1;
628         }
629     
630         D(("disk write block=%d len=%d buf=%p\n", pos, *len, buf));
631
632         while( size > 0 )
633         {
634             cyg_uint32 tfr = size;
635         
636             if (pos > last)
637             {
638                 res = -EIO;
639                 goto done;
640             }
641
642             if( tfr > info->ident.max_transfer )
643                 tfr = info->ident.max_transfer;
644
645             ctlr->result = -EWOULDBLOCK;
646
647             cyg_drv_dsr_lock();
648             
649             res = (funs->write)(chan, (void*)bbuf, tfr, pos);
650
651             if( res == -EWOULDBLOCK )
652             {
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
656                 // to continue.
657
658                 while( ctlr->result == -EWOULDBLOCK )
659                     cyg_drv_cond_wait( &ctlr->async );
660
661                 res = ctlr->result;
662             }
663
664             cyg_drv_dsr_unlock();
665             
666             if (ENOERR != res)
667                 goto done;
668  
669             if (!info->connected)
670             {
671                 res = -EINVAL;
672                 goto done;
673             }
674
675             bbuf        += tfr * info->block_size;
676             pos         += tfr;
677             size        -= tfr;
678             
679         }
680  
681         ctlr->busy = false;
682         cyg_drv_cond_signal( &ctlr->queue );
683     }
684     else
685         res = -EINVAL;
686
687 done:
688     
689     cyg_drv_mutex_unlock( &ctlr->lock );
690 #ifdef CYGPKG_KERNEL    
691     cyg_thread_yield();
692 #endif
693     
694     *len -= size;
695     return res;
696 }
697
698 // ---------------------------------------------------------------------------
699
700 static void disk_transfer_done(struct disk_channel *chan, Cyg_ErrNo res)
701 {
702     disk_controller    *ctlr = chan->controller;    
703
704     ctlr->result = res;
705     
706     cyg_drv_cond_signal( &ctlr->async );
707 }
708
709 // ---------------------------------------------------------------------------
710
711 static cyg_bool
712 disk_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
713 {
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;
717  
718     if (!cinfo->connected || !chan->valid)
719         return false;
720     else
721         return true;
722 }
723
724 // ---------------------------------------------------------------------------
725
726 static Cyg_ErrNo 
727 disk_get_config(cyg_io_handle_t  handle, 
728                 cyg_uint32       key, 
729                 void            *xbuf,
730                 cyg_uint32      *len)
731 {
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;
739  
740     cyg_drv_mutex_lock( &ctlr->lock );
741
742     while( ctlr->busy )
743         cyg_drv_cond_wait( &ctlr->queue );
744
745     if (info->connected && chan->valid)
746     {
747         ctlr->busy = true;
748     
749         D(("disk get config key=%d\n", key)); 
750     
751         switch (key) {
752         case CYG_IO_GET_CONFIG_DISK_INFO:
753             if (*len < sizeof(cyg_disk_info_t)) {
754                 res = -EINVAL;
755                 break;
756             }
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 ));
760             *buf = *chan->info;
761             *len = sizeof(cyg_disk_info_t);
762             break;       
763
764         default:
765             // pass down to lower layers
766             res = (funs->get_config)(chan, key, xbuf, len);
767         }
768         
769         ctlr->busy = false;
770         cyg_drv_cond_signal( &ctlr->queue );
771     }
772     else
773         res = -EINVAL;
774
775     cyg_drv_mutex_unlock( &ctlr->lock );    
776     
777     return res;
778 }
779
780 // ---------------------------------------------------------------------------
781
782 static Cyg_ErrNo 
783 disk_set_config(cyg_io_handle_t  handle, 
784                 cyg_uint32       key, 
785                 const void      *xbuf, 
786                 cyg_uint32      *len)
787 {
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;
794     
795     cyg_drv_mutex_lock( &ctlr->lock );
796
797     while( ctlr->busy )
798         cyg_drv_cond_wait( &ctlr->queue );
799
800     if (info->connected && chan->valid)
801     {
802         ctlr->busy = true;
803         
804         D(("disk set config key=%d\n", key)); 
805
806         switch ( key )
807         {
808         case CYG_IO_SET_CONFIG_DISK_MOUNT:
809             chan->mounts++;
810             info->mounts++;
811             D(("disk mount: chan %d disk %d\n",chan->mounts, info->mounts));
812             break;
813             
814         case CYG_IO_SET_CONFIG_DISK_UMOUNT:
815             chan->mounts--;
816             info->mounts--;
817             D(("disk umount: chan %d disk %d\n",chan->mounts, info->mounts));            
818             break;
819             
820         default:
821             break;
822         }
823         
824         // pass down to lower layers
825         res = (funs->set_config)(chan, key, xbuf, len);
826         
827         ctlr->busy = false;
828         cyg_drv_cond_signal( &ctlr->queue );
829     }
830     else
831         res = -EINVAL;
832     
833     cyg_drv_mutex_unlock( &ctlr->lock );
834
835     return res;
836     
837 }
838
839 // ---------------------------------------------------------------------------
840 // EOF disk.c