⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chardev.c

📁 Simple Operating Systems (简称SOS)是一个可以运行在X86平台上(包括QEMU
💻 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 + -