本文主要探讨210的led驱动相关知识。
驱动
操作系统驱动硬件的代码,驱动上层是系统调用API,下层是硬件
宏内核:内核整体上为一个过程实现,运行在同一地址空间,相互调用简单高效
微内核:功能为独立过程,过程间通过IPC通信
linux本质上是宏内核兼具微内核模块化特性
静态模块化编译时可裁剪但需重新编译,动态模块化模块自动安装和卸载
字符设备软件是以字节为单位进行操作的,块设备,块设备是以块(多字节)为单位操作,网络设备为网卡驱动
驱动模块命令
lsmod打印内核已经安装模块列表
insmod安装模块
modinfo打印模块信息
rmmod卸载已经安装模块
modprobe加载或卸载模块及依赖模块
加载:modprobe xxx
卸载:modprobe -r xxx
depmod载入模块依赖到modules.dep
常用宏(modinfo查看)
MODULE_LICENSE("GPL") 模块许可包括:GPL、GPL V2、GPL and additional rights、Dial BSD/GPL、Dual MPL/GPL、Proprietary通常为为GPL v2
MODULE_AUTHOR("xxx")添加模块作者信息
MODULE_DESCRIPTION("xxx")添加模块描述信息
MODULE_ALIAS("xxxx")添加模块别名
module_init(moudle_test_init);
module_exit(moudle_test_exit);
module_init宏声明驱动初始化函数,初始化函数和insmod命令绑定
module_exit宏声明驱动卸载函数,卸载函数和rmsmod命令绑定
函数及修饰符
include/linux/init.h
#define __init __section(.init.text) __cold notrace
#define __exit __section(.exit.text) __exitused __cold
__init是宏定义,__init将修饰函数放入.init.text段,内核启动时加载.init.text段模块函数初始化
__exit是宏定义,__exit将修饰函数放入.exit.text段,内核结束时加载.init.text段模块函数释放内存
printk为内核中包含打印级别的功能的函数,/proc/sys/kernel/printk可查看当前系统的打印级别,高于打印级别未打印出的信息用dmesg查看
include/linux/kernel.h
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
file_operations结构体的元素主要是函数指针,设备驱动都有该结构体类型变量,设备驱动向内核注册时提供该结构体类型的变量(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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
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 *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);
int (*aio_fsync) (struct kiocb *, 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 **);
};
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
register_chrdev(linux/fs.h)驱动向内核注册自己的file_operations
major为设备驱动编号,为0则为自动分配,其他为静态注册(/proc/devices可查看驱动编号)
name为驱动名称,fops为驱动申请的file_opearatios
copy_to_user(void __user *to, const void *from, unsigned long n)
将数据从用户空间复制到内核空间
copy_from_user(void __user *to, const void *from, usigned long count);
将数据从内核空间复制到用户空间
虚拟地址映射
驱动操控硬件,操控硬件物理地址在内核中的虚拟地址
静态虚拟地址映射:内核启动时建立静态映射表(硬编码),岁内核销毁
动态虚拟地址映射:动态建立映射、使用、销毁映射,映射是临时的
led驱动静态映射(s5pv210)
arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#define S5PV210_GPJ0CON (S5PV210_GPJ0_BASE + 0x00)
#define S5PV210_GPJ0DAT (S5PV210_GPJ0_BASE + 0x04)
#define S5PV210_GPJ0PUD (S5PV210_GPJ0_BASE + 0x08)
#define S5PV210_GPJ0DRV (S5PV210_GPJ0_BASE + 0x0c)
#define S5PV210_GPJ0CONPDN (S5PV210_GPJ0_BASE + 0x10)
#define S5PV210_GPJ0PUDPDN (S5PV210_GPJ0_BASE + 0x14)
arch/arm/mach-s5pv210/include/mach/regs-gpio.h
define S5PV210_GPJ0_BASE (S5P_VA_GPIO + 0x240)
#define S5P_VA_GPIO S3C_ADDR(0x00500000)
#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))
#define S3C_ADDR_BASE (0xFD000000) //静态映射表基地址
arch/arm/plat-s5p/include/plat/map-s5p.h
led驱动态映射
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
向内核申请映射内存资源,start:物理地址,n大小,name名字
#define ioremap(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE)
返回映射的虚拟地址,cookie:物理地址,szie大小
#define iounmap(cookie) __iounmap(cookie)
解除映射虚拟地址,cookie:物理地址
#define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n))
释放申请,start:物理地址,n大小,name名字
demo:
210已移植uboot(ntfs,tftp),kernel,busynox
ubuntu安装ntfs,tftp
led驱动(静态虚拟地址映射)
led.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define FILE "/dev/led"
char buf[16];
char led_cmd[3][4];
void div_cmd(char *str)
{
unsigned int m = 0;
unsigned int n = 0;
memset(led_cmd,'\0',sizeof(led_cmd));
while(*str != '\0')
{
if(*str != ' ')
{
led_cmd[m][n] = *str;
n++;
}
else
{
led_cmd[m][n] = '\0';
n = 0;
m++;
}
str++;
}
}
void write_buf()
{
int i;
memset(buf,'\0',sizeof(buf));
if(!strcmp(led_cmd[1],"on"))
buf[0] = '0';
if(!strcmp(led_cmd[1],"off"))
buf[0] = '1';
buf[1] = *(led_cmd[2]);
}
int main()
{
int fd = -1;
char cmd[16];
fd = open(FILE, O_RDWR);
if (fd < 0)
{
printf("open %s error.\n", FILE);
return -1;
}
printf("input led cmd :led on|off 1|2|3\n");
while(1)
{
memset(cmd,'\0',sizeof(cmd));
printf(">>>>");
fgets(cmd,16,stdin);
if(cmd[0] == '.' && cmd[1] == 'q')
{
break;
}
div_cmd(cmd);
write_buf();
write(fd,buf,strlen(buf));
}
close(fd);
return 0;
}
led_static_module.c
cat: cat: 没有那个文件或目录
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#define NAME "led"
int major;
char kbuf[16];
#define GPJ0CON S5PV210_GPJ0CON
#define GPJ0DAT S5PV210_GPJ0DAT
#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)
static int led_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_dev_open\n");
return 0;
}
static int led_close(struct inode *inode, struct file *file)
{
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
printk(KERN_INFO "led_dev_close\n");
return 0;
}
static ssize_t led_write(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos)
{
int ret = -1;
int led;
memset(kbuf, 0, sizeof(kbuf));
ret = copy_from_user(kbuf, ubuf, count);
if (ret)
{
printk(KERN_ERR "copy_from_user error\n");
return ret;
}
led = kbuf[1] - '0' + 2;
if (kbuf[0] == '0')
{
rGPJ0DAT &= ~(1<<led);
}
else if (kbuf[0] == '1')
{
rGPJ0DAT |= (1<<led);
}
printk(KERN_INFO "copy_from_user ok %d\n",led);
return 0;
}
ssize_t led_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret = -1;
ret = copy_to_user(buf, kbuf, size);
if (ret)
{
printk(KERN_ERR "copy_to_user error\n");
return ret;
}
printk(KERN_INFO "copy_to_user ok\n");
return 0;
}
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close,
.write = led_write,
.read = led_read,
};
static int __init led_dev_init()
{
rGPJ0CON = 0x11111111;
major = register_chrdev(0, NAME, &led_fops);
printk(KERN_INFO "register success,major:%d\n",major);
return 0;
}
static int __exit led_dev_exit()
{
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
unregister_chrdev(major, NAME);
printk(KERN_INFO "Cancel register\n");
return 0;
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cxb");
MODULE_DESCRIPTION("led static module");
MODULE_ALIAS("led");
Makefile
KERN_DIR = /root/kernel
obj-m += led_static_module.o
all:
make -C $(KERN_DIR) M=`pwd` modules
arm-linux-gcc led.c -o led
cp:
cp *.ko /root/rootfs/driver
cp led /root/rootfs/driver
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf ./led
结果显示:
led驱动(动态虚拟地址映射)
led.c(同上)
led_dynamic_module.c
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#define NAME "led"
int major;
char kbuf[16];
#define GPJ0CON_PA 0xe0200240
#define GPJ0DAT_PA 0xe0200244
unsigned int *rGPJ0CON;
unsigned int *rGPJ0DAT;
static int led_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_dev_open\n");
return 0;
}
static int led_close(struct inode *inode, struct file *file)
{
*rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
printk(KERN_INFO "led_dev_close\n");
return 0;
}
static ssize_t led_write(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos)
{
int ret = -1;
int led;
memset(kbuf, 0, sizeof(kbuf));
ret = copy_from_user(kbuf, ubuf, count);
if (ret)
{
printk(KERN_ERR "copy_from_user error\n");
return ret;
}
led = kbuf[1] - '0' + 2;
if (kbuf[0] == '0')
{
*rGPJ0DAT &= ~(1<<led);
}
else if (kbuf[0] == '1')
{
*rGPJ0DAT |= (1<<led);
}
printk(KERN_INFO "copy_from_user ok %d\n",led);
return 0;
}
ssize_t led_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret = -1;
ret = copy_to_user(buf, kbuf, size);
if (ret)
{
printk(KERN_ERR "copy_to_user error\n");
return ret;
}
printk(KERN_INFO "copy_to_user ok\n");
return 0;
}
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_close,
.write = led_write,
.read = led_read,
};
static int __init led_dev_init()
{
major = register_chrdev(0, NAME, &led_fops);
printk(KERN_INFO "register success,major:%d\n",major);
if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
return -EINVAL;
if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
return -EINVAL;
rGPJ0CON = ioremap(GPJ0CON_PA, 4);
rGPJ0DAT = ioremap(GPJ0DAT_PA, 4);
*rGPJ0CON = 0x11111111;
return 0;
}
static int __exit led_dev_exit()
{
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
iounmap(rGPJ0CON);
iounmap(rGPJ0DAT);
release_mem_region(GPJ0CON_PA, 4);
release_mem_region(GPJ0DAT_PA, 4);
unregister_chrdev(major, NAME);
printk(KERN_INFO "Cancel register\n");
return 0;
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cxb");
MODULE_DESCRIPTION("led static module");
MODULE_ALIAS("led");
Makefile
KERN_DIR = /root/kernel
obj-m += led_dynamic_module.o
all:
make -C $(KERN_DIR) M=`pwd` modules
arm-linux-gcc led.c -o led
cp:
cp *.ko /root/rootfs/driver
cp led /root/rootfs/driver
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf ./led
结果显示: