C语言编程思想 — 用结构体实现面向对象和分离

原文地址:https://www.cnblogs.com/zhuangquan/p/12180652.html

说明: 

  以下示例是看到Linux中驱动一个比较简单的架构,然后记录下来。

  示例的功能是:将led通用的一些驱动代码和硬件相关代码分离开。

  什么是通用的驱动代码:比如注册file_operation结构体啊,class类等一些。就算我们修改驱动,这些也不会变动的代码。

  硬件相关代码:比如led的引脚地址

为什么要这样做?

  1.减少耦合性。将通用代码和硬件相关代码分离开。这样,当我们修改LED的驱动的时候,就不用看一段很长的代码。只需要单独修改跟硬件相关代码的那个文件。

  2.扩展性。我们需要去驱动其他板卡的LED的时候,那么我们也只需要修改跟硬件相关代码的那个文件。并且可以同时支持一个驱动代码对应多个不同的板卡。

 led_opr.h

#ifndef _LED_OPR_H
#define _LED_OPR_H

struct led_operations {
    int (*init) (int which); /* 初始化LED, which-哪个LED */
    int (*ctl) (int which, char status); /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
};

struct led_operations *get_board_led_opr(void);


#endif

led_opr.h中定义了一个结构体,结构体里面定义了函数指针init和ctl。并且有一个指针函数,返回的是led_operations类型的结构体指针

  首先分析一下为什么要使用函数指针。

  函数指针就是一个指向函数的指针,我们可以把写好的函数赋给这个函数指针。其他.c文件就可以直接用led_operations->init(参数)来调用我们在其他文件编写好的函数。就不需要每次都要声明一下函数,然后再调用,也不用担心函数名的问题。

  为什么要使用指针函数?

  指针函数就是返回指针的函数。我们需要把在board_demo.c中定义的led_operations结构体变量给其他.c的函数用。

board_demo.c

board_demo.c

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include "led_opr.h"

static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{

    printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    return 0;
}

static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
    printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, which, status ? "on" : "off");
    return 0;
}

static struct led_operations board_demo_led_opr = {
    .init = board_demo_led_init, // 此类赋值语法叫“指定初始化”
    .ctl  = board_demo_led_ctl,
};

struct led_operations *get_board_led_opr(void)
{
    return &board_demo_led_opr;
}

在board_demo.c中实现了board_demo_led_init和board_demo_led_ctl两个函数。然后将这两个函数赋给led_operations类型结构体变量board_demo_led_opr中的成员init和ctl。然后通过get_board_led_opr函数(指针函数:返回led_operations *类型的指针)可以在leddrv.c中获取到board_demo_led_opr结构体的地址,在其他.c中可以调用board_demo_led_init和board_demo_led_ctl这两个函数。

leddrv.c

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include "led_opr.h"

#define LED_NUM 2

/* 1. 确定主设备号                                                                 */
static int major = 0;
static struct class *led_class;
struct led_operations *p_led_opr;


#define MIN(a, b) (a < b ? a : b)

/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int err;
    char status;
    struct inode *inode = file_inode(file);
    int minor = iminor(inode);

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    err = copy_from_user(&status, buf, 1);

    /* 根据次设备号和status控制LED */
    p_led_opr->ctl(minor, status);

    return 1;
}

static int led_drv_open (struct inode *node, struct file *file)
{
    int minor = iminor(node);

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    /* 根据次设备号初始化LED */
    p_led_opr->init(minor);

    return 0;
}

static int led_drv_close (struct inode *node, struct file *file)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations led_drv = {
    .owner     = THIS_MODULE,
    .open    = led_drv_open,
    .read    = led_drv_read,
    .write   = led_drv_write,
    .release = led_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init led_init(void)
{
    int err;
    int i;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    major = register_chrdev(0, "100ask_led", &led_drv);  /* /dev/led */


    led_class = class_create(THIS_MODULE, "100ask_led_class");
    err = PTR_ERR(led_class);
    if (IS_ERR(led_class)) {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "led");
        return -1;
    }

    for (i = 0; i < LED_NUM; i++)
        device_create(led_class, NULL, MKDEV(major, i), NULL, "100ask_led%d", i); /* /dev/100ask_led0,1,... */

    p_led_opr = get_board_led_opr();

    return 0;
}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit led_exit(void)
{
    int i;
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    for (i = 0; i < LED_NUM; i++)
        device_destroy(led_class, MKDEV(major, i)); /* /dev/100ask_led0,1,... */

    device_destroy(led_class, MKDEV(major, 0));
    class_destroy(led_class);
    unregister_chrdev(major, "100ask_led");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

  25行struct led_operations *p_led_opr,定义了一个led_operations结构体指针变量。

  102行中get_board_led_opr()函数(就是一个指针函数,返回一个led_operations结构体变量)。

  49行和60行通过p_led_opr去调用在board_demo.c中写好的函数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值