blob: f2dcbb557b8928c2098a6b8b85fdf7735f700abc [file] [log] [blame]
/* 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,
};