From 3609d2db7d26cbfda07ef4765569a0f42719c0f7 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 14 Oct 2024 23:08:28 +0200 Subject: [PATCH] Mon 14 Oct 23:06:38 CEST 2024 --- kernel/device.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 kernel/device.c diff --git a/kernel/device.c b/kernel/device.c new file mode 100644 index 0000000..7e2b06f --- /dev/null +++ b/kernel/device.c @@ -0,0 +1,176 @@ +/* +Copyright (C) 2016-2019 The University of Notre Dame +This software is distributed under the GNU General Public License. +See the file LICENSE for details. +*/ +#include "device.h" +#include "string.h" +#include "page.h" +#include "kmalloc.h" + +#include "kernel/stats.h" +#include "kernel/types.h" +#include "kernel/error.h" + +static struct device_driver *driver_list = 0; + +struct device { + struct device_driver *driver; + int refcount; + int unit; + int block_size; + int nblocks; + int multiplier; +}; + +void device_driver_register( struct device_driver *d ) +{ + d->next = driver_list; + d->stats = (struct device_driver_stats){0}; + driver_list = d; +} + +static struct device *device_create( struct device_driver *dd, int unit, int nblocks, int block_size ) +{ + struct device *d = kmalloc(sizeof(*d)); + d->refcount = 1; + d->driver = dd; + d->unit = unit; + d->block_size = block_size; + d->nblocks = nblocks; + +/* +If the device driver specifies a non-zero default multiplier, +then save it in this device instance. It gives the effect of +multiplying the block size, typically from the 512 ATA sector +size, up to the usual 4KB page size. See the effect below +in read/write. +*/ + + if(dd->multiplier>0) { + d->multiplier = dd->multiplier; + } else { + d->multiplier = 1; + } + return d; +} + +struct device *device_open( const char *name, int unit ) +{ + int nblocks, block_size; + char info[64]; + + struct device_driver *dd = device_driver_lookup(name); + if (dd && dd->probe(unit,&nblocks,&block_size,info)) { + return device_create(dd,unit,nblocks,block_size); + } + + return 0; +} + +struct device *device_addref( struct device *d ) +{ + d->refcount++; + return d; +} + +int device_set_multiplier( struct device *d, int multiplier ) +{ + if(multiplier<1 || multiplier*d->block_size>PAGE_SIZE ) { + return KERROR_INVALID_REQUEST; + } + + d->multiplier = multiplier; + + return 0; +} + +void device_close( struct device *d ) +{ + d->refcount--; + if(d->refcount<1) kfree(d); +} + +int device_read(struct device *d, void *data, int size, int offset) +{ + int status; + if(d->driver->read) { + status = d->driver->read(d->unit,data,size*d->multiplier,offset*d->multiplier); + if (status) { + d->driver->stats.blocks_read += size*d->multiplier; // number of blocks + } + return status; + } else { + return KERROR_NOT_IMPLEMENTED; + } +} + +int device_read_nonblock(struct device *d, void *data, int size, int offset) +{ + int status; + if(d->driver->read_nonblock) { + status = d->driver->read_nonblock(d->unit,data,size*d->multiplier,offset*d->multiplier); + if (status) { + d->driver->stats.blocks_read += size*d->multiplier; // number of blocks + } + return status; + } else { + return KERROR_NOT_IMPLEMENTED; + } +} + +int device_write(struct device *d, const void *data, int size, int offset) +{ + int status; + if(d->driver->write) { + status = d->driver->write(d->unit,data,size*d->multiplier,offset*d->multiplier); + if (!status) { + d->driver->stats.blocks_written += size*d->multiplier; + } + return status; + } else { + return KERROR_NOT_IMPLEMENTED; + } +} + +int device_block_size( struct device *d ) +{ + return d->block_size*d->multiplier; +} + +int device_nblocks( struct device *d ) +{ + return d->nblocks/d->multiplier; +} + +int device_unit( struct device *d ) +{ + return d->unit; +} + +const char * device_name( struct device *d ) +{ + return d->driver->name; +} + +struct device_driver * device_driver_lookup(const char *name) +{ + struct device_driver *dd = driver_list; + for(dd=driver_list; dd; dd=dd->next) { + if(!strcmp(dd->name, name)) { + break; + } + } + return dd; +} + +void device_driver_get_stats(const char * name, struct device_driver_stats * s) +{ + /* Get the device driver */ + struct device_driver *dd = device_driver_lookup(name); + + /* Copy stats into struct */ + if (dd) { + memcpy(s, &(dd->stats), sizeof(*s)); + } +}