字符驱动之:实现读写

一、介绍:

        实现字符设备的读写其实就是实现驱动中的 file_operation 结构体里的read和write成员。

二、实现例子:


1. 驱动层:--相当于下边的 char_read.c 文件

    ①先实现 file_operations 结构体中列的我们想要实现的函数,比如

        static int hello_open (struct inode *inode, struct file *file)
        static int hello_release (struct inode *inode, struct file *file)
        ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
        ssize_t hello_write (struct file *filp, const char  *buf, size_t count, loff_t *f_pos)

    ②关联上 file_operations 中的入口:

        struct file_operations hello_fops = {
            .owner = THIS_MODULE,
            .open  = hello_open,
            .release = hello_release,
            .read  = hello_read,
            .write = hello_write
        };

    ③注册设备号并填充 cdev 结构体:

        dev_t devno = MKDEV (hello_major, hello_minor);
        result = register_chrdev_region (devno, number_of_devices, "hello");
        cdev_init (&cdev, &hello_fops);    //两个THIS_MODULE填充上设备号,hello_fops填充上file_operations(字符设备操作集合)
        cdev.owner = THIS_MODULE;

    ④注册 cdev 结构体:

        error = cdev_add (&cdev, devno , 1);
        这一步之后,我们关联的函数的符号就放到了应用程序可以调用的地方了。

    ⑤卸载的时候,先注销 cdev 结构体,再注销设备号

        cdev_del (&cdev);
        unregister_chrdev_region (devno, number_of_devices);
如果是上边的这种思路,还需要手动的创建设备节点,如果需要自动的在程序中创建设备节点,用如下的函数:

device_create函数(linux2.6之后版本):-- 创建设备节点(设备节点所在的位置是 /dev 下)
所需库:
    linux/device.h
原型:
    struct device *device_create(struct class *cls, struct device *parent,
                                                    dev_t devt, void *drvdata,
                                                    const char *fmt, ...);
参数:
    ①cls,指向将要注册设备节点的类
    ②parent,指向这个新的设备节点的类的父类,如果有的话,没有则是NULL
    ③devt,将要添加的字符设备的设备号
    ④drvdata,和类设备有关的设备的数据的指针(a pointer to a struct device that is assiociated with this class device.)
    ⑤fmt,要注册的设备要显示的节点名字
例子:
    device_create(my_class,NULL,devno,NULL,"hello");

class_create函数:-- 创建类(在 /sys/class/ 下)
头文件:
    linux/device.h
原型:
struct class *class_create(struct module *owner, const char *name)
    owner,pointer to the module that is to "own" this struct class
    name,pointer to a string for the name of this class.
例子:
    struct class *my_class = class_create(THIS_MODULE,"char_hello"); 

他们相应的删除函数:
    device_destroy(my_class, devno);
    class_destroy(my_class);
这两个的顺序不能调换

2. 用户层函数:-- 相当于下边的 test.c 文件

2.1 采用访问文件 IO 的方式打开文件

        int fd;
        fd = open ("/dev/hello",O_RDWR);
            第一个参数是创建的节点
            第二个参数:
        O_RDONLY:表示对文件只读
        O_WRONLY:表示对文件只写
        O_RDWR:表示对文件可读可写
            上边这3个标志位是互斥的
        O_CREAT:当文件不存在时,创建文件
        O_EXCL:配合O_CREAT使用,当文件已经存在时创建,那么就会出错。
        O_TRUNC:如果文件已经存在,先删除源文件内容。
        O_APPEND:以添加方式打开,对文件的写操作都是在文件的末尾进行

2.2 写:

write:--向文件描述符中写入数据
原型:
    ssize_t write(int fd,void *buf,size_t count);
参数:
    fd,要写的文件描述符
    buf,存放要写的数据
    count,期望执行一次write要写的字节个数
返回值:
    实际写入的字节个数。

例子:
         if (write (fd, buff, strlen(buff)) < 0)
        {
            perror("fail to write");
        }

2.3 读:

read:--读文件,ssize其实就是int型,从文件描述符中读取数据
原型:
    ssize_t read(int fd,void *buf,size_t count);
参数:
    fd,要读的文件描述符
    buf,存放读取到的数据
    count,期望执行一次read要读取的字节个数
返回值:
    实际读到的字节个数。
    0,网络上断开连接返回
    -1,没有独到
特点:
    read读取数据和\n,\0没任何关系。

例子
        printf ("Read returns %d\n", read (fd, buf, sizeof(buf)));

2.4 文件描述符关闭:

        close (fd);

3. 程序:

char_read.c

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/init.h>
  4. #include <linux/fs.h>
  5. #include <linux/cdev.h>
  6. #include <asm/uaccess.h>
  7. #include <linux/device.h>
  8. MODULE_LICENSE ("GPL");
  9. int hello_major = 250;
  10. int hello_minor = 0;
  11. int number_of_devices = 1;
  12. char read_data[128] = "foobar not equal to barfoo";
  13. char write_data[128] = {};
  14. struct cdev cdev;
  15. struct class *my_class;
  16. dev_t devno;
  17. static int hello_open (struct inode *inode, struct file *file)
  18. {
  19. printk (KERN_INFO "Hey! device opened\n");
  20. return 0;
  21. }
  22. static int hello_release (struct inode *inode, struct file *file)
  23. {
  24. printk (KERN_INFO "Hmmm... device closed\n");
  25. return 0;
  26. }
  27. ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
  28. {
  29. ssize_t result = 0;
  30. if (count > 127) count = 127;
  31. if (copy_to_user (buff, read_data, count))
  32. {
  33. result = -EFAULT;
  34. }
  35. else
  36. {
  37. printk (KERN_INFO "wrote %d bytes\n", (int)count);
  38. result = count;
  39. }
  40. return result;
  41. }
  42. ssize_t hello_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
  43. {
  44. ssize_t ret = 0;
  45. if (count > 127) return -ENOMEM;
  46. if (copy_from_user (write_data, buf, count)) {
  47. ret = -EFAULT;
  48. }
  49. else {
  50. write_data[count] = '\0';
  51. printk (KERN_INFO"Received: %s\n", write_data);
  52. ret = count;
  53. }
  54. return ret;
  55. }
  56. struct file_operations hello_fops = {
  57. .owner = THIS_MODULE,
  58. .open = hello_open,
  59. .release = hello_release,
  60. .read = hello_read,
  61. .write = hello_write
  62. };
  63. static void char_reg_setup_cdev (void) /* 在系统中添加一个设备 */
  64. {
  65. int error;
  66. cdev_init (&cdev, &hello_fops);
  67. cdev.owner = THIS_MODULE;
  68. error = cdev_add (&cdev, devno, number_of_devices);
  69. if (error)
  70. printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
  71. }
  72. static int char_dev_create (void) /* 为设备自动创建节点 */
  73. {
  74. my_class = class_create(THIS_MODULE,"char_hello"); // 类名为char_class
  75. if(IS_ERR(my_class))
  76. {
  77. printk("Err: failed in creating class.\n");
  78. return -1;
  79. }
  80. device_create(my_class,NULL,devno,NULL,"hello"); // 设备名为hello
  81. return 0;
  82. }
  83. static int __init hello_2_init (void)
  84. {
  85. int result;
  86. devno = MKDEV (hello_major, hello_minor);
  87. result = register_chrdev_region (devno, number_of_devices, "hello");
  88. if (result < 0) {
  89. printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
  90. return result;
  91. }
  92. char_dev_create();
  93. char_reg_setup_cdev();
  94. printk (KERN_INFO "char device registered\n");
  95. return 0;
  96. }
  97. static void __exit hello_2_exit (void)
  98. {
  99. cdev_del (&cdev);
  100. device_destroy(my_class, devno); //delete device node under /dev//必须先删除设备,再删除class类
  101. class_destroy(my_class); //delete class created by us
  102. unregister_chrdev_region (devno, number_of_devices);
  103. printk (KERN_INFO "char driver cleaned up\n");
  104. }
  105. module_init (hello_2_init);
  106. module_exit (hello_2_exit);

Makefile
  1. ifeq ($(KERNELRELEASE),)
  2. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
  3. #KERNELDIR ?= ~/wor_lip/linux-3.4.112
  4. PWD := $(shell pwd)
  5. modules:
  6. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  7. modules_install:
  8. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
  9. clean:
  10. rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules* Module*
  11. .PHONY: modules modules_install clean
  12. else
  13. obj-m := char_read.o
  14. endif

test.c
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <errno.h>
  7. #include <sys/types.h>
  8. int main (void)
  9. {
  10. int n, fd;
  11. char read_buf[256] ={};
  12. char *p =NULL;
  13. char write_buff[256]="beijing 15071 bu yao shui jiao";
  14. puts("1:this is read_buf:");
  15. printf("read_buf = %s\n", read_buf);
  16. fd = open ("/dev/hello", O_RDWR);
  17. if (fd < 0) {
  18. perror("open");
  19. exit(0);
  20. }
  21. printf ("\n/dev/hello opened, fd=%d\n", fd);
  22. /* read */
  23. printf ("Read returns %d\n", (int)read (fd, read_buf, sizeof(read_buf)));
  24. puts("\n2:this is read_buf:");
  25. printf("read_buf = %s\n", read_buf);
  26. /* write */
  27. printf("write buffer: %s\n", write_buff);
  28. if (write (fd, write_buff, strlen(write_buff)) < 0)
  29. {
  30. perror("fail to write");
  31. }
  32. close (fd);
  33. printf ("/dev/hello closed :)\n");
  34. return 0;
  35. }

将这些文件放到一个文件夹下,在文件夹下执行:
make
加载模块
sudo insmod char_read.ko
编译 test.c 文件:
gcc test.c
执行:
sudo ./a.out
执行的结果是:
1:this is read_buf:
read_buf = 

/dev/hello opened, fd=3
Read returns 127

2:this is read_buf:
read_buf = foobar not equal to barfoo
write buffer: beijing 15071 bu yao shui jiao
/dev/hello closed :)


dmesg显示的结果如下:
[12432.692810] char device registered
[12444.042488] Hey! device opened
[12444.042501] wrote 127 bytes
[12444.042526] Received: beijing 15071 bu yao shui jiao
[12444.042528] Hmmm... device closed

如果你的程序中没有自动的创建设备节点,那么,你还要用mknode来自己创建设备节点:

mknod /dev/设备要显示的名称 c 主设备号 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值