| /* 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 | O_RDWR, 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, |
| }; |