博客
关于我
【安卓开发系列 -- 系统开发】字符设备驱动基础
阅读量:367 次
发布时间:2019-03-04

本文共 6512 字,大约阅读时间需要 21 分钟。

【安卓开发系列 -- 系统开发】字符设备驱动基础

【1】简单的字符设备驱动示例代码

驱动代码

#include 
#include
static int __init chrdev_init(void) { printk(KERN_INFO "chrdev_init helloworld init\n"); return 0;}static void __exit chrdev_exit(void) { printk(KERN_INFO "chrdev_exit helloworld exit\n");}module_init(chrdev_init);module_exit(chrdev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("aston");MODULE_DESCRIPTION("module test");MODULE_ALIAS("alias xxx");

ubuntu 系统中的 Makefile

KERN_VER = $(shell uname -r)KERN_DIR = /lib/modules/$(KERN_VER)/buildobj-m += module_test.oall:    make -C $(KERN_DIR) M=`pwd` modulesclean:    make -C $(KERN_DIR) M=`pwd` clean

交叉编译的 Makefile (基于 AIO-3399C 六核 AI 开发板)

ifneq ($(KERNELRELEASE),)    obj-m += module_test.oelse    KERNELDIR := /home/shallysun/proj/firefly-rk3399-Industry/kernel    PWD := $(shell pwd)    ARCH = arm64    CROSS_COMPILE=/home/shallysun/proj/firefly-rk3399-Industry/prebuilts/gcc/        linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-PHONY: modules cleanmodules:    $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modulesclean:    @rm -rf *.o *.order *.symvers *.mod.* .*.o.cmd .*.mod.o.cmd .*.ko.cmd .tmp_versions *.koendif

【2】常用的模块操作命令

  • lsmod:列表内核中已安装的模块
  • insmod:安装模块
  • modinfo:查看模块信息
  • rmmod:卸载模块
  • 【3】模块的安装(1) 使用 lsmod 查看安装前后的模块记录(2) insmod 与 module_init 宏模块源代码中用 module_init 宏声明了一个函数,本例中即指定 chrdev_init 函数和 insmod 命令绑定insmod module_test.ko 时会调用 chrdev_init 函数在 ubuntu 中使用 dmesg 查看打印信息

    【4】模块的版本信息

  • 使用 modinfo 查看模块版本
  • 内核 zImage 中也有确定的版本信息
  • insmod 时模块的 vermagic 必须与内核相同
  • 报错信息:insmod 错误:无法插入模块 module_test.ko:无效模块格式
  • 确保模块的 vermagic 与内核一致保证编译模块的内核源码树为正在运行的内核源码树
  • 【5】模块卸载

  • module_exit 与 rmmod 绑定
  • 使用 lsmod 查看卸载前后模块记录
  • 【6】模块中常用宏

  • MODULE_LICENSE:模块许可证
  • MODULE_AUTHOR:模块作者
  • MODULE_DESCRIPTION:模块介绍
  • MODULE_ALIAS:模块别名
  • 【7】函数修饰符

  • __init:将函数放入 .init.text 段
  • __exit:与 __init 类似
  • static:静态函数
  • printk:内核打印函数
  • 【8】printk 函数详解

  • 在内核源代码中使用
  • 打印级别控制
  • 在 ubuntu 中使用 dmesg 查看打印信息
  • 【9】驱动模块中的头文件驱动源代码中的头文件是内核源代码目录下的 include 目录下的头文件

    【10】驱动编译的 Makefile 分析

  • KERN_DIR:内核源码树目录
  • obj-m += module_test.o:编译模块
  • make -C $(KERN_DIR) M=pwd modules:编译模块
  • make clean:清除编译痕迹
  • 【11】字符设备驱动工作原理11.1 系统整体工作原理应用层 → API → 设备驱动 → 硬件

    11.2 file_operations 结构体每个设备驱动需要提供该结构体struct file_operations {struct module *owner;loff_t (*llseek)(struct file *, loff_t, int);ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);ssize_t (*read_iter)(struct kiocb *, struct iov_iter *);ssize_t (*write_iter)(struct kiocb *, struct iov_iter *);int (*iterate)(struct file *, struct dir_context *);int (*iterate_shared)(struct file *, struct dir_context *);__poll_t (*poll)(struct file *, struct poll_table_struct *);long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long);long (*compat_ioctl)(struct file *, unsigned int, unsigned long);int (*mmap)(struct file *, struct vm_area_struct *);unsigned long mmap_supported_flags;int (*open)(struct inode *, struct file *);int (*flush)(struct file *, fl_owner_t id);int (*release)(struct inode *, struct file *);int (*fsync)(struct file *, loff_t, loff_t, int datasync);int (*fasync)(int, struct file *, int);int (*lock)(struct file *, int, struct file_lock *);ssize_t (*sendpage)(struct file *, struct page *, int, size_t, loff_t *, int);unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);int (*check_flags)(int);int (*flock)(struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **, void **);long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);void (*show_fdinfo)(struct seq_file *m, struct file *f);#ifndef CONFIG_MMUunsigned (*mmap_capabilities)(struct file *);#endifssize_t (*copy_file_range)(struct file *, loff_t, struct file *, loff_t, size_t, unsigned int);int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t, u64);ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *, u64);}

    11.3 注册字符设备驱动register_chrdev 函数static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

    参数:

    • major:主设备号
    • name:驱动名称
    • fops:file_operations 结构体

    11.4 驱动中添加读写接口copy_from_user 和 copy_to_user 函数copy_from_user:将数据从用户空间复制到内核空间copy_to_user:将数据从内核空间复制到用户空间

    11.5 应用程序调用驱动设备文件的创建:mknod /dev/xxx c 主设备号 次设备号

    11.6 驱动中操控硬件与裸机相比的不同点:

    • 寄存器地址不同
    • 编程方法不同
    • 内核使用静态或动态映射

    11.7 静态映射操作 LED示例代码:#include <linux/module.h>#include <linux/init.h>#include <asm/uaccess.h>#include <machregs-gpio.h>

    #define MYMAJOR 200#define MYNAME "testchar"#define GPJ0CON_S5PV210_GPJ0CON#define GPJ0DAT_S5PV210_GPJ0DAT

    int mymajor;char kbuf[100];

    static int test_chrdev_open(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_open\n");rGPJ0CON = 0x11111111;rGPJ0DAT = ((0 << 3) | (0 << 4) | (0 << 5));return 0;}

    static int test_chrdev_release(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_release\n");rGPJ0DAT = ((1 << 3) | (1 << 4) | (1 << 5));return 0;}

    ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) {int ret = -1;ret = copy_to_user(ubuf, kbuf, count);if (ret) {printk(KERN_ERR "copy_to_user fail\n");return -EINVAL;}return 0;}

    static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) {int ret = -1;ret = copy_from_user(kbuf, ubuf, count);if (ret) {printk(KERN_ERR "copy_from_user fail\n");return -EINVAL;}if (kbuf[0] == '1') {rGPJ0DAT = ((0 << 3) | (0 << 4) | (0 << 5));} else if (kbuf[0] == '0') {rGPJ0DAT = ((1 << 3) | (1 << 4) | (1 << 5));}return 0;}

    static const struct file_operations test_fops = {.owner = THIS_MODULE,.open = test_chrdev_open,.release = test_chrdev_release,.write = test_chrdev_write,.read = test_chrdev_read,};

    static int __init chrdev_init(void) {printk(KERN_INFO "chrdev_init helloworld init\n");mymajor = register_chrdev(0, MYNAME, &test_fops);if (mymajor < 0) {printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "register_chrdev success... mymajor = %d\n", mymajor);return 0;}

    static void __exit chrdev_exit(void) {printk(KERN_INFO "chrdev_exit helloworld exit\n");unregister_chrdev(mymajor, MYNAME);}

    module_init(chrdev_init);module_exit(chrdev_exit);

    MODULE_LICENSE("GPL");MODULE_AUTHOR("aston");MODULE_DESCRIPTION("module test");MODULE_ALIAS("alias xxx");

    转载地址:http://msmr.baihongyu.com/

    你可能感兴趣的文章
    Openlayers高级交互(2/20):清除所有图层的有效方法
    查看>>
    Openlayers高级交互(20/20):超级数据聚合,页面不再混乱
    查看>>
    Openlayers高级交互(3/20):动态添加 layer 到 layerGroup,并动态删除
    查看>>
    Openlayers高级交互(4/20):手绘多边形,导出KML文件,可以自定义name和style
    查看>>
    Openlayers高级交互(5/20):右键点击,获取该点下多个图层的feature信息
    查看>>
    Openlayers高级交互(6/20):绘制某点,判断它是否在一个电子围栏内
    查看>>
    Openlayers高级交互(7/20):点击某点弹出窗口,自动播放视频
    查看>>
    Openlayers高级交互(8/20):选取feature,平移feature
    查看>>
    Openlayers高级交互(9/20):编辑图形(放缩、平移、变形、旋转),停止编辑
    查看>>
    Openlayers:DMS-DD坐标形式互相转换
    查看>>
    openlayers:圆孔相机根据卫星经度、纬度、高度、半径比例推算绘制地面的拍摄的区域
    查看>>
    OpenLDAP(2.4.3x)服务器搭建及配置说明
    查看>>
    OpenLDAP编译安装及配置
    查看>>
    Openmax IL (二)Android多媒体编解码Component
    查看>>
    OpenMCU(一):STM32F407 FreeRTOS移植
    查看>>
    OpenMCU(三):STM32F103 FreeRTOS移植
    查看>>
    OpenMCU(三):STM32F103 FreeRTOS移植
    查看>>
    OpenMCU(二):GD32E23xx FreeRTOS移植
    查看>>
    OpenMCU(五):STM32F103时钟树初始化分析
    查看>>
    OpenMCU(四):STM32F103启动汇编代码分析
    查看>>