Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
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 }