本文共 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】常用的模块操作命令
【3】模块的安装(1) 使用 lsmod 查看安装前后的模块记录(2) insmod 与 module_init 宏模块源代码中用 module_init 宏声明了一个函数,本例中即指定 chrdev_init 函数和 insmod 命令绑定insmod module_test.ko 时会调用 chrdev_init 函数在 ubuntu 中使用 dmesg 查看打印信息
【4】模块的版本信息
【5】模块卸载
【6】模块中常用宏
【7】函数修饰符
【8】printk 函数详解
【9】驱动模块中的头文件驱动源代码中的头文件是内核源代码目录下的 include 目录下的头文件
【10】驱动编译的 Makefile 分析
pwd modules:编译模块【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)
参数:
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/