设备驱动基础学习--misc device简单实现

在Linux驱动中把无法归类的五花八门的设备定义为混杂设备(用miscdevice结构体表述)。miscdevice共享一个主设备号MISC_MAJOR(即10),但次设备号不同。 所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。

miscdevice的API实现在drivers/char/misc.c中。

struct miscdevice {
  int minor;
  const char *name;
  const struct file_operations *fops;
  struct list_head list;
  struct device *parent;
  struct device *this_device;
  const struct attribute_group **groups;
  const char *nodename;
  umode_t mode;
};

misc也是作为一个模块被加载到内核的,只不过是静态模块。misc_init是misc静态模块加载时的初始化函数。

static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.llseek = noop_llseek,
};

static int __init misc_init(void)
{
  int err;
  struct proc_dir_entry *ret;

  ret = proc_create("misc", 0, NULL, &misc_proc_fops);
  misc_class = class_create(THIS_MODULE, "misc");//创建misc设备节点
  err = PTR_ERR(misc_class);
  if (IS_ERR(misc_class))
    goto fail_remove;

  err = -EIO;
  if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))//注册名字为misc的字符设备,主设备号为MISC_MAJOR(10)
    goto fail_printk;
  misc_class->devnode = misc_devnode;
    return 0;

fail_printk:
  printk("unable to get major %d for misc devices\n", MISC_MAJOR);
  class_destroy(misc_class);
fail_remove:
  if (ret)
  remove_proc_entry("misc", NULL);
    return err;
}
subsys_initcall(misc_init);//静态加载,开机时加载

注册miscdevice:

int misc_register(struct miscdevice * misc)
{
  dev_t dev;
  int err = 0;
  bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);//是否是动态分配minor

  INIT_LIST_HEAD(&misc->list);

  mutex_lock(&misc_mtx);

  if (is_dynamic) {
    int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
    if (i >= DYNAMIC_MINORS) {
      err = -EBUSY;
      goto out;
    }
    misc->minor = DYNAMIC_MINORS - i - 1;
    set_bit(i, misc_minors);
  } else {
    struct miscdevice *c;

    list_for_each_entry(c, &misc_list, list) {//如果misc_list中存在和当前设备相同的minor返回错误
      if (c->minor == misc->minor) {
        err = -EBUSY;
        goto out;
      }
    }
  }

  dev = MKDEV(MISC_MAJOR, misc->minor);

  misc->this_device =device_create_with_groups(misc_class, misc->parent, dev,misc, misc->groups, "%s", misc->name);
  if (IS_ERR(misc->this_device)) {
  if (is_dynamic) {
    int i = DYNAMIC_MINORS - misc->minor - 1;

    if (i < DYNAMIC_MINORS && i >= 0)
      clear_bit(i, misc_minors);
      misc->minor = MISC_DYNAMIC_MINOR;
    }
    err = PTR_ERR(misc->this_device);
    goto out;
  }

/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
  list_add(&misc->list, &misc_list);//将miscdevice加到misc_list中。
out:
  mutex_unlock(&misc_mtx);
  return err;
}

当我们在open miscdevice时, misc_open将被调用到。

static int misc_open(struct inode * inode, struct file * file)
{
  int minor = iminor(inode);//从inode获取minor
  struct miscdevice *c;
  int err = -ENODEV;
  const struct file_operations *new_fops = NULL;

  mutex_lock(&misc_mtx);

  list_for_each_entry(c, &misc_list, list) {//在misc_list中搜索minor,并获取fops
    if (c->minor == minor) {
      new_fops = fops_get(c->fops);
      break;
    }
  }

  if (!new_fops) {
    mutex_unlock(&misc_mtx);
    request_module("char-major-%d-%d", MISC_MAJOR, minor);
    mutex_lock(&misc_mtx);

    list_for_each_entry(c, &misc_list, list) {
      if (c->minor == minor) {
      new_fops = fops_get(c->fops);
      break;
    }
  }
  if (!new_fops)
    goto fail;
  }

/*
* Place the miscdevice in the file's
* private_data so it can be used by the
* file operations, including f_op->open below
*/
  file->private_data = c;

  err = 0;
  replace_fops(file, new_fops);
  if (file->f_op->open)
  err = file->f_op->open(inode,file);//调用实际miscdevice的open函数。
fail:
  mutex_unlock(&misc_mtx);
  return err;
}

 

miscdevice实现的简单sample

fellowmisc.h

#ifndef _FELLOW_MISC_H_
#define _FELLOW_MISC_H_
#include <linux/ioctl.h>

struct miscdata {
  int val;
  char *str;
  unsigned int size;
};
#define FELLOW_MISC_IOC_MAGIC 'f'
#define FELLOW_MISC_IOC_PRINT _IO(FELLOW_MISC_IOC_MAGIC, 1)
#define FELLOW_MISC_IOC_GET _IOR(FELLOW_MISC_IOC_MAGIC, 2, struct miscdata)
#define FELLOW_MISC_IOC_SET _IOW(FELLOW_MISC_IOC_MAGIC, 3, struct miscdata)
#define FELLOW_MISC_IOC_MAXNR 3
#endif

 

fellowmisc.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "fellowmisc.h"
struct fellowmisc_dev{
  struct miscdevice misc;
  struct miscdata data;
};

struct fellowmisc_dev *fellowmisc_devp;
int fellowmisc_open(struct inode *inode, struct file *filep)
{
  filep->private_data = fellowmisc_devp;
  return 0;
}
int fellowmisc_release(struct inode *inode, struct file *filep)
{
  return 0;
}

long fellowmisc_ioctl(struct file *filep,unsigned int cmd,unsigned long arg)

{

  int ret = 0;
  struct fellowmisc_dev *devp = (struct fellowmisc_dev *)(filep->private_data);
  if (_IOC_TYPE(cmd) != FELLOW_MISC_IOC_MAGIC)
    return -EINVAL;
  if (_IOC_NR(cmd) > FELLOW_MISC_IOC_MAXNR)
    return -EINVAL;
  switch(cmd)
  {
    case FELLOW_MISC_IOC_PRINT:
      printk("FELLOW_MISC_IOC_PRINT\n");
      printk("val:%d, size: %d, str: %s\n", devp->data.val, devp->data.size, devp->data.str);
    break;
    case FELLOW_MISC_IOC_SET:
      printk("FELLOW_MISC_IOC_SET\n");
      ret = copy_from_user((unsigned char*)&(devp->data), (unsigned char *)arg, sizeof(struct miscdata));
      printk("set val:%d, size: %d, str: %s\n", devp->data.val, devp->data.size, devp->data.str);
    break;
    case FELLOW_MISC_IOC_GET:
      printk("FELLOW_MISC_IOC_GET\n");
      ret = copy_to_user((unsigned char*)arg,(unsigned char*)&(devp->data), sizeof(struct miscdata));
    break;
    default:
      return -EINVAL;

  }
  return ret;
}
static const struct file_operations fellowmisc_fops ={
.owner = THIS_MODULE,
.open = fellowmisc_open,
.release = fellowmisc_release,
.unlocked_ioctl = fellowmisc_ioctl,
};
static struct miscdevice fellow_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "fellowmisc",
.fops = &fellowmisc_fops,
};
static int fellowmisc_init(void)
{
  int ret = 0;
  printk("fellowmisc_init\n");
  fellowmisc_devp = kmalloc(sizeof(struct fellowmisc_dev), GFP_KERNEL);
  if (!fellowmisc_devp)
  {
    ret = -ENOMEM;
    goto fail;
  }
  memset(&(fellowmisc_devp->data), 0, sizeof(fellowmisc_devp->data));
  fellowmisc_devp->misc = fellow_misc;
  return misc_register(&(fellowmisc_devp->misc));
fail:
  return ret;
}

static void fellowmisc_exit(void)
{
  misc_deregister(&(fellowmisc_devp->misc));
  kfree(fellowmisc_devp);
}

MODULE_AUTHOR("fellow");
MODULE_LICENSE("GPL");
module_init(fellowmisc_init);
module_exit(fellowmisc_exit);

 

app.c

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "fellowmisc.h"
int main(void)
{
  int fd = open("/dev/fellowmisc", O_RDWR);
  if (fd < 0)
  {
    printf("open fail:%s\n", strerror(errno));
    return -1;
  }
  int ret = 0;
  struct miscdata data;
  data.val = 18;
  data.str = "fellowmisc";
  data.size = sizeof("fellowmisc");
  if ((ret = ioctl(fd, FELLOW_MISC_IOC_SET, &data)) < 0)
  {
    printf("ioctl set fail:%s\n", strerror(errno));
  }
  struct miscdata getdata;
  if ((ret = ioctl(fd, FELLOW_MISC_IOC_GET, &getdata)) < 0)
  {
    printf("ioctl get fail:%s\n", strerror(errno));
  }
  printf("get val:%d, str:%s, size: %d\n", getdata.val, getdata.str, getdata.size);
  if ((ret = ioctl(fd, FELLOW_MISC_IOC_PRINT, NULL)) < 0)
  {
    printf("ioctl print fail:%s\n", strerror(errno));
  }
  close(fd);
  return ret;
}

 

posted @ 2016-12-29 22:50  fellow_jing  阅读(9503)  评论(0编辑  收藏  举报