Fork me on GitHub

root/drivers/block/ide_hd.c

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

DEFINITIONS

This source file includes following definitions.
  1. assign_minors
  2. block2sector
  3. sector2chs
  4. ide_hd_open
  5. ide_hd_close
  6. ide_hd_read
  7. ide_hd_write
  8. ide_hd_ioctl
  9. ide_hd_init

   1 /*
   2  * fiwix/drivers/block/ide_hd.c
   3  *
   4  * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved.
   5  * Distributed under the terms of the Fiwix License.
   6  */
   7 
   8 #include <fiwix/asm.h>
   9 #include <fiwix/buffer.h>
  10 #include <fiwix/ide.h>
  11 #include <fiwix/ide_hd.h>
  12 #include <fiwix/ioctl.h>
  13 #include <fiwix/devices.h>
  14 #include <fiwix/sleep.h>
  15 #include <fiwix/timer.h>
  16 #include <fiwix/sched.h>
  17 #include <fiwix/cpu.h>
  18 #include <fiwix/fs.h>
  19 #include <fiwix/part.h>
  20 #include <fiwix/process.h>
  21 #include <fiwix/mm.h>
  22 #include <fiwix/errno.h>
  23 #include <fiwix/stdio.h>
  24 #include <fiwix/string.h>
  25 
  26 static struct fs_operations ide_hd_driver_fsop = {
  27         0,
  28         0,
  29 
  30         ide_hd_open,
  31         ide_hd_close,
  32         NULL,                   /* read */
  33         NULL,                   /* write */
  34         ide_hd_ioctl,
  35         NULL,                   /* lseek */
  36         NULL,                   /* readdir */
  37         NULL,                   /* mmap */
  38         NULL,                   /* select */
  39 
  40         NULL,                   /* readlink */
  41         NULL,                   /* followlink */
  42         NULL,                   /* bmap */
  43         NULL,                   /* lockup */
  44         NULL,                   /* rmdir */
  45         NULL,                   /* link */
  46         NULL,                   /* unlink */
  47         NULL,                   /* symlink */
  48         NULL,                   /* mkdir */
  49         NULL,                   /* mknod */
  50         NULL,                   /* truncate */
  51         NULL,                   /* create */
  52         NULL,                   /* rename */
  53 
  54         ide_hd_read,
  55         ide_hd_write,
  56 
  57         NULL,                   /* read_inode */
  58         NULL,                   /* write_inode */
  59         NULL,                   /* ialloc */
  60         NULL,                   /* ifree */
  61         NULL,                   /* statfs */
  62         NULL,                   /* read_superblock */
  63         NULL,                   /* remount_fs */
  64         NULL,                   /* write_superblock */
  65         NULL                    /* release_superblock */
  66 };
  67 
  68 static void assign_minors(__dev_t rdev, struct ide *ide, struct partition *part)
  69 {
  70         int n;
  71         int drive, minor;
  72         struct device *d;
  73 
  74         minor = 0;
  75         drive = get_ide_drive(rdev);
  76 
  77         if(!(d = get_device(BLK_DEV, rdev))) {
  78                 printk("WARNING: %s(): unable to assign minors to device %d,%d.\n", __FUNCTION__, MAJOR(rdev), MINOR(rdev));
  79                 return;
  80         }
  81 
  82         for(n = 0; n < NR_PARTITIONS; n++) {
  83                 if(drive == IDE_MASTER) {
  84                         minor = (1 << ide->drive[drive].minor_shift) + n;
  85                 }
  86                 if(drive == IDE_SLAVE) {
  87                         minor = (1 << ide->drive[drive].minor_shift) + n + 1;
  88                 }
  89                 CLEAR_MINOR(d->minors, minor);
  90                 if(part[n].type) {
  91                         SET_MINOR(d->minors, minor);
  92                         ((unsigned int *)d->device_data)[minor] = part[n].nr_sects / 2;
  93                 }
  94         }
  95 }
  96 
  97 static __off_t block2sector(__blk_t block, int blksize, struct partition *part, int minor)
  98 {
  99         __off_t sector;
 100 
 101         sector = block * (blksize / IDE_HD_SECTSIZE);
 102         if(minor) {
 103                 sector += part[minor - 1].startsect;
 104         }
 105         return sector;
 106 }
 107 
 108 static void sector2chs(__off_t offset, int *cyl, int *head, int *sector, struct ide_drv_ident *ident)
 109 {
 110         *cyl = offset / (ident->logic_spt * ident->logic_heads);
 111         *head = (offset / ident->logic_spt) % ident->logic_heads;
 112         *sector = (offset % ident->logic_spt) + 1;
 113 }
 114 
 115 int ide_hd_open(struct inode *i, struct fd *fd_table)
 116 {
 117         return 0;
 118 }
 119 
 120 int ide_hd_close(struct inode *i, struct fd *fd_table)
 121 {
 122         sync_buffers(i->rdev);
 123         return 0;
 124 }
 125 
 126 int ide_hd_read(__dev_t dev, __blk_t block, char *buffer, int blksize)
 127 {
 128         int minor;
 129         int drive;
 130         int sectors_to_read, cmd;
 131         int n, status, r, retries;
 132         int cyl, head, sector;
 133         __off_t offset;
 134         struct ide *ide;
 135         struct ide_drv_ident *ident;
 136         struct partition *part;
 137         struct callout_req creq;
 138 
 139         if(!(ide = get_ide_controller(dev))) {
 140                 return -EINVAL;
 141         }
 142 
 143         minor = MINOR(dev);
 144         if((drive = get_ide_drive(dev))) {
 145                 minor &= ~(1 << IDE_SLAVE_MSF);
 146         }
 147 
 148         SET_IDE_RDY_RETR(retries);
 149 
 150         blksize = blksize ? blksize : BLKSIZE_1K;
 151         sectors_to_read = MIN(blksize, PAGE_SIZE) / IDE_HD_SECTSIZE;
 152 
 153         ident = &ide->drive[drive].ident;
 154         part = ide->drive[drive].part_table;
 155         offset = block2sector(block, blksize, part, minor);
 156 
 157         CLI();
 158         lock_resource(&ide->resource);
 159 
 160         n = 0;
 161 
 162         while(n < sectors_to_read) {
 163                 if(ide->drive[drive].flags & DEVICE_HAS_RW_MULTIPLE) {
 164                         outport_b(ide->base + IDE_SECCNT, sectors_to_read);
 165                         cmd = ATA_READ_MULTIPLE_PIO;
 166                 } else {
 167                         outport_b(ide->base + IDE_SECCNT, 1);
 168                         cmd = ATA_READ_PIO;
 169                 }
 170 
 171                 if(ide->drive[drive].flags & DEVICE_REQUIRES_LBA) {
 172                         outport_b(ide->base + IDE_SECNUM, offset & 0xFF);
 173                         outport_b(ide->base + IDE_LCYL, (offset >> 8) & 0xFF);
 174                         outport_b(ide->base + IDE_HCYL, (offset >> 16) & 0xFF);
 175                         if(ide_drvsel(ide, drive, IDE_LBA_MODE, (offset >> 24) & 0x0F)) {
 176                                 printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name);
 177                                 unlock_resource(&ide->resource);
 178                                 return -EIO;
 179                         }
 180                 } else {
 181                         sector2chs(offset, &cyl, &head, &sector, ident);
 182                         outport_b(ide->base + IDE_SECNUM, sector);
 183                         outport_b(ide->base + IDE_LCYL, cyl);
 184                         outport_b(ide->base + IDE_HCYL, (cyl >> 8));
 185                         if(ide_drvsel(ide, drive, IDE_CHS_MODE, head)) {
 186                                 printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name);
 187                                 unlock_resource(&ide->resource);
 188                                 return -EIO;
 189                         }
 190                 }
 191                 outport_b(ide->base + IDE_COMMAND, cmd);
 192                 if(ide->channel == IDE_PRIMARY) {
 193                         ide0_wait_interrupt = ide->base;
 194                         creq.fn = ide0_timer;
 195                         creq.arg = 0;
 196                         add_callout(&creq, WAIT_FOR_IDE);
 197                         if(ide0_wait_interrupt) {
 198                                 sleep(&irq_ide0, PROC_UNINTERRUPTIBLE);
 199                         }
 200                         if(ide0_timeout) {
 201                                 printk("WARNING: %s(): %s: timeout on hard disk dev %d,%d during read.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev));
 202                         } else {
 203                                 del_callout(&creq);
 204                         }
 205                 }
 206                 if(ide->channel == IDE_SECONDARY) {
 207                         ide1_wait_interrupt = ide->base;
 208                         creq.fn = ide1_timer;
 209                         creq.arg = 0;
 210                         add_callout(&creq, WAIT_FOR_IDE);
 211                         if(ide1_wait_interrupt) {
 212                                 sleep(&irq_ide1, PROC_UNINTERRUPTIBLE);
 213                         }
 214                         if(ide1_timeout) {
 215                                 printk("WARNING: %s(): %s: timeout on hard disk dev %d,%d during read.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev));
 216                         } else {
 217                                 del_callout(&creq);
 218                         }
 219                 }
 220                 for(r = 0; r < retries; r++) {
 221                         status = inport_b(ide->base + IDE_STATUS);
 222                         if(!(status & IDE_STAT_BSY) && (status & IDE_STAT_DRQ)) {
 223                                 break;
 224                         }
 225                         ide_delay();
 226                 }
 227                 if(status & IDE_STAT_ERR) {
 228                         printk("WARNING: %s(): %s: error on hard disk dev %d,%d during read.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev));
 229                         printk("\tstatus=0x%x ", status);
 230                         ide_error(ide, status);
 231                         printk("\tblock %d, sector %d.\n", block, offset);
 232                         inport_b(ide->base + IDE_STATUS);       /* clear any pending interrupt */
 233                         unlock_resource(&ide->resource);
 234                         return -EIO;
 235                 }
 236 
 237                 if(cmd == ATA_READ_MULTIPLE_PIO) {
 238                         inport_sw(ide->base + IDE_DATA, (void *)buffer, (IDE_HD_SECTSIZE * sectors_to_read) / sizeof(short int));
 239                         break;
 240                 }
 241                 inport_sw(ide->base + IDE_DATA, (void *)buffer, IDE_HD_SECTSIZE / sizeof(short int));
 242                 inport_b(ide->ctrl + IDE_ALT_STATUS);   /* ignore results */
 243                 inport_b(ide->base + IDE_STATUS);       /* clear any pending interrupt */
 244                 n++;
 245                 offset++;
 246                 buffer += IDE_HD_SECTSIZE;
 247         }
 248         inport_b(ide->ctrl + IDE_ALT_STATUS);   /* ignore results */
 249         inport_b(ide->base + IDE_STATUS);       /* clear any pending interrupt */
 250         unlock_resource(&ide->resource);
 251         return sectors_to_read * IDE_HD_SECTSIZE;
 252 }
 253 
 254 int ide_hd_write(__dev_t dev, __blk_t block, char *buffer, int blksize)
 255 {
 256         int minor;
 257         int drive;
 258         int sectors_to_write, cmd;
 259         int n, status, r, retries;
 260         int cyl, head, sector;
 261         __off_t offset;
 262         struct ide *ide;
 263         struct ide_drv_ident *ident;
 264         struct partition *part;
 265         struct callout_req creq;
 266 
 267         if(!(ide = get_ide_controller(dev))) {
 268                 return -EINVAL;
 269         }
 270 
 271         minor = MINOR(dev);
 272         if((drive = get_ide_drive(dev))) {
 273                 minor &= ~(1 << IDE_SLAVE_MSF);
 274         }
 275 
 276         SET_IDE_RDY_RETR(retries);
 277 
 278         blksize = blksize ? blksize : BLKSIZE_1K;
 279         sectors_to_write = MIN(blksize, PAGE_SIZE) / IDE_HD_SECTSIZE;
 280 
 281         ident = &ide->drive[drive].ident;
 282         part = ide->drive[drive].part_table;
 283         offset = block2sector(block, blksize, part, minor);
 284 
 285         CLI();
 286         lock_resource(&ide->resource);
 287 
 288         n = 0;
 289 
 290         while(n < sectors_to_write) {
 291                 if(ide->drive[drive].flags & DEVICE_HAS_RW_MULTIPLE) {
 292                         outport_b(ide->base + IDE_SECCNT, sectors_to_write);
 293                         cmd = ATA_WRITE_MULTIPLE_PIO;
 294                 } else {
 295                         outport_b(ide->base + IDE_SECCNT, 1);
 296                         cmd = ATA_WRITE_PIO;
 297                 }
 298 
 299                 if(ide->drive[drive].flags & DEVICE_REQUIRES_LBA) {
 300                         outport_b(ide->base + IDE_SECNUM, offset & 0xFF);
 301                         outport_b(ide->base + IDE_LCYL, (offset >> 8) & 0xFF);
 302                         outport_b(ide->base + IDE_HCYL, (offset >> 16) & 0xFF);
 303                         if(ide_drvsel(ide, drive, IDE_LBA_MODE, (offset >> 24) & 0x0F)) {
 304                                 printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name);
 305                                 unlock_resource(&ide->resource);
 306                                 return -EIO;
 307                         }
 308                 } else {
 309                         sector2chs(offset, &cyl, &head, &sector, ident);
 310                         outport_b(ide->base + IDE_SECNUM, sector);
 311                         outport_b(ide->base + IDE_LCYL, cyl);
 312                         outport_b(ide->base + IDE_HCYL, (cyl >> 8));
 313                         if(ide_drvsel(ide, drive, IDE_CHS_MODE, head)) {
 314                                 printk("WARNING: %s(): %s: drive not ready.\n", __FUNCTION__, ide->drive[drive].dev_name);
 315                                 unlock_resource(&ide->resource);
 316                                 return -EIO;
 317                         }
 318                 }
 319                 outport_b(ide->base + IDE_COMMAND, cmd);
 320                 for(r = 0; r < retries; r++) {
 321                         status = inport_b(ide->base + IDE_STATUS);
 322                         if(!(status & IDE_STAT_BSY) && (status & IDE_STAT_DRQ)) {
 323                                 break;
 324                         }
 325                         ide_delay();
 326                 }
 327                 if(status & IDE_STAT_ERR) {
 328                         printk("WARNING: %s(): %s: error on hard disk dev %d,%d during write.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev));
 329                         printk("\tstatus=0x%x ", status);
 330                         ide_error(ide, status);
 331                         printk("\tblock %d, sector %d.\n", block, offset);
 332                         inport_b(ide->base + IDE_STATUS);       /* clear any pending interrupt */
 333                         unlock_resource(&ide->resource);
 334                         return -EIO;
 335                 }
 336 
 337                 if(cmd == ATA_WRITE_MULTIPLE_PIO) {
 338                         outport_sw(ide->base + IDE_DATA, (void *)buffer, (IDE_HD_SECTSIZE * sectors_to_write) / sizeof(short int));
 339                 } else {
 340                         outport_sw(ide->base + IDE_DATA, (void *)buffer, IDE_HD_SECTSIZE / sizeof(short int));
 341                 }
 342                 if(ide->channel == IDE_PRIMARY) {
 343                         ide0_wait_interrupt = ide->base;
 344                         creq.fn = ide0_timer;
 345                         creq.arg = 0;
 346                         add_callout(&creq, WAIT_FOR_IDE);
 347                         if(ide0_wait_interrupt) {
 348                                 sleep(&irq_ide0, PROC_UNINTERRUPTIBLE);
 349                         }
 350                         if(ide0_timeout) {
 351                                 printk("WARNING: %s(): %s: timeout on hard disk dev %d,%d during write.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev));
 352                         } else {
 353                                 del_callout(&creq);
 354                         }
 355                 }
 356                 if(ide->channel == IDE_SECONDARY) {
 357                         ide1_wait_interrupt = ide->base;
 358                         creq.fn = ide1_timer;
 359                         creq.arg = 0;
 360                         add_callout(&creq, WAIT_FOR_IDE);
 361                         if(ide1_wait_interrupt) {
 362                                 sleep(&irq_ide1, PROC_UNINTERRUPTIBLE);
 363                         }
 364                         if(ide1_timeout) {
 365                                 printk("WARNING: %s(): %s: timeout on hard disk dev %d,%d during write.\n", __FUNCTION__, ide->drive[drive].dev_name, MAJOR(dev), MINOR(dev));
 366                         } else {
 367                                 del_callout(&creq);
 368                         }
 369                 }
 370 
 371                 inport_b(ide->ctrl + IDE_ALT_STATUS);   /* ignore results */
 372                 inport_b(ide->base + IDE_STATUS);       /* clear any pending interrupt */
 373                 if(cmd == ATA_WRITE_MULTIPLE_PIO) {
 374                         break;
 375                 }
 376                 n++;
 377                 offset++;
 378                 buffer += IDE_HD_SECTSIZE;
 379         }
 380         inport_b(ide->ctrl + IDE_ALT_STATUS);   /* ignore results */
 381         inport_b(ide->base + IDE_STATUS);       /* clear any pending interrupt */
 382         unlock_resource(&ide->resource);
 383         return sectors_to_write * IDE_HD_SECTSIZE;
 384 }
 385 
 386 int ide_hd_ioctl(struct inode *i, int cmd, unsigned long int arg)
 387 {
 388         int minor;
 389         int drive;
 390         struct ide *ide;
 391         struct ide_drv_ident *ident;
 392         struct partition *part;
 393         struct hd_geometry *geom;
 394         int errno;
 395 
 396         if(!(ide = get_ide_controller(i->rdev))) {
 397                 return -EINVAL;
 398         }
 399 
 400         minor = MINOR(i->rdev);
 401         drive = get_ide_drive(i->rdev);
 402         if(drive) {
 403                 minor &= ~(1 << IDE_SLAVE_MSF);
 404         }
 405 
 406         ident = &ide->drive[drive].ident;
 407         part = ide->drive[drive].part_table;
 408 
 409         switch(cmd) {
 410                 case HDIO_GETGEO:
 411                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct hd_geometry)))) {
 412                                 return errno;
 413                         }
 414                         geom = (struct hd_geometry *)arg;
 415                         geom->cylinders = ident->logic_cyls;
 416                         geom->heads = (char)ident->logic_heads;
 417                         geom->sectors = (char)ident->logic_spt;
 418                         geom->start = 0;
 419                         if(minor) {
 420                                 geom->start = part[minor - 1].startsect;
 421                         }
 422                         break;
 423                 case BLKGETSIZE:
 424                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)))) {
 425                                 return errno;
 426                         }
 427                         if(!minor) {
 428                                 *(int *)arg = (unsigned int)ide->drive[drive].nr_sects;
 429                         } else {
 430                                 *(int *)arg = (unsigned int)ide->drive[drive].part_table[minor - 1].nr_sects;
 431                         }
 432                         break;
 433                 case BLKFLSBUF:
 434                         sync_buffers(i->rdev);
 435                         invalidate_buffers(i->rdev);
 436                         break;
 437                 case BLKRRPART:
 438                         read_msdos_partition(i->rdev, part);
 439                         assign_minors(i->rdev, ide, part);
 440                         break;
 441                 default:
 442                         return -EINVAL;
 443                         break;
 444         }
 445 
 446         return 0;
 447 }
 448 
 449 int ide_hd_init(struct ide *ide, int drive)
 450 {
 451         int n;
 452         __dev_t rdev;
 453         struct device *d;
 454         struct partition *part;
 455 
 456         rdev = 0;
 457         ide->drive[drive].fsop = &ide_hd_driver_fsop;
 458         part = ide->drive[drive].part_table;
 459 
 460         if(ide->channel == IDE_PRIMARY) {
 461                 if(drive == IDE_MASTER) {
 462                         rdev = MKDEV(IDE0_MAJOR, drive);
 463                         ide->drive[drive].minor_shift = IDE_MASTER_MSF;
 464                         if(!(d = get_device(BLK_DEV, rdev))) {
 465                                 return -EINVAL;
 466                         }
 467                         ((unsigned int *)d->device_data)[0] = ide->drive[drive].nr_sects / 2;
 468                 } else {
 469                         rdev = MKDEV(IDE0_MAJOR, 1 << IDE_SLAVE_MSF);
 470                         ide->drive[drive].minor_shift = IDE_SLAVE_MSF;
 471                         if(!(d = get_device(BLK_DEV, rdev))) {
 472                                 return -EINVAL;
 473                         }
 474                         ((unsigned int *)d->device_data)[1 << IDE_SLAVE_MSF] = ide->drive[drive].nr_sects / 2;
 475                 }
 476         } else if(ide->channel == IDE_SECONDARY) {
 477                 if(drive == IDE_MASTER) {
 478                         rdev = MKDEV(IDE1_MAJOR, drive);
 479                         ide->drive[drive].minor_shift = IDE_MASTER_MSF;
 480                         if(!(d = get_device(BLK_DEV, rdev))) {
 481                                 return -EINVAL;
 482                         }
 483                         ((unsigned int *)d->device_data)[0] = ide->drive[drive].nr_sects / 2;
 484                 } else {
 485                         rdev = MKDEV(IDE1_MAJOR, 1 << IDE_SLAVE_MSF);
 486                         ide->drive[drive].minor_shift = IDE_SLAVE_MSF;
 487                         if(!(d = get_device(BLK_DEV, rdev))) {
 488                                 return -EINVAL;
 489                         }
 490                         ((unsigned int *)d->device_data)[1 << IDE_SLAVE_MSF] = ide->drive[drive].nr_sects / 2;
 491                 }
 492         } else {
 493                 printk("WARNING: %s(): invalid drive number %d.\n", __FUNCTION__, drive);
 494                 return 1;
 495         }
 496 
 497         read_msdos_partition(rdev, part);
 498         assign_minors(rdev, ide, part);
 499         printk("                                partition summary: ");
 500         for(n = 0; n < NR_PARTITIONS; n++) {
 501                 /* status values other than 0x00 and 0x80 are invalid */
 502                 if(part[n].status && part[n].status != 0x80) {
 503                         continue;
 504                 }
 505                 if(part[n].type) {
 506                         printk("%s%d ", ide->drive[drive].dev_name, n + 1);
 507                 }
 508         }
 509         printk("\n");
 510 
 511         outport_b(ide->ctrl + IDE_DEV_CTRL, IDE_DEVCTR_NIEN);
 512         if(ide->drive[drive].flags & DEVICE_HAS_RW_MULTIPLE) {
 513                 /* read/write in 4KB blocks (8 sectors) by default */
 514                 outport_b(ide->base + IDE_SECCNT, PAGE_SIZE / IDE_HD_SECTSIZE);
 515                 outport_b(ide->base + IDE_COMMAND, ATA_SET_MULTIPLE_MODE);
 516                 ide_wait400ns(ide);
 517                 while(inport_b(ide->base + IDE_STATUS) & IDE_STAT_BSY);
 518         }
 519         outport_b(ide->ctrl + IDE_DEV_CTRL, IDE_DEVCTR_DRQ);
 520 
 521         return 0;
 522 }

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