HAL(1) -- 编写android内核驱动

本文详细介绍了一种基于Linux内核的字符设备驱动开发过程,包括创建驱动目录、编写头文件及驱动程序、定义函数结构体等内容。通过实例演示了如何进行设备的初始化、读写操作及权限设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 进入到kernel/drivers目录,新建zhx_print目录:  

   $ cd ics/kernel/drivers
 
$ mkdir zhx_print

  二. 在zhx_print目录中增加zhx_print.h文件:

#ifndef _PRINT_H_
#define _PRINT_H_
 
#include <linux/cdev.h>
 
#define PRINT_NAME "zhx_print"
 
struct print_reg_dev
{
    struct cdev print_cdev;
    int val;
};
 
#endif

   这个头文件定义了一些字符串常量宏,在后面我们要用到!。此外,还定义了一个字符设备结构体print_dev,这个就是我们虚拟的硬件设备了,val成员变量就代表设备里面的寄存器,它的类型为int。

  三.zhx_print目录中增加print.c文件

这是驱动程序的实现部分。驱动程序的功能主要是向上层提供访问设备的寄存器的值,包括读和写。

这里,为了让程序看起来简单明了,我们只实现普遍的字符串驱动模式。 

首先是包含必要的头文件和定义三种访问设备的方法:

#include<linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/types.h>

#include <linux/proc_fs.h>
#include <linux/errno.h>

#include "print.h"
/*device major and minor*/
static int major_number = 0;
static int minor_number = 0;

static struct print_reg_dev *print_dev= NULL;
static struct class*print_class= NULL;

/*function*/
static int print_open(struct inode*inodep,struct file*filep);
static int print_close(struct inode*inodep,struct file*filep);
static ssize_t print_read(struct file*filep,char __user*buf,size_t count,loff_t *ppos);
static ssize_t print_write(struct file*filep,constchar __user *buf,size_t count,loff_t *ppos)

四:实现各个函数的功能

主要有 print_open,print_close ,print_read,print_write等

/*打开函数*/
static int print_open(struct inode*inodep,struct file*filep)
{
    struct print_reg_dev *dev = NULL;
    dev = container_of(inodep->i_cdev,struct print_reg_dev,print_cdev);
    if(dev == NULL)
    {
        printk("print_open is error.\n");
        return -1;
    }
    filep->private_data= (void*)dev;
    
    return 0;
}
/*关闭*/
static int print_close(struct inode*inodep,struct file*filep)
{
    return 0;
}
/*读取*/
static ssize_t print_read(struct file*filep,char __user*buff,
        size_t count,loff_t *ppos)
{
    /*定义自己定义的结构体函数*/
    struct print_reg_dev *dev = (struct print_reg_dev*)filep->private_data;
    /*返回值*/
    int ret = 0;
    printk(KERN_INFO"print_read : you read value\n");
    if(count != sizeof(dev->val))    
    {
        printk("print_read : sorry error\n");
        return 0;
    }
    if(copy_to_user(buff,&(dev->val),sizeof(dev->val)))
    {
        ret =  -EFAULT;
    }
    else
    {
        ret = sizeof(dev->val);
    }    
    return ret;
}
/*写入*/
static ssize_t print_write(struct file*filep,constchar __user *buf,
        size_t count,loff_t *ppos)
{
    struct print_reg_dev *dev = filep->private_data;
    int ret = 0;
    printk(KERN_INFO"print_write:yout write value\n");
    
    if(count != sizeof(dev->val))
    {
        printk("print_write : sorry error\n");
        return 0;
    }
    if(copy_from_user((void*)&dev->val,(void*)buf,sizeof(dev->val)))
    {
        ret = -EFAULT;
    }
    else
    {
        ret = sizeof(dev->val);
    }
    return ret;
}

五:定义字符驱动函数结构体
 
staticstruct file_operations print_fops=
{
    .owner = THIS_MODULE,
    .open = print_open,
    .release = print_close,
    .read = print_read,
    .write = print_write,
}
 
六:实现初始化init函数
 
staticint __init print_init(void)
{

    int err = 0;

    dev_t devno = 0;

    printk(KERN_ALERT"Initializing print device\n");
    
    //1.动态分配主从设备号,并且注册驱动
    err = alloc_chrdev_region(&devno,0,1,"zhx_print");

    if(err < 0)
    {
        printk(KERN_ALERT"Failed to alloc char dev region.\n");
        return err;
    }

    printk(KERN_INFO"print_init : alloc_chrdev_region sucessfully\n");

    //2.分配结构体内存空间
    print_dev = kmalloc(sizeof(struct print_reg_dev),GFP_KERNEL);

    if(print_dev == NULL)
    {
        printk(KERN_ALERT"Failed to kmalloc print_dev");
        err = -ENOMEM;
    }

    major_number = MAJOR(devno);
    minor_number = MINOR(devno);

    printk(KERN_INFO"print_init : kmalloc print_dev sucessfully\n");

    //3.初始化和注册cdev
    memset(print_dev,0,sizeof(struct print_reg_dev));//结构体初始化

    cdev_init(&(print_dev->print_cdev),&print_fops);//cdev初始化

    print_dev->print_cdev.owner= THIS_MODULE;
    print_dev->print_cdev.ops=&print_fops;

    err = cdev_add(&print_dev->print_cdev,devno,1);//注册cdev
    if(err)
        return err;
    print_dev->val=0;

    printk(KERN_INFO"print_init : cdev_init -- cdev_add sucessfully\n");

    //4.创建设备节点,就是在/dev/目录下创建zhx_print
    print_class = class_create(THIS_MODULE,"zhx_print");

    if(IS_ERR(print_class))    
    {
        err = PTR_ERR(print_class);
        printk(KERN_ALERT"Failed to create print device class.\n");
        cdev_del(&(print_dev->print_cdev));
        unregister_chrdev_region(devno,1);
        kfree(print_dev);
        return err;
    }

    device_create(print_class,NULL,devno,"%s","zhx_print");//创建zhx_print

    printk(KERN_INFO" init sucessfully!!!!!\n");

    return 0;
}
 
七:实现卸载函数
 
staticvoid __exit print_exit(void)
{
    //1.卸载设备节点
    device_destroy(print_class,MKDEV(major_number,minor_number));
    //2.卸载cdev
    if(print_dev)    
    {
        cdev_del(&(print_dev->print_cdev));
        kfree(print_dev);
    }
    //3.卸载
    unregister_chrdev_region(MKDEV(major_number,minor_number),1);
}
 
八:添加模块协议和指定模块初始化和卸载函数
MODULE_LICENSE("GPL");
module_init(print_init);
module_exit(print_exit);
 
九:在zhx_print目录下添加Kconfig文件,文件内容:
config ZHX_PRINT
    tristate "zhx write driver"
    default n
    help
    this is the driver
 
十:在zhx_print目录下添加Makefile文件,文件内容:
obj-$(CONFIG_ZHX_PRINT)+= print.o
 
十一:在ics/kernel/drivers目录上的Kconfig 中添加粗体红色语句:
...
source "drivers/i-tv/Kconfig"
source "drivers/gps/Kconfig"
source
"drivers/zhx_print/Kconfig"
endmenu
 
十二:在ics/kernel/drivers/目录下的Makefile中添加以下语句:
obj-$(CONFIG_ZHX_PRINT)+= zhx_print/
 
十三:在ics/kernel/目录下,敲入以下命令:
 
ics/kernle$  make menuconfig
 
到这里会弹出内核配置菜单,这里配置如下:
 
Device Drivers --->
                [*]zhx write driver
 
然后重新编译内核!
 
ics/kernle$  make-j4
 
到这里,zhx_print的驱动就添加进入内核里面了!!!  
 
十四:处理硬件设备访问权限问题
 
在硬件抽象层中,我们调用open函数来打开对应内核设备时:
 
if((dev->fd= open(DEVICE_NAME,O_RDWR))== -1)
{
   LOGE("Failed to open device file /dev/zhx_print");
   free(dev);
   return -EFAULT;
}
 
如果没有修改设备节点/dev/zhx_print权限,那么,就会提示:
 
Failed to open device file /dev/zhx_print  
 
 
这表明当前用户没有权限打开设备文件/dev/zhx_print,解决方法如下:
 在板子的命令行中 执行以下命令:

board@Ubuntu:$ chmod 777 /dev/zhx_print                                                                                                                                                                   

然后就可以操作啦!!!


介绍 STM32CubeTM是意法半导体的一项原始计划,旨在通过减少 开发工作,时间和成本。 STM32CubeTM涵盖了STM32产品组合。 STM32CubeTM版本1.x包括: STM32CubeMX,一种图形软件配置工具,允许生成C初始化 使用图形向导编写代码。 每个系列都提供了一个全面的嵌入式软件平台(例如STM32CubeF4 STM32F4系列) STM32Cube HAL是STM32抽象层嵌入式软件,可确保最大化 跨STM32产品组合的可移植性 一套一致的中间件组件,例如RTOS,USB,TCP / IP,图形 所有嵌入式软件实用程序均附带全套示例。 HAL驱动程序层提供了一组通用的多实例简单API(应用程序编程) 接口)与上层(应用程序,库和堆栈)进行交互。它由通用组成 和扩展API。它是直接基于通用体系结构构建的,并允许构建层, 例如中间件层,以实现其功能,而无需深入了解如何使用 单片机这种结构提高了库代码的可重用性,并保证了在其他库上的轻松移植 设备。 HAL驱动程序包括一整套现成的API,可简化用户应用程序 实施。例如,通信外围设备包含用于初始化和配置的API 外设,以基于轮询管理数据传输,处理中断或DMA,以及管理 通讯错误。 HAL驱动程序API分为两类:提供通用和通用的通用API 所有STM32系列和扩展API的函数,其中包括特定的和自定义的函数 给定的家庭或零件号。 HAL驱动程序是面向功能的,而不是面向IP的。例如,计时器API分为 IP提供的功能包括以下几类:基本计时器,捕获,脉冲宽度调制 (PWM)等。 驱动程序源代码是在严格的ANSI-C中开发的,使它独立于 开发工具。使用CodeSonarTM静态分析工具进行检查。它是有据可查的,并且 符合MISRA-C 2004。 HAL驱动程序层通过检查所有输入值来实现运行时故障检测 功能。这种动态检查有助于增强固件的鲁棒性。运行时检测 也适用于用户应用程序开发和调试。 本用户手册的结构如下: HAL驱动程序概述 每个外围设备驱动程序的详细描述:配置结构,功能以及使用方法 给定的API来构建您的应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值