驱动 (1.1) linux字符设备驱动的组织及应用程序如何交互

  • 字符设备驱动

#define WITH_SYSFS 0

#if WITH_SYSFS
#include <linux/device.h>
#endif

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


#if WITH_SYSFS
#define CLASS_NAME "hello_class"
#define DEVICE_NAME "hello_device"
#endif

#define DATA_SIZE   32

static dev_t dev_number;
//static struct device * hello_device;
#if WITH_SYSFS
static struct class * hello_class;
#endif
static struct cdev * hello_cdev;


typedef struct{
    void * data;
    int data_size;
} resource_t;

static resource_t resource;

int get_cpsr(void){
    int tmp;
	asm volatile (
		"mrs r3,cpsr\n\t"
		"mov %0,r3\n\t"
        : "=r" (tmp)
        :
	);
    return tmp;
}

int print_cpsr(void){
    printk("kernel space:0x%x\n",get_cpsr());
}

static int hello_open(struct inode *inode, struct file *file)
{
    print_cpsr();
    dump_stack();
	printk("my device is open\n");
    file->private_data = &resource;
	return 0;
}

static ssize_t hello_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
    int ret = 0;
    int actual_readed;
    int max_free;
    int need_read;


    dump_stack();
    resource_t * tmp_resource = file->private_data;
	printk("my device is read\n");

    max_free = tmp_resource->data_size - *ppos;
    printk("max_free:%d\n",max_free);
    need_read = max_free > count? count : max_free;
    printk("need_read:%d\n",need_read);

    if (need_read == 0)
        printk("hello_device,no space for read\n");

    ret = copy_to_user(buffer,((resource_t *)(file->private_data))->data + *ppos, need_read);
    if (ret == need_read)
        return -EFAULT;

    actual_readed = need_read - ret;
    printk("actual_readed:%d\n",actual_readed);
    *ppos += actual_readed;

	return actual_readed;
}

static ssize_t hello_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
    int ret = 0;
    int free;
    int actual_write;
    int need_write;

    resource_t * tmp_resource = file->private_data;
	printk("my device is write\n");
    dump_stack();

    free = tmp_resource->data_size - *ppos;
    printk("free:%d\n",free);
    need_write = free > count ? count : free;
    printk("need_write:%d\n",need_write);
    if (need_write == 0){
        printk("hello_device,no space for write\n");

    }

    ret = copy_from_user(((resource_t *)(file->private_data))->data + *ppos,buffer,need_write);
    if (ret == need_write)
        return -EFAULT;

    actual_write = need_write - ret;
    printk("actual_write:%d\n",actual_write);
    *ppos += actual_write;

	return actual_write;
}

static loff_t hello_llseek (struct file * file, loff_t ppos, int whence){

    loff_t newpos;
    resource_t * tmp_resource = file->private_data;
    dump_stack();

    switch(whence)
    {
        case 0: /* SEEK_SET */
            newpos = ppos;
            break;

        case 1: /* SEEK_CUR */
            newpos = file->f_pos + ppos;
            break;

        case 2: /* SEEK_END */
            newpos = tmp_resource->data_size + ppos;
            break;

        default: /* can't happen */
            return -EINVAL;
    }
    if (newpos < 0)
        return -EINVAL;
    file->f_pos = newpos;
    return newpos;

}

static int hello_close(struct inode *inode, struct file *file)
{
    dump_stack();
	printk("my device is close\n");
	return 0;
}

static struct file_operations hello_fops = {
	.owner  = THIS_MODULE,
	.open	= hello_open,
	.read 	= hello_read,
	.write 	= hello_write,
    .llseek = hello_llseek,
	.release = hello_close,

    .aio_read = NULL, // 如果不填充,默认为NULL
    .aio_write = NULL,
    .read_iter = NULL,
    .write_iter = NULL,
    .iterate = NULL,
    .poll = NULL,
    .unlocked_ioctl = NULL,
    .compat_ioctl = NULL,
    .mmap = NULL,
    .mremap = NULL,
    .flush = NULL,
    .fsync = NULL,
    .aio_fsync = NULL,
    .fasync = NULL,
    .lock = NULL,
    .sendpage = NULL,
    .get_unmapped_area = NULL,
    .check_flags = NULL,
    .flock = NULL,
    .splice_write = NULL,
    .splice_read = NULL,
    .setlease = NULL,
    .fallocate = NULL,
    .show_fdinfo = NULL,
};


static void data_init(void){
    resource.data_size = DATA_SIZE;
    resource.data = kzalloc(resource.data_size,GFP_KERNEL);
    strcpy(resource.data,"hello world");
}

static void data_deinit(void){

    kfree(resource.data);
    resource.data = NULL;
}
static int __init hello_init(void)
{
	int error;

    // 申请设备号
	error = alloc_chrdev_region(&dev_number, 0, 1, "hello_dev");
	if (error)
	{
		printk("hello: alloc_chardev_region failed! ");
		goto out;
	}

    printk("major is %d\n",MAJOR(dev_number));
    printk("minor is %d\n",MINOR(dev_number));

    // 申请 cdev
	hello_cdev = cdev_alloc();
	if (hello_cdev == NULL)
	{
		printk("hello: alloc cdev failed! ");
		error = -ENOMEM;
		goto out_chrdev;
	}
    // 填充 cdev
	hello_cdev->ops = &hello_fops;
	hello_cdev->owner = THIS_MODULE;

    // 绑定设备号到cdev,并注册cdev
	error = cdev_add(hello_cdev, dev_number, 1);
	if (error)
	{
		printk("hello: cdev_add failed! ");
		goto out_cdev;
	}

#if WITH_SYSFS
    // 创建 /sys/CLASS_NAME 文件夹
	hello_class = class_create(THIS_MODULE, CLASS_NAME);
	if (IS_ERR(hello_class))
	{
		error = PTR_ERR(hello_class);
		goto out_chrdev;
	}
    // 在 /sys/CLASS_NAME 文件夹下创建 /sys/CLASS_NAME/DEVICE_NAME
	device_create(hello_class, NULL, dev_number, NULL, DEVICE_NAME);
#endif

    data_init();
	printk("hello: Hello World \n");
	return 0;

out_cdev:
	cdev_del(hello_cdev);
out_chrdev:
	unregister_chrdev_region(hello_cdev->dev, 1);
out:
	return error;
}

static void __exit hello_exit(void)
{
#if WITH_SYSFS
    // 销毁文件
	device_destroy(hello_class, dev_number);
    // 销毁 /sys/CLASS_NAME 文件夹
	class_destroy(hello_class);
#endif
    // 注销设备号
	unregister_chrdev_region(hello_cdev->dev, 1);
    // 注销 cdev
	cdev_del(hello_cdev);

    data_deinit();

	printk("hello: Goodbye World \n");
}


module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("YWW");
MODULE_DESCRIPTION("HELLO_CLASS_AUTO_DEV");

  • 插入驱动
插入之后,打印如下
major is 252
minor is 0
hello: Hello World 
  • 手动创建设备文件
mknod  -m 666 /dev/hello_device  c 252 0 
  • app
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#define check_read() \
    do{\
        if (n < 0){\
            perror("read");\
        } else{\
            printf("buf read :%d,%s\n",n,buf);\
        }\
    }while(0)
#define check_write() \
    do{\
        if (n < 0){\
            perror("write");\
        } else{\
            printf("buf write :%d\n",n);\
        }\
    }while(0)


int main(void)
{
	char buf[32];
	int fd = 0;
    int n  = 0;

	fd = open("/dev/hello_device",O_RDWR);
    if (fd < 0){
        perror("open");
    }

	fd = open("/dev/hello_device",O_RDWR);
    if (fd < 0){
        perror("open");
    }

	n = read(fd,buf,32);
    check_read();

    lseek(fd,0,SEEK_SET); // 从 SEEK_SET 移动 0 // SEEK_CUR  // SEEK_END

	n = read(fd,buf,32);
    check_read();

    lseek(fd,0,SEEK_END); // 从 SEEK_SET 移动 0 // SEEK_CUR  // SEEK_END

	n = read(fd,buf,32);
    check_read();


    lseek(fd,0,SEEK_SET); // 从 SEEK_SET 移动 0 // SEEK_CUR  // SEEK_END


    n = write(fd,"hello2",6);
    check_write();

    memset(buf,0,32);
	read(fd,buf,32);
    check_read();

	close(fd);
	return 0;
}


调用堆栈

  • open
CPU: 0 PID: 704 Comm: char_user_test Tainted: G           O    4.0.0+ #62
Hardware name: ARM-Versatile Express
[<80016cb0>] (unwind_backtrace) from [<80012bc4>] (show_stack+0x20/0x24)
[<80012bc4>] (show_stack) from [<80543528>] (dump_stack+0x80/0x90)
[<80543528>] (dump_stack) from [<7f0040a8>] (hello_open+0x24/0x2c [class_test])
[<7f0040a8>] (hello_open [class_test]) from [<8011b5a4>] (chrdev_open+0xdc/0x19c)
[<8011b5a4>] (chrdev_open) from [<80115020>] (do_dentry_open.isra.2+0x204/0x31c)
[<80115020>] (do_dentry_open.isra.2) from [<801151d4>] (vfs_open+0x54/0x58)
[<801151d4>] (vfs_open) from [<80124a74>] (do_last.isra.17+0x458/0xc8c)
[<80124a74>] (do_last.isra.17) from [<80125334>] (path_openat+0x8c/0x5f8)
[<80125334>] (path_openat) from [<80126818>] (do_filp_open+0x3c/0x98)
[<80126818>] (do_filp_open) from [<80116290>] (do_sys_open+0x120/0x1e4)
[<80116290>] (do_sys_open) from [<8011637c>] (SyS_open+0x28/0x2c)
[<8011637c>] (SyS_open) from [<8000ebc0>] (ret_fast_syscall+0x0/0x34)
  • write
CPU: 0 PID: 704 Comm: char_user_test Tainted: G           O    4.0.0+ #62
Hardware name: ARM-Versatile Express
[<80016cb0>] (unwind_backtrace) from [<80012bc4>] (show_stack+0x20/0x24)
[<80012bc4>] (show_stack) from [<80543528>] (dump_stack+0x80/0x90)
[<80543528>] (dump_stack) from [<7f004050>] (hello_write+0x24/0x2c [class_test])
[<7f004050>] (hello_write [class_test]) from [<80117368>] (vfs_write+0xb0/0x1c0)
[<80117368>] (vfs_write) from [<801179fc>] (SyS_write+0x4c/0xa0)
[<801179fc>] (SyS_write) from [<8000ebc0>] (ret_fast_syscall+0x0/0x34)
  • read

CPU: 0 PID: 704 Comm: char_user_test Tainted: G           O    4.0.0+ #62
Hardware name: ARM-Versatile Express
[<80016cb0>] (unwind_backtrace) from [<80012bc4>] (show_stack+0x20/0x24)
[<80012bc4>] (show_stack) from [<80543528>] (dump_stack+0x80/0x90)
[<80543528>] (dump_stack) from [<7f004024>] (hello_read+0x24/0x2c [class_test])
[<7f004024>] (hello_read [class_test]) from [<801177c8>] (__vfs_read+0x28/0x5c)
[<801177c8>] (__vfs_read) from [<80117884>] (vfs_read+0x88/0x114)
[<80117884>] (vfs_read) from [<8011795c>] (SyS_read+0x4c/0xa0)
[<8011795c>] (SyS_read) from [<8000ebc0>] (ret_fast_syscall+0x0/0x34)
  • close

CPU: 0 PID: 704 Comm: char_user_test Tainted: G           O    4.0.0+ #62
Hardware name: ARM-Versatile Express
[<80016cb0>] (unwind_backtrace) from [<80012bc4>] (show_stack+0x20/0x24)
[<80012bc4>] (show_stack) from [<80543528>] (dump_stack+0x80/0x90)
[<80543528>] (dump_stack) from [<7f00407c>] (hello_close+0x24/0x2c [class_test])
[<7f00407c>] (hello_close [class_test]) from [<80118570>] (__fput+0x90/0x1d8)
[<80118570>] (__fput) from [<80118728>] (____fput+0x18/0x1c)
[<80118728>] (____fput) from [<80040c04>] (task_work_run+0xbc/0xf4)
[<80040c04>] (task_work_run) from [<8001241c>] (do_work_pending+0x8c/0xb4)
[<8001241c>] (do_work_pending) from [<8000ec04>] (work_pending+0xc/0x20)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值