📄 chardev.c
字号:
/* Copyright (C) 2005 David Decotigny, Thomas Petazzoni This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#include <sos/assert.h>#include <sos/types.h>#include <sos/fs.h>#include <sos/list.h>#include <sos/kmalloc.h>#include "chardev.h"struct sos_chardev_class{ sos_ui32_t device_class; struct sos_chardev_ops *ops; /** This corresponds to the chardev_class_custom_data field passed to open/read/etc. and to sos_chardev_register_class() */ void *custom_data; sos_count_t ref_cnt; /**< increased each time a FS node is opened */ struct sos_chardev_class *next, *prev;};struct sos_chardev_opened_file{ /** "normal" VFS opened file structure is available in this structure */ struct sos_fs_opened_file super; /** Additional information for this opened file: the device's class structure */ struct sos_chardev_class *class;};/** The list of registered classes, ie the dictionary major number -> device class description */static struct sos_chardev_class *registered_chardev_classes;/* Forward declarations */static struct sos_fs_ops_opened_file chardev_ops_opened_file;static struct sos_fs_ops_opened_chardev chardev_ops_opened_chardev;static sos_ret_tchardev_helper_sync(struct sos_fs_node * this);static sos_ret_tchardev_helper_new_opened_file(struct sos_fs_node * this, const struct sos_process * owner, sos_ui32_t open_flags, struct sos_fs_opened_file ** result_of);static sos_ret_tchardev_helper_close_opened_file(struct sos_fs_node * this, struct sos_fs_opened_file * of);static sos_ret_tduplicate_opened_chardev(struct sos_fs_opened_file *this, const struct sos_process * for_owner, struct sos_fs_opened_file **result);/** * Return the device descriptionn structure for the corresponding * device class, or NULL when none found. */static struct sos_chardev_class * lookup_chardev_class(sos_ui32_t device_class){ struct sos_chardev_class *chardev; int nb; list_foreach (registered_chardev_classes, chardev, nb) { if (chardev->device_class == device_class) return chardev; } return NULL;}sos_ret_t sos_chardev_register_class (sos_ui32_t device_class, struct sos_chardev_ops *ops, void * chardev_class_custom_data){ struct sos_chardev_class *chardev; /* Make sure this device class is not already registered */ chardev = lookup_chardev_class(device_class); if (NULL != chardev) return -SOS_EBUSY; /* Allocate and initialize a new device description */ chardev = (struct sos_chardev_class *) sos_kmalloc(sizeof(struct sos_chardev_class), 0); if (chardev == NULL) return -SOS_ENOMEM; chardev->device_class = device_class; chardev->custom_data = chardev_class_custom_data; chardev->ops = ops; chardev->ref_cnt = 1; /* insert it into the list */ list_add_tail (registered_chardev_classes, chardev); return SOS_OK;}sos_ret_t sos_chardev_unregister_class (sos_ui32_t device_class){ struct sos_chardev_class *chardev; /* Make sure this device class is already registered */ chardev = lookup_chardev_class(device_class); if (NULL == chardev) return -SOS_ENODEV; /* Make sure no files are already opened for it */ if (chardev->ref_cnt != 1) return -SOS_EBUSY; /* remove it from the list */ list_delete (registered_chardev_classes, chardev); return sos_kfree((sos_vaddr_t)chardev);}sos_ret_t sos_chardev_helper_ref_new_fsnode(struct sos_fs_node * this){ this->sync = chardev_helper_sync; this->new_opened_file = chardev_helper_new_opened_file; this->close_opened_file = chardev_helper_close_opened_file; return SOS_OK;}sos_ret_t sos_chardev_helper_release_fsnode(struct sos_fs_node * this){ return SOS_OK;}/** No synchronization to anything needed for a character device */static sos_ret_tchardev_helper_sync(struct sos_fs_node * this){ return SOS_OK;}/** Callback called each time an FS-node is opened by a user process: create a new sos_chardev_opened_file object associated to the correct major number, and call the device driver's open method */static sos_ret_tchardev_helper_new_opened_file(struct sos_fs_node * this, const struct sos_process * owner, sos_ui32_t open_flags, struct sos_fs_opened_file ** result_of){ sos_ret_t retval; struct sos_chardev_opened_file *chardev_of; /* Lookup the character device description structure */ struct sos_chardev_class * chardev = lookup_chardev_class(this->dev_id.device_class); if (NULL == chardev) return -SOS_ENODEV; /* Alloocate the new "open file" description structure */ chardev_of = (struct sos_chardev_opened_file*) sos_kmalloc(sizeof(struct sos_chardev_opened_file), 0); if (NULL == chardev_of) return -SOS_ENOMEM; memset(chardev_of, 0x0, sizeof(struct sos_chardev_opened_file)); chardev_of->class = chardev; *result_of = & chardev_of->super; /* Increase the reference coount for that node */ SOS_ASSERT_FATAL(chardev->ref_cnt >= 1); chardev->ref_cnt ++; /* Initialize the read/write/seek callbacks */ (*result_of)->owner = owner; (*result_of)->open_flags = open_flags; (*result_of)->ops_file = & chardev_ops_opened_file; (*result_of)->ops_chardev = & chardev_ops_opened_chardev; /* Call the open callback */ retval = chardev->ops->open(this, & chardev_of->super, chardev->custom_data); if (SOS_OK != retval) { sos_kfree((sos_vaddr_t) chardev_of); chardev->ref_cnt --; *result_of = NULL; return retval; } /* Specify the duplicate method */ (*result_of)->duplicate = duplicate_opened_chardev; return retval;}/** Callback called each time an opened file is closed. Un-allocate the associated sos_chardev_opened_file object */static sos_ret_tchardev_helper_close_opened_file(struct sos_fs_node * this, struct sos_fs_opened_file * of){ sos_ret_t retval; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)of); struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); /* Free the new "open file" description structure */ if (NULL != chardev->ops->close) retval = chardev->ops->close(& chardev_of->super, chardev->custom_data); else retval = SOS_OK; if (SOS_OK != retval) return retval; /* Decrease the reference coount for that node */ SOS_ASSERT_FATAL(chardev->ref_cnt > 1); chardev->ref_cnt --; sos_kfree((sos_vaddr_t) chardev_of); return retval;}/** * Callback called each time a process is "forked": create a new * sos_chardev_opened_file for the new process. * * @note Almost identical to the open callback. */static sos_ret_tduplicate_opened_chardev(struct sos_fs_opened_file *this, const struct sos_process * for_owner, struct sos_fs_opened_file **result){ sos_ret_t retval; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)this); struct sos_chardev_opened_file *new_chardev_of; struct sos_fs_node * fsnode = sos_fs_nscache_get_fs_node(this->direntry); *result = NULL; /* Lookup the character device description structure */ struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); /* Allocate a new duplicate copy of the original opened file */ new_chardev_of = (struct sos_chardev_opened_file*) sos_kmalloc(sizeof(struct sos_chardev_opened_file), 0); if (NULL == new_chardev_of) return -SOS_ENOMEM; memcpy(new_chardev_of, chardev_of, sizeof(*new_chardev_of)); new_chardev_of->super.owner = for_owner; new_chardev_of->super.direntry = NULL; /* Reset the direntry for the new opened file */ SOS_ASSERT_FATAL(chardev->ref_cnt > 1); chardev->ref_cnt ++; retval = chardev->ops->open(fsnode, & new_chardev_of->super, chardev->custom_data); if (SOS_OK != retval) { sos_kfree((sos_vaddr_t) new_chardev_of); chardev->ref_cnt --; return retval; } /* Make sure the required methods are overloaded */ SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file); SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file->seek); SOS_ASSERT_FATAL(NULL != new_chardev_of->super.ops_file->read); *result = & new_chardev_of->super; return retval;}/* * FS generic character device wrapper functions *//** * Callback called to change the position in the opened file: call the * seek method of the device driver. */static sos_ret_t chardev_wrap_seek(struct sos_fs_opened_file *this, sos_lsoffset_t offset, sos_seek_whence_t whence, /* out */ sos_lsoffset_t * result_position){ sos_ret_t retval = -SOS_ENOSYS; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)this); struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); SOS_ASSERT_FATAL(NULL != chardev->ops); if (NULL != chardev->ops->seek) retval = chardev->ops->seek(this, offset, whence, result_position); return retval;}/** * Callback called to read the contents of the opened file: call the * read method of the device driver. */static sos_ret_t chardev_wrap_read(struct sos_fs_opened_file *this, sos_uaddr_t dest_buf, sos_size_t * /* in/out */len){ sos_ret_t retval = -SOS_ENOSYS; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)this); struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); SOS_ASSERT_FATAL(NULL != chardev->ops); if (NULL != chardev->ops->read) retval = chardev->ops->read(this, dest_buf, len); return retval;}/** * Callback called to write bytes to the opened file: call the write * method of the device driver. */static sos_ret_t chardev_wrap_write(struct sos_fs_opened_file *this, sos_uaddr_t src_buf, sos_size_t * /* in/out */len){ sos_ret_t retval = -SOS_ENOSYS; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)this); struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); SOS_ASSERT_FATAL(NULL != chardev->ops); if (NULL != chardev->ops->write) retval = chardev->ops->write(this, src_buf, len); return retval;}/** * Callback called to map the contents of the opened file: call the * map method of the device driver. */static sos_ret_t chardev_wrap_mmap(struct sos_fs_opened_file *this, sos_uaddr_t *uaddr, sos_size_t size, sos_ui32_t access_rights, sos_ui32_t flags, sos_luoffset_t offset){ sos_ret_t retval = -SOS_ENOSYS; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)this); struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); SOS_ASSERT_FATAL(NULL != chardev->ops); if (NULL != chardev->ops->mmap) retval = chardev->ops->mmap(this, uaddr, size, access_rights, flags, offset); return retval;}/** * Callback called to change the state of the opened file: call the * fcntl method of the device driver. */static sos_ret_t chardev_wrap_fcntl(struct sos_fs_opened_file *this, int req_id, sos_ui32_t req_arg){ sos_ret_t retval = -SOS_ENOSYS; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)this); struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); SOS_ASSERT_FATAL(NULL != chardev->ops); if (NULL != chardev->ops->fcntl) retval = chardev->ops->fcntl(this, req_id, req_arg); return retval;}/** * Callback called to control the underlying device: call the ioctl * method of the device driver. */static sos_ret_t chardev_wrap_ioctl(struct sos_fs_opened_file *this, int req_id, sos_ui32_t req_arg){ sos_ret_t retval = -SOS_ENOSYS; struct sos_chardev_opened_file *chardev_of = ((struct sos_chardev_opened_file*)this); struct sos_chardev_class * chardev = chardev_of->class; SOS_ASSERT_FATAL(NULL != chardev); SOS_ASSERT_FATAL(NULL != chardev->ops); if (NULL != chardev->ops->ioctl) retval = chardev->ops->ioctl(this, req_id, req_arg); return retval;}/** * Gather the callbacks for a "character device" opened file */static struct sos_fs_ops_opened_file chardev_ops_opened_file = (struct sos_fs_ops_opened_file) { .seek = chardev_wrap_seek, .read = chardev_wrap_read, .write = chardev_wrap_write, .mmap = chardev_wrap_mmap, .fcntl = chardev_wrap_fcntl };static struct sos_fs_ops_opened_chardev chardev_ops_opened_chardev = (struct sos_fs_ops_opened_chardev) { .ioctl = chardev_wrap_ioctl };
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -