/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* Vnode operations for VFS devices.31*32* These hand off to the functions in the VFS device structure (see dev.h)33* but take care of a bunch of common tasks in a uniform fashion.34*/35#include <types.h>36#include <kern/errno.h>37#include <kern/fcntl.h>38#include <stat.h>39#include <lib.h>40#include <uio.h>41#include <synch.h>42#include <vnode.h>43#include <device.h>4445/*46* Called for each open().47*48* We reject O_APPEND.49*/50static51int52dev_open(struct vnode *v, int flags)53{54struct device *d = v->vn_data;5556if (flags & (O_CREAT | O_TRUNC | O_EXCL | O_APPEND)) {57return EINVAL;58}5960return d->d_open(d, flags);61}6263/*64* Called on the last close().65* Just pass through.66*/67static68int69dev_close(struct vnode *v)70{71struct device *d = v->vn_data;72return d->d_close(d);73}7475/*76* Called when the vnode refcount reaches zero.77* Do nothing; devices are permanent.78*/79static80int81dev_reclaim(struct vnode *v)82{83(void)v;84/* nothing - device continues to exist even when not in use */85return 0;86}8788/*89* Called for read. Hand off to d_io.90*/91static92int93dev_read(struct vnode *v, struct uio *uio)94{95struct device *d = v->vn_data;96KASSERT(uio->uio_rw == UIO_READ);97return d->d_io(d, uio);98}99100/*101* Used for several functions with the same type signature that are102* not meaningful on devices.103*/104static105int106null_io(struct vnode *v, struct uio *uio)107{108(void)v;109(void)uio;110return EINVAL;111}112113/*114* Called for write. Hand off to d_io.115*/116static117int118dev_write(struct vnode *v, struct uio *uio)119{120struct device *d = v->vn_data;121KASSERT(uio->uio_rw == UIO_WRITE);122return d->d_io(d, uio);123}124125/*126* Called for ioctl(). Just pass through.127*/128static129int130dev_ioctl(struct vnode *v, int op, userptr_t data)131{132struct device *d = v->vn_data;133return d->d_ioctl(d, op, data);134}135136/*137* Called for stat().138* Set the type and the size (block devices only).139* The link count for a device is always 1.140*/141static142int143dev_stat(struct vnode *v, struct stat *statbuf)144{145struct device *d = v->vn_data;146int result;147148bzero(statbuf, sizeof(struct stat));149150if (d->d_blocks > 0) {151statbuf->st_size = d->d_blocks * d->d_blocksize;152statbuf->st_blksize = d->d_blocksize;153}154else {155statbuf->st_size = 0;156}157158result = VOP_GETTYPE(v, &statbuf->st_mode);159if (result) {160return result;161}162/* Make up some plausible default permissions. */163statbuf->st_mode |= 0600;164165statbuf->st_nlink = 1;166statbuf->st_blocks = d->d_blocks;167168/* The device number this device sits on (in OS/161, it doesn't) */169statbuf->st_dev = 0;170171/* The device number this device *is* */172statbuf->st_rdev = d->d_devnumber;173174return 0;175}176177/*178* Return the type. A device is a "block device" if it has a known179* length. A device that generates data in a stream is a "character180* device".181*/182static183int184dev_gettype(struct vnode *v, mode_t *ret)185{186struct device *d = v->vn_data;187if (d->d_blocks > 0) {188*ret = S_IFBLK;189}190else {191*ret = S_IFCHR;192}193return 0;194}195196/*197* Attempt a seek.198* For block devices, require block alignment.199* For character devices, prohibit seeking entirely.200*/201static202int203dev_tryseek(struct vnode *v, off_t pos)204{205struct device *d = v->vn_data;206if (d->d_blocks > 0) {207if ((pos % d->d_blocksize)!=0) {208/* not block-aligned */209return EINVAL;210}211if (pos < 0) {212/*213* Nonsensical.214* (note: off_t must be signed for SEEK_CUR or215* SEEK_END seeks to work, so this case must216* be checked.)217*/218return EINVAL;219}220if (pos / d->d_blocksize >= d->d_blocks) {221/* off the end */222return EINVAL;223}224}225else {226return ESPIPE;227}228return 0;229}230231/*232* For fsync() - meaningless, do nothing.233*/234static235int236null_fsync(struct vnode *v)237{238(void)v;239return 0;240}241242/*243* For mmap. If you want this to do anything, you have to write it244* yourself. Some devices may not make sense to map. Others do.245*/246static247int248dev_mmap(struct vnode *v /* add stuff as needed */)249{250(void)v;251return EUNIMP;252}253254/*255* For ftruncate().256*/257static258int259dev_truncate(struct vnode *v, off_t len)260{261struct device *d = v->vn_data;262263/*264* Allow truncating to the object's own size, if it has one.265*/266if (d->d_blocks > 0 && (off_t)(d->d_blocks*d->d_blocksize) == len) {267return 0;268}269270return EINVAL;271}272273/*274* For namefile (which implements "pwd")275*276* This should never be reached, as it's not possible to chdir to a277* device vnode.278*/279static280int281dev_namefile(struct vnode *v, struct uio *uio)282{283/*284* The name of a device is always just "device:". The VFS285* layer puts in the device name for us, so we don't need to286* do anything further.287*/288289(void)v;290(void)uio;291292return 0;293}294295/*296* Operations that are completely meaningless on devices.297*/298299static300int301null_creat(struct vnode *v, const char *name, bool excl, mode_t mode,302struct vnode **result)303{304(void)v;305(void)name;306(void)excl;307(void)mode;308(void)result;309return ENOTDIR;310}311312static313int314null_mkdir(struct vnode *v, const char *name, mode_t mode)315{316(void)v;317(void)name;318(void)mode;319return ENOTDIR;320}321322static323int324null_symlink(struct vnode *v, const char *contents, const char *name)325{326(void)v;327(void)contents;328(void)name;329return ENOTDIR;330}331332static333int334null_nameop(struct vnode *v, const char *name)335{336(void)v;337(void)name;338return ENOTDIR;339}340341static342int343null_link(struct vnode *v, const char *name, struct vnode *file)344{345(void)v;346(void)name;347(void)file;348return ENOTDIR;349}350351static352int353null_rename(struct vnode *v, const char *n1, struct vnode *v2, const char *n2)354{355(void)v;356(void)n1;357(void)v2;358(void)n2;359return ENOTDIR;360}361362363/*364* Name lookup.365*366* One interesting feature of device:name pathname syntax is that you367* can implement pathnames on arbitrary devices. For instance, if you368* had a graphics device that supported multiple resolutions (which we369* don't), you might arrange things so that you could open it with370* pathnames like "video:800x600/24bpp" in order to select the operating371* mode.372*373* However, we have no support for this in the base system.374*/375static376int377dev_lookup(struct vnode *dir,378char *pathname, struct vnode **result)379{380/*381* If the path was "device:", we get "". For that, return self.382* Anything else is an error.383* Increment the ref count of the vnode before returning it.384*/385if (strlen(pathname)>0) {386return ENOENT;387}388VOP_INCREF(dir);389*result = dir;390return 0;391}392393static394int395dev_lookparent(struct vnode *dir,396char *pathname, struct vnode **result,397char *namebuf, size_t buflen)398{399/*400* This is always an error.401*/402(void)dir;403(void)pathname;404(void)result;405(void)namebuf;406(void)buflen;407408return ENOTDIR;409}410411/*412* Function table for device vnodes.413*/414static const struct vnode_ops dev_vnode_ops = {415VOP_MAGIC,416417dev_open,418dev_close,419dev_reclaim,420dev_read,421null_io, /* readlink */422null_io, /* getdirentry */423dev_write,424dev_ioctl,425dev_stat,426dev_gettype,427dev_tryseek,428null_fsync,429dev_mmap,430dev_truncate,431dev_namefile,432null_creat,433null_symlink,434null_mkdir,435null_link,436null_nameop, /* remove */437null_nameop, /* rmdir */438null_rename,439dev_lookup,440dev_lookparent,441};442443/*444* Function to create a vnode for a VFS device.445*/446struct vnode *447dev_create_vnode(struct device *dev)448{449int result;450struct vnode *v;451452v = kmalloc(sizeof(struct vnode));453if (v==NULL) {454return NULL;455}456457result = VOP_INIT(v, &dev_vnode_ops, NULL, dev);458if (result != 0) {459panic("While creating vnode for device: VOP_INIT: %s\n",460strerror(result));461}462463return v;464}465466467