使用结构体 file_operations封装驱动设备的操作
Posted xiaohujian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用结构体 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 "); 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 "); 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中有相同的全局变量定义,且没有初始化,编译器会认为第二个是声明而不是定义。)
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中有相同的全局变量定义,且没有初始化,编译器会认为第二个是声明而不是定义。)
以上是关于使用结构体 file_operations封装驱动设备的操作的主要内容,如果未能解决你的问题,请参考以下文章