使用结构体 file_operations封装驱动设备的操作

最近学习到了Linux驱动章节的课程,对设备的对应驱动的注册有些困惑,看了下发现是把设备的所有操作方法封装到结构体 file_operations 中,这个结构体为所有的设备文件都提供了统一的操作函数接口。然后把这个结构体连同设备的主设备号、名字(没啥用)一起,通过函数 register_chrdev(0, "first_drv", &first_drv_fops) 注册驱动模块到内核的字符设备数组chrdev的第xx项,具体注册和使用方法和裸机LCD及LCD控制器的一模一样!

使用的都是以面向对象的思维,结构化编程的思想。

1. 驱动程序使用如下:

 1 /*
 2 2018-10-18
 3 功能: 第一个驱动程序;
 4 */
 5
 6 #include <linux/module.h>
 7 #include <linux/kernel.h>
 8 #include <linux/fs.h>
 9 #include <linux/init.h>
10 #include <linux/delay.h>
11 #include <asm/uaccess.h>
12 #include <asm/irq.h>
13 #include <asm/io.h>
14 #include <asm/arch/regs-gpio.h>
15 #include <asm/hardware.h>
16
17 static int first_drv_open(struct inode * inode, struct file *file)
18 {
19     printk("first_drv_open\n");
20 }
21
22 static ssize_t first_drv_write(struct file *file, const char __usr *buf, size_t count, loff_t * ppos)
23 {
24     printk("first_drv_write\n");
25 }
26
27 static struct file_operations first_drv_fops =
28 {
29     .owner = THIS_MODULE,
30     .open  = first_drv_open,
31     .write = first_drv_write
32 };
33
34 int major;
35 static int first_drv_init(void)
36 {
37     major = register_chrdev(0, "first_drv", &first_drv_fops);
38     firstdrv_class = class_create(THIS_MODULE, "firstdrv");
39     first_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz");
40     gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
41     gpfdat = gpfcon + 1;
42     return 0;
43 }
44
45 static void first_drv_exit(void)
46 {
47     unregister_chrdev(major, "first_drv");
48     class_device_unregister(firstdrv_class_dev);
49     class_destroy(firstdrv_class);
50 }
51
52 module_init(first_drv_init);
53 module_exit(first_drv_exit);
54 MODULE_LICENSE("GPL");

2.文件lcd.h如下:

 1 #ifndef _LCD_H
 2 #define _LCD_H
 3
 4
 5 enum {
 6     NORMAL = 0,
 7     INVERT = 1,
 8 };
 9
10 /* NORMAL : 正常极性
11  * INVERT : 反转极性
12  */
13 typedef struct pins_polarity {
14     int de;    /* normal: 高电平时可以传输数据 */
15     int pwren; /* normal: 高电平有效 */
16     int vclk;  /* normal: 在下降沿获取数据 */
17     int rgb;   /* normal: 高电平表示1 */
18     int hsync; /* normal: 高脉冲 */
19     int vsync; /* normal: 高脉冲 */
20 }pins_polarity, *p_pins_polarity;
21
22 typedef struct time_sequence {
23     /* 垂直方向 */
24     int tvp; /* vysnc脉冲宽度 */
25     int tvb; /* 上边黑框, Vertical Back porch */
26     int tvf; /* 下边黑框, Vertical Front porch */
27
28     /* 水平方向 */
29     int thp; /* hsync脉冲宽度 */
30     int thb; /* 左边黑框, Horizontal Back porch */
31     int thf; /* 右边黑框, Horizontal Front porch */
32
33     int vclk;
34 }time_sequence, *p_time_sequence;
35
36
37 typedef struct lcd_params {
38     char *name;
39
40     /* 引脚极性 */
41     pins_polarity pins_pol;
42
43     /* 时序 */
44     time_sequence time_seq;
45
46     /* 分辨率, bpp */
47     int xres;
48     int yres;
49     int bpp;
50
51     /* framebuffer的地址 */
52     unsigned int fb_base;
53 }lcd_params, *p_lcd_params;
54
55 void get_lcd_params(unsigned int *fb_base, int *xres, int *yres, int *bpp);
56
57 #endif /* _LCD_H */

3.文件lcd_4.3.c如下:

#include "lcd.h"

#define LCD_FB_BASE 0x33c00000

lcd_params lcd_4_3_params = {
    .name = "lcd_4.3",
    .pins_pol = {
        .de    = NORMAL,    /* normal: 高电平时可以传输数据 */
        .pwren = NORMAL,    /* normal: 高电平有效 */
        .vclk  = NORMAL,    /* normal: 在下降沿获取数据 */
        .rgb   = NORMAL,    /* normal: 高电平表示1 */
        .hsync = INVERT,    /* normal: 高脉冲 */
        .vsync = INVERT,     /* normal: 高脉冲 */
    },
    .time_seq = {
        /* 垂直方向 */
        .tvp=    10, /* vysnc脉冲宽度 */
        .tvb=    2,  /* 上边黑框, Vertical Back porch */
        .tvf=    2,  /* 下边黑框, Vertical Front porch */

        /* 水平方向 */
        .thp=    41, /* hsync脉冲宽度 */
        .thb=    2,  /* 左边黑框, Horizontal Back porch */
        .thf=    2,  /* 右边黑框, Horizontal Front porch */

        .vclk=    9,  /* MHz */
    },
    .xres = 480,
    .yres = 272,
    .bpp  = 32,  /* 16, no 24bpp */
    .fb_base = LCD_FB_BASE,
};

void lcd_4_3_add(void)
{
    register_lcd(&lcd_4_3_params);
}

链接: https://www.jb51.net/article/37246.htm

本篇文章是对C语言中结构体的初始化进行了详细的分析介绍,需要的朋友参考下

《代码大全》建议在变量定义的时候进行初始化,但是很多人,特别是新人对结构体或者结构体数组定义是一般不会初始化,或者不知道怎么初始化。
1、初始化

复制代码代码如下:

typedef struct _TEST_T {
        int i;
        char c[10];
}TEST_T;
TEST_T gst  = {1, “12345”};//可以初始化,设置i为1,s为一个字符串.
TEST_T gst  = {1};//初始化个数少于实际个数时,只初始化前面的成员。
TEST_Tgst  = {.c=“12345”};//有选择的初始化成员。

2、复合字面量。
gst = (TEST_T){122, "1256"};//这是一个赋值语句,也可以作为初始化。可以出现在程序的任何地方。
当然也可以使用复合字面量来初始化:
gst = (TEST_T){.i=122, .c="123"};
3、结构体数组
可以用多个大括号括起来:
TEST_T gst[10] = {{},{},{},{}}
也可以初始化其中的一个元素:
TEST_T gst[10] = {[2]={}, [3]={}}
也可以使用复合字面量:
TEST_T gst[10] = {[2].i=0, [3].i={}}
为什么要初始化:
1、对局部变量初始化可以防止随机值产生的危害。
2、对全局变量初始化可以告诉编译器,这是一个定义,而不是一个声明。(如果两个c中有相同的全局变量定义,且没有初始化,编译器会认为第二个是声明而不是定义。)

原文地址:https://www.cnblogs.com/xiaohujian/p/9862643.html

时间: 2024-08-12 20:44:10

使用结构体 file_operations封装驱动设备的操作的相关文章

[转] 结构体file_operations

原文地址: http://www.cnblogs.com/sunyubo/archive/2010/12/22/2282079.html 结构体file_operations在头文件 linux/fs.h中定义,用来存储驱动内核模块提供的对设备进行各种操作的函数的指针.该结构体的每个域都对应着驱动内核模块用来处理某个被请求的 事务的函数的地址. 举个例子,每个字符设备需要定义一个用来读取设备数据的函数.结构体 file_operations中存储着内核模块中执行这项操作的函数的地址.一下是该结构

(ios开发)基本数据类型和结构体的封装与解封

ios开发基本数据类型和结构体的封装与解封 -- 妖妖 //知识: //因为基本数据类型和结构体不是继承自NSObject,所以它们不可以直接存放到数组和字典中. //数组和字典中只能存储对象类型,其他基本类型和结构体是没有办法放到数组和字典中的,当然你也是无法给它们发送消息的(也就是说有些NSObject的方法是无法调用的),这个时候通常会用到装箱(boxing)和拆箱(unboxing).但是在ObjC中装箱的过程必须手动实现,ObjC不支持自动装箱. //在ObjC中我们一般将基本数据类型

cdev成员结构体file_operations文件操作结构的分析

struct file_operations{ struct module *owner; // 指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES loff_t (*llseek) (struct file *, loff_t, int); // llseek用来修改文件当前的读写位置,返回新位置 // loff_t为一个”长偏移量”.当此函数指针为空,seek调用将会以不可预期的方式修改file结构中的位置计数器. ssize_t (*read) (st

结构体的封装、单例模式代码

//对象方法封装结构体 1 #import <Foundation/Foundation.h> 2 typedef struct 3 { 4     char *name; 5     int age; 6 }Student; 7 8 @interface CZValue : NSObject 9 @property (nonatomic,assign)Student stu; 10 - (instancetype)valueWithStudent:(Student)stu; 11 @end

file_operations结构体解析 1

注:学了这么长时间了,还没有好好看看 file_operations机构体,这其中还有很多的东西,当你学着学着的时候,就会用到这里面的一些系统调用对应的函数了,我在网上搜索之后,记录如下,一边将来查看..... 前沿:这些东西估计对你有用 linux驱动程序中最重要的涉及3个重要的内核数据结构,分别为file_operations,file和inode. 在linux中inode结构用于表示文件,而file结构则表示打开的文件的描述,因为对于单个文件而言可能会有许多个表示打开的文件的描述符,因而

file结构体中private_data指针的疑惑【转】

本文转载自:http://www.cnblogs.com/pengdonglin137/p/3328984.html hi all and barry, 最近在学习字符设备驱动,不太明白private_data在字符驱动中的作用,我们在 驱动中添加一个设备结构体,然后定义了这个结构体的全局指针变量,接着我们就能在 驱动程序中使用这个指针了.我看到很多驱动程序中都把结构体指针付给private_data, 然后对private_data操作. 为什么要使用private_data,难道仅仅是避免使

linux设备驱动归纳总结(三):2.字符型设备的操作open、close、read、write【转】

本文转载自:http://blog.chinaunix.net/uid-25014876-id-59417.html linux设备驱动归纳总结(三):2.字符型设备的操作open.close.read.write 一.文件操作结构体file_operations 继续上次没讲完的问题,文件操作结构体到底是什么东西,为什么我注册了设备之后什么现象都没有?可以验证文件操作结构体的内容. file_operations是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,

C#调用C/C++动态库 封送结构体,结构体数组

因为实验室图像处理的算法都是在OpenCV下写的,还有就是导航的算法也是用C++写的,然后界面部分要求在C#下写,所以不管是Socket通信,还是调用OpenCV的DLL模块,都设计到了C#和C++数据类型的对应,还有结构体的封装使用.在夸语言调用方面,Java和C#都只能调用C格式导出的动态库,因为C数据类型比较单一,容易映射,两者都是在本地端提供一套与之映射的C#或者Java的描述接口,通过底层处理这种映射关系达到调用的目的. 5月19日学习内容: http://www.cnblogs.co

struct结构体详解

为什么要有结构体 结构体和其他类型基础数据类型一样,例如int类型,char类型 只不过结构体可以做成你想要的数据类型.以方便日后的使用. 在实际项目中,结构体是大量存在的.研发人员常使用结构体来封装一些属性来组成新的类型.由于C语言内部程序比较简单,研发人员通常使用结构体创造新的"属性",其目的是简化运算. 结构体在函数中的作用不是简便,其最主要的作用就是封装.封装的好处就是可以再次利用.让使用者不必关心这个是什么,只要根据定义使用就可以了. 在C语言中,可以定义结构体类型,将多个相