Linux驱动之字符设备

Linux将所有的设备当作一个文件,为了达到这个目的,内核提供了一个结构体

struct file_operations {
  struct module *owner;
  loff_t(*llseek) (struct file *, loff_t, int);
  ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);
  ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
  ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);
  ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
  int (*readdir) (struct file *, void *, filldir_t);
  unsigned int (*poll) (struct file *, struct poll_table_struct *);
  int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  int (*mmap) (struct file *, struct vm_area_struct *);
  int (*open) (struct inode *, struct file *);
  int (*flush) (struct file *);
  int (*release) (struct inode *, struct file *);
  int (*fsync) (struct file *, struct dentry *, int datasync);
  int (*aio_fsync) (struct kiocb *, int datasync);
  int (*fasync) (int, struct file *, int);
  int (*lock) (struct file *, int, struct file_lock *);
  ssize_t(*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
  ssize_t(*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
  ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void __user *);
  ssize_t(*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  unsigned long (*get_unmapped_area) (struct file *, unsigned long,
         unsigned long, unsigned long,
         unsigned long);
};

这个结构体定义了设备作为一个文件可以被操作方法。这么多方法,有些比较简单,有些比较复杂。作为最简单的字符设备,我们只需要实现一些常用的接口,就能实现一般用用的设备操作

驱动的注册和销毁接口

设备的打开和关闭接口

设备的读和写接口

设备的命令控制接口

下面是一个hello world模板

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/jzsoc.h>
#include <linux/device.h>

#define TEST1_DEBUG
//#undef TEST_DEBUG

#if defined(TEST1_DEBUG)
#define TEST_DEBUG(fmt, ...) \
	printk(KERN_ALERT fmt, ##__VA_ARGS__)
#else
#define TEST_DEBUG(fmt, ...) 
#endif



static struct cdev *freg_cdev; //An instance of a character device
static dev_t ndev;             //The node of the device


static char freg_var[256] = "hello jin youzhi";
//static int freg_major = 250;
static int freg_major = 0;
static int freg_minor = 0;
static int howmany=1;
static char* whoem="hello";
module_param(howmany,int,S_IRUGO);
module_param(whoem,charp,S_IRUGO);


static int freg_open(struct inode *inode, struct file *filp)
{
	TEST_DEBUG("device opened\n");
	return 0;
}
static int freg_close(struct inode *inode, struct file *filp)
{
	TEST_DEBUG("device closed\n");
	return 0;
}
static ssize_t freg_read(struct file *filp, char __user *buf, size_t sz, loff_t *off)
{
	printk("read :%d,pos:%d\n",sz,*off);
	//Copy from kernel space to user space
	if(sz>sizeof(freg_var)-1-*off)
		sz=sizeof(freg_var)-1-*off;	
	if (copy_to_user(buf, &freg_var, sz)) {
		return - EFAULT;
	}
	*off+=sz;
	return sz;
}


static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos)
{
	printk("write:%d,pos:%d!\n",count,*f_pos);
	//Copy from user space to kernel space
	if(count>sizeof(freg_var)-1-*f_pos)
		count=sizeof(freg_var)-1-*f_pos;
	if (copy_from_user(&freg_var, buf, count)) {
		return -EFAULT;
	}
	return count;
}
static int freg_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
{
 	switch(cmd)
 	{
   	case 0:
   		TEST_DEBUG("device cmd 0\n");
		return 0;
   	default:
		TEST_DEBUG("device invalid command\n");
	return -1;
 	}
}

// file_operations is a very important data struct in character type device
struct file_operations freg_ops = {
	 .owner = THIS_MODULE,
       .open= freg_open,
       .release = freg_close,
       .read = freg_read,
	 .write = freg_write,
       .ioctl=freg_ioctl
};


// initialization function of the module
struct class *freg_class;
static int __init freg_init(void)
{
	int err;


	printk(KERN_ALERT"Initializing freg device.\n");
// -----------------------register device node number--------------------------
        if(freg_major)
	{
		ndev=MKDEV(freg_major,0);
		err=register_chrdev_region(ndev,1,"testc");
	}
	else
		err = alloc_chrdev_region(&ndev, 0, 1, "testc"); //申请字符设备编号
	if(err < 0)
		return err;
	freg_major = MAJOR(ndev);
	freg_minor = MINOR(ndev);


	printk("freg_init():major=%d, minor=%d\n", MAJOR(ndev), MINOR(ndev));
// ------------------------------------------------------


// ----------------------register cdev structure--------------------------
        
	freg_cdev = cdev_alloc(); //分配字符设备实例

	//初始化设备实例
	//主要设置owner和ops字段
	cdev_init(freg_cdev, &freg_ops); 
	freg_cdev->owner = THIS_MODULE;  //设置所有者字段
	//freg_cdev->ops = &freg_ops;   //


	err = cdev_add(freg_cdev, ndev, 1);//将设备添加到字符设备列表中,能在/proc/devices中查到
	if(err < 0)
		return err;
// -------------------------------------------------------

//==================register device node==================================
        freg_class = class_create(THIS_MODULE, "testc_class"); //建立设备类,在/sys/class能找到freg_class目录
        if(IS_ERR(freg_class))
        {
             return -1;
        }
       device_create(freg_class, NULL, ndev,  "testc",  "testc"); //建立设备节点,在/dev/目录下能找到设备

       TEST_DEBUG("test: all done\n");

//===================================================================

	printk(KERN_ALERT"Succedded to initialize freg device.\n");


	return 0;
}


static void __exit freg_exit(void)
{
	ndev = MKDEV(freg_major, freg_minor);
      printk(KERN_ALERT"Desctroy freg device class\n");
      device_destroy(freg_class, ndev);
      class_destroy(freg_class);
	printk(KERN_ALERT"Destroy freg device.\n");
	cdev_del(freg_cdev); //unregister the char_dev from the system
	unregister_chrdev_region(ndev, 1);//free the device node number
}


MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fake Register Driver");


module_init(freg_init);
module_exit(freg_exit);


其他一些接口例如poll也很有用,可以作为读写的补充。


分析:

1、初始化设备号

初始化设备号一般使用 alloc_chrdev_region 、 register_chrdev_region 或者 register_chrdev

参考http://blog.csdn.net/tommy_wxie/article/details/7195471
内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里。该散列表中的每一个元素是一个 char_device_struct 结构
register_chrdev成功后,设备名称会出现在/proc/devices文件中,此后可以使用mknod将设备映射成文件节点


2、初始化cdev结构体
静态初始化
struct cdev my_cdev;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
或者动态初始化
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops=&fops;

my_cdev->owner=THIS_MODULE

 

3、注册字符设备

cdev_add(&cdev, devno, 1)定义如下:返回负值表示错误

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
   p->dev = dev;
   p->count = count;
   return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

内核中所有的字符设备都会记录在一个 kobj_map结构的 cdev_map变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map()函数就是用来把字符设备编号和 cdev结构变量一起保存到 cdev_map这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup()函数,根据设备编号就可以找到cdev结构变量,从而取出其中的ops字段。

4、生成设备节点

struct class *myclass ;class_create(THIS_MODULE, “my_device_driver”);

device_create(myclass, NULL, MKDEV(major_num, minor_num), NULL, “my_device”);

class_create在/sys/class中创建一个类目录,device_create在/dev目录下建立一个设备节点。不过设备节点的建立实际上是udev在应用层实现的。

参考:http://www.embedu.org/Column/Column476.htm

 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值