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