root/kernel/blk_drv/hd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sys_setup
  2. controller_ready
  3. win_result
  4. hd_out
  5. drive_busy
  6. reset_controller
  7. redo_hd_request
  8. reset_hd
  9. unexpected_hd_interrupt
  10. bad_rw_intr
  11. read_intr
  12. write_intr
  13. do_hd_request
  14. hd_init

   1 /*
   2  *  linux/kernel/hd.c
   3  *
   4  *  (C) 1991  Linus Torvalds
   5  */
   6 
   7 /*
   8  * This is the low-level hd interrupt support. It traverses the
   9  * request-list, using interrupts to jump between functions. As
  10  * all the functions are called within interrupts, we may not
  11  * sleep. Special care is recommended.
  12  */
  13 
  14 #include <linux/config.h>
  15 #include <linux/sched.h>
  16 #include <linux/fs.h>
  17 #include <linux/kernel.h>
  18 #include <linux/hdreg.h>
  19 #include <asm/system.h>
  20 #include <asm/io.h>
  21 #include <asm/segment.h>
  22 
  23 #define MAJOR_NR 3
  24 #include "blk.h"
  25 
  26 /* Max read/write errors/sector */
  27 #define MAX_ERRORS      5
  28 #define MAX_HD          2
  29 
  30 /*
  31  *  This struct defines the HD's and their types.
  32  */
  33 struct hd_i_struct {
  34         int head,sect,cyl,wpcom,lzone,ctl;
  35         };
  36 #ifdef HD_TYPE
  37 struct hd_i_struct hd_info[] = { HD_TYPE };
  38 #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
  39 #else
  40 struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
  41 static int NR_HD = 0;
  42 #endif
  43 
  44 static struct hd_struct {
  45         long start_sect;
  46         long nr_sects;
  47 } hd[5*MAX_HD]={{0,0},};
  48 
  49 #define port_read(port,buf,nr) \
  50 __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
  51 
  52 #define port_write(port,buf,nr) \
  53 __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
  54 
  55 extern void hd_interrupt(void);
  56 
  57 /* This may be used only once, enforced by 'static int callable' */
  58 int sys_setup(void * BIOS)
     /* [previous][next][first][last][top][bottom][index][help] */
  59 {
  60         static int callable = 1;
  61         int i,drive;
  62         struct partition *p;
  63         struct buffer_head * bh;
  64 
  65         if (!callable)
  66                 return -1;
  67         callable = 0;
  68 #ifndef HD_TYPE
  69         for (drive=0 ; drive<2 ; drive++) {
  70                 hd_info[drive].cyl = *(unsigned short *) BIOS;
  71                 hd_info[drive].head = *(unsigned char *) (2+BIOS);
  72                 hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
  73                 hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
  74                 hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
  75                 hd_info[drive].sect = *(unsigned char *) (14+BIOS);
  76                 BIOS += 16;
  77         }
  78         if (hd_info[1].cyl)
  79                 NR_HD=2;
  80         else
  81                 NR_HD=1;
  82 #endif
  83         for (i=0 ; i<NR_HD ; i++) {
  84                 hd[i*5].start_sect = 0;
  85                 hd[i*5].nr_sects = hd_info[i].head*
  86                                 hd_info[i].sect*hd_info[i].cyl;
  87         }
  88         for (drive=0 ; drive<NR_HD ; drive++) {
  89                 if (!(bh = bread(0x300 + drive*5,0))) {
  90                         printk("Unable to read partition table of drive %d\n\r",
  91                                 drive);
  92                         panic("");
  93                 }
  94                 if (bh->b_data[510] != 0x55 || (unsigned char)
  95                     bh->b_data[511] != 0xAA) {
  96                         printk("Bad partition table on drive %d\n\r",drive);
  97                         panic("");
  98                 }
  99                 p = 0x1BE + (void *)bh->b_data;
 100                 for (i=1;i<5;i++,p++) {
 101                         hd[i+5*drive].start_sect = p->start_sect;
 102                         hd[i+5*drive].nr_sects = p->nr_sects;
 103                 }
 104                 brelse(bh);
 105         }
 106         printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
 107         mount_root();
 108         return (0);
 109 }
 110 
 111 static int controller_ready(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 112 {
 113         int retries=1000;
 114 
 115         while (--retries && (inb(HD_STATUS)&0xc0)!=0x40);
 116         return (retries);
 117 }
 118 
 119 static int win_result(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 120 {
 121         int i=inb(HD_STATUS);
 122 
 123         if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
 124                 == (READY_STAT | SEEK_STAT))
 125                 return(0); /* ok */
 126         if (i&1) i=inb(HD_ERROR);
 127         return (1);
 128 }
 129 
 130 static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
     /* [previous][next][first][last][top][bottom][index][help] */
 131                 unsigned int head,unsigned int cyl,unsigned int cmd,
 132                 void (*intr_addr)(void))
 133 {
 134         register int port asm("dx");
 135 
 136         if (drive>1 || head>15)
 137                 panic("Trying to write bad sector");
 138         if (!controller_ready())
 139                 panic("HD controller not ready");
 140         do_hd = intr_addr;
 141         outb(hd_info[drive].ctl,HD_CMD);
 142         port=HD_DATA;
 143         outb_p(hd_info[drive].wpcom>>2,++port);
 144         outb_p(nsect,++port);
 145         outb_p(sect,++port);
 146         outb_p(cyl,++port);
 147         outb_p(cyl>>8,++port);
 148         outb_p(0xA0|(drive<<4)|head,++port);
 149         outb(cmd,++port);
 150 }
 151 
 152 static int drive_busy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 153 {
 154         unsigned int i;
 155 
 156         for (i = 0; i < 100000; i++)
 157                 if (READY_STAT == (inb(HD_STATUS) & (BUSY_STAT | READY_STAT)))
 158                         break;
 159         i = inb(HD_STATUS);
 160         i &= BUSY_STAT | READY_STAT | SEEK_STAT;
 161         if (i == READY_STAT | SEEK_STAT)
 162                 return(0);
 163         printk("HD controller times out\n\r");
 164         return(1);
 165 }
 166 
 167 static void reset_controller(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 168 {
 169         int     i;
 170 
 171         outb(4,HD_CMD);
 172         for(i = 0; i < 1000; i++) nop();
 173         outb(0,HD_CMD);
 174         for(i = 0; i < 10000 && drive_busy(); i++) /* nothing */;
 175         if (drive_busy())
 176                 printk("HD-controller still busy\n\r");
 177         if((i = inb(ERR_STAT)) != 1)
 178                 printk("HD-controller reset failed: %02x\n\r",i);
 179 }
 180 
 181 static void redo_hd_request(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 182 {
 183         do_hd = NULL;
 184         do_hd_request();
 185 }
 186 
 187 static void reset_hd(int nr)
     /* [previous][next][first][last][top][bottom][index][help] */
 188 {
 189         reset_controller();
 190         hd_out(nr,hd_info[nr].sect,hd_info[nr].sect,hd_info[nr].head-1,
 191                 hd_info[nr].cyl,WIN_SPECIFY,&redo_hd_request);
 192 }
 193 
 194 void unexpected_hd_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 195 {
 196         printk("Unexpected HD interrupt\n\r");
 197 }
 198 
 199 static void bad_rw_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 200 {
 201         int i = CURRENT_DEV;
 202 
 203         if (CURRENT->errors++ >= MAX_ERRORS)
 204                 end_request(0);
 205         reset_hd(i);
 206 }
 207 
 208 static void read_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 209 {
 210         if (win_result()) {
 211                 bad_rw_intr();
 212                 return;
 213         }
 214         port_read(HD_DATA,CURRENT->buffer,256);
 215         CURRENT->errors = 0;
 216         CURRENT->buffer += 512;
 217         CURRENT->sector++;
 218         if (--CURRENT->nr_sectors)
 219                 return;
 220         end_request(1);
 221         do_hd_request();
 222 }
 223 
 224 static void write_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226         if (win_result()) {
 227                 bad_rw_intr();
 228                 return;
 229         }
 230         if (--CURRENT->nr_sectors) {
 231                 CURRENT->sector++;
 232                 CURRENT->buffer += 512;
 233                 port_write(HD_DATA,CURRENT->buffer,256);
 234                 return;
 235         }
 236         end_request(1);
 237         do_hd_request();
 238 }
 239 
 240 void do_hd_request(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242         int i,r;
 243         unsigned int block,dev;
 244         unsigned int sec,head,cyl;
 245         unsigned int nsect;
 246 
 247         INIT_REQUEST;
 248         dev = MINOR(CURRENT->dev);
 249         block = CURRENT->sector;
 250         if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
 251                 end_request(0);
 252                 goto repeat;
 253         }
 254         block += hd[dev].start_sect;
 255         dev /= 5;
 256         __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
 257                 "r" (hd_info[dev].sect));
 258         __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
 259                 "r" (hd_info[dev].head));
 260         sec++;
 261         nsect = CURRENT->nr_sectors;
 262         if (CURRENT->cmd == WRITE) {
 263                 hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
 264                 for(i=0 ; i<3000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
 265                         /* nothing */ ;
 266                 if (!r) {
 267                         reset_hd(CURRENT_DEV);
 268                         return;
 269                 }
 270                 port_write(HD_DATA,CURRENT->buffer,256);
 271         } else if (CURRENT->cmd == READ) {
 272                 hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
 273         } else
 274                 panic("unknown hd-command");
 275 }
 276 
 277 void hd_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 278 {
 279         blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 280         set_trap_gate(0x2E,&hd_interrupt);
 281         outb_p(inb_p(0x21)&0xfb,0x21);
 282         outb(inb_p(0xA1)&0xbf,0xA1);
 283 }

/* [previous][next][first][last][top][bottom][index][help] */