好记性不如铅笔头

C && C++, kernel, 编程

驱动学习笔记:字符设备的简单读写

字符设备使用起来比较麻烦。这里笔记下最简单的使用方法。

CONTENTS

Makefile:

obj-m := hellocdev.o
KERNEL_DIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
	make -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules
clean:
	rm *.o *.ko *.mod.c
 
.PHONY:clean

hellocdev.c:

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


MODULE_LICENSE("Dual BSD/GPL");

static int default_Major = 0;
static int default_Minor = 0;
static int default_DevNum = 1;
module_param(default_Major, int, S_IRUGO);

#define BUFFER_SIZE 10
struct Hellocdev_Node_Struct 
{
	struct cdev _cdev;
	dev_t _devnum;
	char _buffer[BUFFER_SIZE];
}MyNode;

int hellocdev_open(struct inode *inode, struct file *filp)
{
    struct Hellocdev_Node_Struct *pMyNode;
    pMyNode = container_of(inode->i_cdev, struct Hellocdev_Node_Struct, _cdev);
    filp->private_data = pMyNode;
    
	printk(KERN_ALERT "hellocdev_open Called pMyNode->_buffer:%s\n", pMyNode->_buffer);
    return 0;
}
int hellocdev_release(struct inode *inode, struct file *filp)
{
	struct Hellocdev_Node_Struct *pMyNode = filp->private_data;

	printk(KERN_ALERT "hellocdev_release Called pMyNode->_buffer:%s\n", pMyNode->_buffer);
   return 0;
}
ssize_t hellocdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    struct Hellocdev_Node_Struct *pMyNode = filp->private_data;
        
    if (count >= BUFFER_SIZE)
    {
        count = BUFFER_SIZE;
    }
    if (copy_to_user(buf, pMyNode->_buffer + *f_pos, count))
    {
    	printk(KERN_ALERT "copy_to_user fails\n");
    	return 0;
    }
    
    *f_pos += count;
    if(*f_pos >= BUFFER_SIZE)
    {
	    return 0;
    }
    
	return count;
}
ssize_t hellocdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct Hellocdev_Node_Struct *pMyNode = filp->private_data;
    
    if (count >= BUFFER_SIZE)
    {
        count = BUFFER_SIZE;
    }

    if (copy_from_user(pMyNode->_buffer, buf, count))
    {
    	printk(KERN_ALERT "copy_from_user fails\n");
		return 0;
    }
    
    return count; 
}

struct file_operations fops = {
	.owner = THIS_MODULE,
	.read  = hellocdev_read,
	.write = hellocdev_write,
	.open  = hellocdev_open,
	.release = hellocdev_release,    
};

static int __init hello_init(void)
{
	int result = 0;

    printk(KERN_ALERT "Hello world\n");
    
    if(default_Major != 0)
    {
    	MyNode._devnum = MKDEV(default_Major, default_Minor);
		result = register_chrdev_region(MyNode._devnum, default_DevNum, "hellocdev");
    }else
    {
		result = alloc_chrdev_region(&MyNode._devnum, default_Minor, default_DevNum, "hellocdev");
		default_Major = MAJOR(MyNode._devnum); 
    }
	if(result < 0)
	{
		printk(KERN_ALERT "Get CDev Fails:[ %d , %d ]\n", default_Major, default_Minor);
		return result;
	}
	printk(KERN_ALERT "Get CDev Success:[ %d , %d ]\n", default_Major, default_Minor);
		
	cdev_init(&MyNode._cdev, &fops);
	MyNode._cdev.owner = THIS_MODULE;
	MyNode._cdev.ops = &fops;
	result = cdev_add(&MyNode._cdev, MyNode._devnum, default_DevNum);
	
	if (result)
 	{
 		printk(KERN_ALERT "cdev_add Fails\n");
	}

    return 0;
}

static void __exit hello_exit(void)
{
	cdev_del(&MyNode._cdev);
	unregister_chrdev_region(MyNode._devnum, default_DevNum);
    printk(KERN_ALERT "Goodbye world\n");
}
 
module_init(hello_init);
module_exit(hello_exit);

注册设备:

首先插入驱动:

root@ubuntu:/home/cstriker1407/drivers/hellocdev# make 
make -C /lib/modules/3.13.0-37-generic/build SUBDIRS=/home/cstriker1407/drivers/hellocdev modules
make[1]: 正在进入目录 `/usr/src/linux-headers-3.13.0-37-generic'
  CC [M]  /home/cstriker1407/drivers/hellocdev/hellocdev.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/cstriker1407/drivers/hellocdev/hellocdev.mod.o
  LD [M]  /home/cstriker1407/drivers/hellocdev/hellocdev.ko
make[1]:正在离开目录 `/usr/src/linux-headers-3.13.0-37-generic'
root@ubuntu:/home/cstriker1407/drivers/hellocdev# insmod hellocdev.ko 

然后查看设备号:

cstriker1407@ubuntu:~$ cat /proc/devices 
Character devices:
。。。。。
250 hellocdev
。。。。。

根据设备号新建节点并读写:

root@ubuntu:/dev# mknod hellocdev c 250 0
root@ubuntu:/dev# echo 12345678 > hellocdev 
root@ubuntu:/dev# cat hellocdev 
12345678

日志:

Oct 26 07:36:17 ubuntu kernel: [ 4019.585615] Goodbye world
Oct 26 07:36:27 ubuntu kernel: [ 4029.934183] Hello world
Oct 26 07:36:27 ubuntu kernel: [ 4029.934200] Get CDev Success:[ 250 , 0 ]
Oct 26 07:36:33 ubuntu kernel: [ 4035.157324] perf samples too long (2505 > 2500), lowering kernel.perf_event_max_sample_rate to 50000
Oct 26 07:37:15 ubuntu kernel: [ 4077.019422] hellocdev_open Called pMyNode->_buffer:
Oct 26 07:37:15 ubuntu kernel: [ 4077.019469] hellocdev_release Called pMyNode->_buffer:12345678
Oct 26 07:37:15 ubuntu kernel: [ 4077.019469] 
Oct 26 07:37:18 ubuntu kernel: [ 4079.967624] hellocdev_open Called pMyNode->_buffer:12345678
Oct 26 07:37:18 ubuntu kernel: [ 4079.967624] 
Oct 26 07:37:18 ubuntu kernel: [ 4079.967893] hellocdev_release Called pMyNode->_buffer:12345678
Oct 26 07:37:18 ubuntu kernel: [ 4079.967893] 
Oct 26 07:37:45 ubuntu kernel: [ 4107.149606] Goodbye world

 

发表评论

18 − 12 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据