|  | /* Copyright (c) 2010 The Regents of the University of California | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * Devfs: filesystem interfaces to devices.  For now, we just create the | 
|  | * needed/discovered devices in KFS in its /dev/ folder, and only do this for | 
|  | * stdin and stdout. */ | 
|  |  | 
|  | #include <devfs.h> | 
|  | #include <kfs.h> | 
|  | #include <error.h> | 
|  | #include <syscall.h> | 
|  | #include <process.h> | 
|  | #include <smp.h> | 
|  | #include <umem.h> | 
|  | #include <kmalloc.h> | 
|  | #include <console.h> | 
|  |  | 
|  | /* These structs are declared again and initialized farther down */ | 
|  | struct file_operations dev_f_op_stdin; | 
|  | struct file_operations dev_f_op_stdout; | 
|  | struct file_operations dev_f_op_null; | 
|  |  | 
|  | struct file *dev_stdin, *dev_stdout, *dev_stderr, *dev_null; | 
|  |  | 
|  | void devfs_init(void) | 
|  | { | 
|  | int mode; | 
|  | /* Make sure there is a dev directory */ | 
|  | struct dentry *dentry = lookup_dentry("/dev/", 0); | 
|  | if (!dentry) { | 
|  | assert(!do_mkdir("/dev/", S_IRWXU | S_IRWXG | S_IRWXO)); | 
|  | } else { | 
|  | kref_put(&dentry->d_kref); | 
|  | } | 
|  | /* Notice we don't kref_put().  We're storing the refs globally */ | 
|  | dev_stdin = make_device("/dev/stdin", S_IRUSR | S_IRGRP | S_IROTH, | 
|  | __S_IFCHR, &dev_f_op_stdin); | 
|  | dev_stdout = make_device("/dev/stdout", S_IWUSR | S_IWGRP | S_IWOTH, | 
|  | __S_IFCHR, &dev_f_op_stdout); | 
|  | /* Note stderr uses the same f_op as stdout */ | 
|  | dev_stderr = make_device("/dev/stderr", S_IWUSR | S_IWGRP | S_IWOTH, | 
|  | __S_IFCHR, &dev_f_op_stdout); | 
|  | dev_null = make_device("/dev/null", S_IWUSR | S_IWGRP | S_IWOTH, | 
|  | __S_IFCHR, &dev_f_op_null); | 
|  | } | 
|  |  | 
|  | /* Creates a device node at a given location in the FS-tree */ | 
|  | /* TODO: consider making this only deal with the inode */ | 
|  | struct file *make_device(char *path, int mode, int type, | 
|  | struct file_operations *fop) | 
|  | { | 
|  | struct file *f_dev = do_file_open(path, O_CREAT, mode); | 
|  | assert(f_dev); | 
|  | /* Overwrite the f_op with our own f_ops */ | 
|  | f_dev->f_dentry->d_inode->i_fop = fop; | 
|  | f_dev->f_op = fop; | 
|  | SET_FTYPE(f_dev->f_dentry->d_inode->i_mode, type); | 
|  | return f_dev; | 
|  | } | 
|  |  | 
|  | /* We provide a separate set of f_ops for devices (char and block), and the fops | 
|  | * is the only thing that differs from the regular KFS.  We need to do some | 
|  | * ghetto-overriding of these ops after we create them. */ | 
|  | int dev_c_llseek(struct file *file, off64_t offset, off64_t *ret, int whence) | 
|  | { | 
|  | set_errno(EINVAL); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* we don't allow mmapping of any device file */ | 
|  | int dev_mmap(struct file *file, struct vm_region *vmr) | 
|  | { | 
|  | set_errno(EINVAL); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* this is really /dev/console, and will need some tty work.  for now, no matter | 
|  | * how much they ask for, we return one character at a time. */ | 
|  | ssize_t dev_stdin_read(struct file *file, char *buf, size_t count, | 
|  | off64_t *offset) | 
|  | { | 
|  | char c; | 
|  | extern struct kb_buffer cons_buf; | 
|  |  | 
|  | if (!count) | 
|  | return 0; | 
|  | kb_get_from_buf(&cons_buf, &c, 1); | 
|  | /* TODO UMEM */ | 
|  | if (current) | 
|  | memcpy_to_user_errno(current, buf, &c, 1); | 
|  | else | 
|  | memcpy(buf, &c, 1); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | ssize_t dev_stdout_write(struct file *file, const char *buf, size_t count, | 
|  | off64_t *offset) | 
|  | { | 
|  | char *t_buf; | 
|  | struct proc *p = current; | 
|  | if (p) | 
|  | t_buf = user_strdup_errno(p, buf, count); | 
|  | else | 
|  | t_buf = (char*)buf; | 
|  | if (!t_buf) | 
|  | return -1; | 
|  | /* TODO: tty hack.  they are sending us an escape sequence, and the keyboard | 
|  | * would try to print it (which it can't do yet).  The hack is even dirtier | 
|  | * in that we only detect it if it is the first char, and we ignore | 
|  | * everything else. */ | 
|  | if (t_buf[0] != '\033') /* 0x1b */ | 
|  | cputbuf(t_buf, count); | 
|  | if (p) | 
|  | user_memdup_free(p, t_buf); | 
|  | return count; | 
|  | } | 
|  |  | 
|  | /* stdin/stdout/stderr file ops */ | 
|  | struct file_operations dev_f_op_stdin = { | 
|  | dev_c_llseek, | 
|  | dev_stdin_read, | 
|  | 0,	/* write - can't write to stdin */ | 
|  | kfs_readdir,	/* this will fail gracefully */ | 
|  | dev_mmap, | 
|  | kfs_open, | 
|  | kfs_flush, | 
|  | kfs_release, | 
|  | 0,	/* fsync - makes no sense */ | 
|  | kfs_poll, | 
|  | 0,	/* readv */ | 
|  | 0,	/* writev */ | 
|  | kfs_sendpage, | 
|  | kfs_check_flags, | 
|  | }; | 
|  |  | 
|  | struct file_operations dev_f_op_stdout = { | 
|  | dev_c_llseek, | 
|  | 0,	/* read - can't read stdout */ | 
|  | dev_stdout_write, | 
|  | kfs_readdir,	/* this will fail gracefully */ | 
|  | dev_mmap, | 
|  | kfs_open, | 
|  | kfs_flush, | 
|  | kfs_release, | 
|  | 0,	/* fsync - makes no sense */ | 
|  | kfs_poll, | 
|  | 0,	/* readv */ | 
|  | 0,	/* writev */ | 
|  | kfs_sendpage, | 
|  | kfs_check_flags, | 
|  | }; | 
|  |  | 
|  | ssize_t dev_null_read(struct file *file, char *buf, size_t count, | 
|  | off64_t *offset) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* /dev/null: just take whatever was given and pretend it was written */ | 
|  | ssize_t dev_null_write(struct file *file, const char *buf, size_t count, | 
|  | off64_t *offset) | 
|  | { | 
|  | return count; | 
|  | } | 
|  |  | 
|  | struct file_operations dev_f_op_null = { | 
|  | dev_c_llseek, | 
|  | dev_null_read, | 
|  | dev_null_write, | 
|  | kfs_readdir,	/* this will fail gracefully */ | 
|  | dev_mmap, | 
|  | kfs_open, | 
|  | kfs_flush, | 
|  | kfs_release, | 
|  | 0,	/* fsync - makes no sense */ | 
|  | kfs_poll, | 
|  | 0,	/* readv */ | 
|  | 0,	/* writev */ | 
|  | kfs_sendpage, | 
|  | kfs_check_flags, | 
|  | }; |