Fork me on GitHub

root/fs/devices.c

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

DEFINITIONS

This source file includes following definitions.
  1. register_device
  2. get_device
  3. chr_dev_open
  4. blk_dev_open
  5. blk_dev_close
  6. blk_dev_read
  7. blk_dev_write
  8. blk_dev_ioctl
  9. blk_dev_lseek
  10. dev_init

   1 /*
   2  * fiwix/fs/devices.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/types.h>
   9 #include <fiwix/errno.h>
  10 #include <fiwix/buffer.h>
  11 #include <fiwix/devices.h>
  12 #include <fiwix/fs.h>
  13 #include <fiwix/mm.h>
  14 #include <fiwix/process.h>
  15 #include <fiwix/stdio.h>
  16 #include <fiwix/string.h>
  17 
  18 struct device *chr_device_table[NR_CHRDEV];
  19 struct device *blk_device_table[NR_BLKDEV];
  20 
  21 struct fs_operations def_chr_fsop = {
  22         0,
  23         0,
  24 
  25         chr_dev_open,
  26         NULL,                   /* close */
  27         NULL,                   /* read */
  28         NULL,                   /* write */
  29         NULL,                   /* ioctl */
  30         NULL,                   /* lseek */
  31         NULL,                   /* readdir */
  32         NULL,                   /* mmap */
  33         NULL,                   /* select */
  34 
  35         NULL,                   /* readlink */
  36         NULL,                   /* followlink */
  37         NULL,                   /* bmap */
  38         NULL,                   /* lockup */
  39         NULL,                   /* rmdir */
  40         NULL,                   /* link */
  41         NULL,                   /* unlink */
  42         NULL,                   /* symlink */
  43         NULL,                   /* mkdir */
  44         NULL,                   /* mknod */
  45         NULL,                   /* truncate */
  46         NULL,                   /* create */
  47         NULL,                   /* rename */
  48 
  49         NULL,                   /* read_block */
  50         NULL,                   /* write_block */
  51 
  52         NULL,                   /* read_inode */
  53         NULL,                   /* write_inode */
  54         NULL,                   /* ialloc */
  55         NULL,                   /* ifree */
  56         NULL,                   /* stats */
  57         NULL,                   /* read_superblock */
  58         NULL,                   /* remount_fs */
  59         NULL,                   /* write_superblock */
  60         NULL                    /* release_superblock */
  61 };
  62 
  63 struct fs_operations def_blk_fsop = {
  64         0,
  65         0,
  66 
  67         blk_dev_open,
  68         blk_dev_close,
  69         blk_dev_read,
  70         blk_dev_write,
  71         blk_dev_ioctl,
  72         blk_dev_lseek,
  73         NULL,                   /* readdir */
  74         NULL,                   /* mmap */
  75         NULL,                   /* select */
  76 
  77         NULL,                   /* readlink */
  78         NULL,                   /* followlink */
  79         NULL,                   /* bmap */
  80         NULL,                   /* lockup */
  81         NULL,                   /* rmdir */
  82         NULL,                   /* link */
  83         NULL,                   /* unlink */
  84         NULL,                   /* symlink */
  85         NULL,                   /* mkdir */
  86         NULL,                   /* mknod */
  87         NULL,                   /* truncate */
  88         NULL,                   /* create */
  89         NULL,                   /* rename */
  90 
  91         NULL,                   /* read_block */
  92         NULL,                   /* write_block */
  93 
  94         NULL,                   /* read_inode */
  95         NULL,                   /* write_inode */
  96         NULL,                   /* ialloc */
  97         NULL,                   /* ifree */
  98         NULL,                   /* stats */
  99         NULL,                   /* read_superblock */
 100         NULL,                   /* remount_fs */
 101         NULL,                   /* write_superblock */
 102         NULL                    /* release_superblock */
 103 };
 104 
 105 int register_device(int type, struct device *new_d)
 106 {
 107         struct device **d;
 108         int n, minors;
 109 
 110         switch(type) {
 111                 case CHR_DEV:
 112                         if(new_d->major >= NR_CHRDEV) {
 113                                 printk("%s(): character device major %d is greater than NR_CHRDEV (%d).\n", __FUNCTION__, new_d->major, NR_CHRDEV);
 114                                 return 1;
 115                         }
 116                         d = &chr_device_table[new_d->major];
 117                         break;
 118                 case BLK_DEV:
 119                         if(new_d->major >= NR_BLKDEV) {
 120                                 printk("%s(): block device major %d is greater than NR_BLKDEV (%d).\n", __FUNCTION__, new_d->major, NR_BLKDEV);
 121                                 return 1;
 122                         }
 123                         d = &blk_device_table[new_d->major];
 124                         break;
 125                 default:
 126                         printk("WARNING: %s(): invalid device type %d.\n", __FUNCTION__, type);
 127                         return 1;
 128                         break;
 129         }
 130 
 131         /* make sure there are minors defined */
 132         for(n = 0, minors = 0; n < 8; n++) {
 133                 minors += new_d->minors[n];
 134         }
 135         if(!minors) {
 136                 printk("WARNING: %s(): device major %d with no defined minors.\n", __FUNCTION__, new_d->major);
 137                 return 1;
 138         }
 139 
 140         if(*d) {
 141                 if(&(*d)->minors == &new_d->minors || (&(*d)->next && &(*d)->next->minors == &new_d->minors)) {
 142                         printk("WARNING: %s(): duplicated device major %d.\n", __FUNCTION__, new_d->major);
 143                         return 1;
 144                 }
 145                 do {
 146                         d = &(*d)->next;
 147                 } while(*d);
 148         }
 149         *d = new_d;
 150 
 151         return 0;
 152 }
 153 
 154 struct device * get_device(int type, __dev_t dev)
 155 {
 156         char *name;
 157         unsigned char major;
 158         struct device *d;
 159 
 160         major = MAJOR(dev);
 161 
 162         switch(type) {
 163                 case CHR_DEV:
 164                         if(major >= NR_CHRDEV) {
 165                                 printk("%s(): character device major %d is greater than NR_CHRDEV (%d).\n", __FUNCTION__, major, NR_CHRDEV);
 166                                 return NULL;
 167                         }
 168                         d = chr_device_table[major];
 169                         name = "character";
 170                         break;
 171                 case BLK_DEV:
 172                         if(major >= NR_BLKDEV) {
 173                                 printk("%s(): block device major %d is greater than NR_BLKDEV (%d).\n", __FUNCTION__, major, NR_BLKDEV);
 174                                 return NULL;
 175                         }
 176                         d = blk_device_table[major];
 177                         name = "block";
 178                         break;
 179                 default:
 180                         printk("WARNING: %s(): invalid device type %d.\n", __FUNCTION__, type);
 181                         return NULL;
 182         }
 183 
 184         while(d) {
 185                 if(d->major == major) {
 186                         if(TEST_MINOR(d->minors, MINOR(dev))) {
 187                                 return d;
 188                         }
 189                         d = d->next;
 190                         continue;
 191                 }
 192                 break;
 193         }
 194 
 195         printk("WARNING: %s(): %s device %d,%d not found.\n", __FUNCTION__, name, major, MINOR(dev));
 196         return NULL;
 197 }
 198 
 199 int chr_dev_open(struct inode *i, struct fd *fd_table)
 200 {
 201         struct device *d;
 202 
 203         if((d = get_device(CHR_DEV, i->rdev))) {
 204                 i->fsop = d->fsop;
 205                 if(i->fsop && i->fsop->open) {
 206                         return i->fsop->open(i, fd_table);
 207                 }
 208                 return -EINVAL;
 209         }
 210 
 211         return -ENXIO;
 212 }
 213 
 214 int blk_dev_open(struct inode *i, struct fd *fd_table)
 215 {
 216         struct device *d;
 217 
 218         if((d = get_device(BLK_DEV, i->rdev))) {
 219                 if(d->fsop && d->fsop->open) {
 220                         return d->fsop->open(i, fd_table);
 221                 }
 222                 return -EINVAL;
 223         }
 224 
 225         return -ENXIO;
 226 }
 227 
 228 int blk_dev_close(struct inode *i, struct fd *fd_table)
 229 {
 230         struct device *d;
 231 
 232         if((d = get_device(BLK_DEV, i->rdev))) {
 233                 if(d->fsop && d->fsop->close) {
 234                         return d->fsop->close(i, fd_table);
 235                 }
 236                 printk("WARNING: %s(): block device %d,%d does not have the close() method.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev));
 237                 return -EINVAL;
 238         }
 239 
 240         return -ENXIO;
 241 }
 242 
 243 int blk_dev_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count)
 244 {
 245         __blk_t block;
 246         __off_t total_read;
 247         unsigned long long int device_size;
 248         int blksize;
 249         unsigned int boffset, bytes;
 250         struct buffer *buf;
 251         struct device *d;
 252 
 253         if(!(d = get_device(BLK_DEV, i->rdev))) {
 254                 return -ENXIO;
 255         }
 256 
 257         blksize = d->blksize ? d->blksize : BLKSIZE_1K;
 258         total_read = 0;
 259         if(!d->device_data) {
 260                 printk("%s(): don't know the size of the block device %d,%d.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev));
 261                 return -EIO;
 262         }
 263 
 264         device_size = ((unsigned int *)d->device_data)[MINOR(i->rdev)];
 265         device_size *= 1024LLU;
 266 
 267         count = (fd_table->offset + count > device_size) ? device_size - fd_table->offset : count;
 268         if(!count || fd_table->offset > device_size) {
 269                 return 0;
 270         }
 271         while(count) {
 272                 boffset = fd_table->offset % blksize;
 273                 block = (fd_table->offset / blksize);
 274                 if(!(buf = bread(i->rdev, block, blksize))) {
 275                         return -EIO;
 276                 }
 277                 bytes = blksize - boffset;
 278                 bytes = MIN(bytes, count);
 279                 memcpy_b(buffer + total_read, buf->data + boffset, bytes);
 280                 total_read += bytes;
 281                 count -= bytes;
 282                 fd_table->offset += bytes;
 283                 brelse(buf);
 284         }
 285         return total_read;
 286 }
 287 
 288 int blk_dev_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count)
 289 {
 290         __blk_t block;
 291         __off_t total_written;
 292         unsigned long long int device_size;
 293         int blksize;
 294         unsigned int boffset, bytes;
 295         struct buffer *buf;
 296         struct device *d;
 297 
 298         if(!(d = get_device(BLK_DEV, i->rdev))) {
 299                 return -ENXIO;
 300         }
 301 
 302         blksize = d->blksize ? d->blksize : BLKSIZE_1K;
 303         total_written = 0;
 304         if(!d->device_data) {
 305                 printk("%s(): don't know the size of the block device %d,%d.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev));
 306                 return -EIO;
 307         }
 308 
 309         device_size = ((unsigned int *)d->device_data)[MINOR(i->rdev)];
 310         device_size *= 1024LLU;
 311 
 312         count = (fd_table->offset + count > device_size) ? device_size - fd_table->offset : count;
 313         if(!count || fd_table->offset > device_size) {
 314                 return -ENOSPC;
 315         }
 316         while(count) {
 317                 boffset = fd_table->offset % blksize;
 318                 block = (fd_table->offset / blksize);
 319                 if(!(buf = bread(i->rdev, block, blksize))) {
 320                         return -EIO;
 321                 }
 322                 bytes = blksize - boffset;
 323                 bytes = MIN(bytes, count);
 324                 memcpy_b(buf->data + boffset, buffer + total_written, bytes);
 325                 total_written += bytes;
 326                 count -= bytes;
 327                 fd_table->offset += bytes;
 328                 bwrite(buf);
 329         }
 330         return total_written;
 331 }
 332 
 333 int blk_dev_ioctl(struct inode *i, int cmd, unsigned long int arg)
 334 {
 335         struct device *d;
 336 
 337         if((d = get_device(BLK_DEV, i->rdev))) {
 338                 if(d->fsop && d->fsop->ioctl) {
 339                         return d->fsop->ioctl(i, cmd, arg);
 340                 }
 341                 printk("WARNING: %s(): block device %d,%d does not have the ioctl() method.\n", __FUNCTION__, MAJOR(i->rdev), MINOR(i->rdev));
 342                 return -EINVAL;
 343         }
 344 
 345         return -ENXIO;
 346 }
 347 
 348 int blk_dev_lseek(struct inode *i, __off_t offset)
 349 {
 350         struct device *d;
 351 
 352         if((d = get_device(BLK_DEV, i->rdev))) {
 353                 if(d->fsop && d->fsop->lseek) {
 354                         return d->fsop->lseek(i, offset);
 355                 }
 356         }
 357 
 358         return offset;
 359 }
 360 
 361 void dev_init(void)
 362 {
 363         memset_b(chr_device_table, NULL, sizeof(chr_device_table));
 364         memset_b(blk_device_table, NULL, sizeof(blk_device_table));
 365 }

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