应用程序调用驱动
在驱动学习1里学习了驱动模块的基本框架。但没有涉及到应用程序调用驱动以及驱动如何控制硬件。下面就是它两者的使用。
I/O内存访问函数
- 读操作函数
- 读字节。
u8 readb(const volatile void __iomem *addr)
- 读16位数。
u16 readw(const volatile void __iomem *addr)
- 读32位数。
u32 readl(const volatile void __iomem *addr)
- 读字节。
- 写操作函数
- 写字节。
void writeb(u8 value,volatile void __iomem *addr)
- 写16位数。
void writew(u16 value,volatile void __iomem *addr)
- 写32位数。
void writel(u32 value,volatile void __iomem *addr)
- 写字节。
物理地址与虚拟地址转换函数
函数引用头文件arch/arm/include/asm/io.h
。
- __iomem *ioremap(phys_addr,size)宏。
- 获取指定物理地址对应的虚拟地址。
- void iounmap(volatile void __iomem *addr)
- 取消映射的虚拟空间的首地址。
驱动模块的设备号分配
设备号是一个32位的无符号数,由主设备号
(占20bit)和从设备号
(占12bit)组成.
驱动的设备号分配可以分为静态分配
和动态分配
两种方式。在使用静态分配时,应该先用cat /proc/devices
命令查看已使用的设备号,然后指定一个没有使用的设备号。(具体可以参看文档Documentation/devices.txt)。动态分配是使用分配函数让系统分配设备号给驱动使用。相关函数如下:
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)
- 用于动态申请设备号
- dev: 保存申请到的设备号。
- baseminor: 此设备号起始地址。一般为0.
- count: 要申请的数量。
- name: 设备名称。
void unregister_chrdev_region(dev_t from,unsigned count)
- 用于释放申请的设备号。
- from: 要释放的设备号。
- count: 要释放的数量。
MAJOR(dev)
- 获取设备号的主设备号
MINOR
- 获取设备号的从设备号。
MKDEV(ma,mi)
- 将主设备号和从设备号组合成设备号。
字符设备注册、注销
字符设备的结构体为struct cdev
.
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
}
void cdev_init(struct cdev *, const struct file_operations *)
- 字符设备初始化函数。
int cdev_add(struct cdev *, dev_t, unsigned)
- 向linux系统中添加此字符设备。
void cdev_del(struct cdev *)
- 卸载字符设备。
设备节点的自动创建
创建类
struct class *class_create(owner, name)
- 创建类。
- owner: 一般为 THIS_MODULE.
- name: 类的名称。
void class_destroy(struct class *cls)
- 删除类。
创建设备
struct device *device_create(struct class *cls,struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...)
- 创建设备。
- cls: 指定设备创建在哪个类下面。
- parent: 一般为NULL。
- drvdata: 一般为NULL。
- fmt: 设备名称。如fmt=“charDev”,那么就会生成在/dev/charDev的设备文件。
void device_destroy(struct *class,dev_t devt)
- class: 要删除设备的类。
- devt: 设备号。
应用程序调用驱动模块
驱动程序如下
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <asm/uaccess.h>
//创建一个自己的设备类
struct led_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
};
struct led_dev myled;
static char readbuff[100];
static char writebuff[100];
static char kerneldata[] = {"hello word"};
static int char_open(struct inode *inode,struct file *filp)
{
filp->private_data = &myled;
return 0;
}
static ssize_t char_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{
int revalue = 0;
memcpy(readbuff,kerneldata,sizeof(kerneldata));
revalue = copy_to_user(buf,readbuff,cnt);
if(revalue == 0){
printk("send data ok.\n");
}
else{
printk("send data error.\n");
}
return 0;
}
static ssize_t char_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
int revalue = 0;
revalue = copy_from_user(writebuff,buf,cnt);
if(revalue == 0){
printk("rev data:%s.\n",writebuff);
}
else{
printk("rev data error.\n");
}
return 0;
}
static int char_release(struct inode *inode,struct file *filp)
{
return 0;
}
//对操作函数进行映射
static struct file_operations test_fops = {
.owner = THIS_MODULE,
.open = char_open,
.read = char_read,
.write = char_write,
.release = char_release
};
static int __init char_init(void)
{
//动态分配设备号
alloc_chrdev_region(&myled.devid,0,1,"chartest2");
myled.major = MAJOR(myled.devid);
myled.minor = MINOR(myled.devid);
printk("init.major:%d,minor:%d.\n",myled.major,myled.minor);
//设备初始化,绑定文件操作函数
myled.cdev.owner = THIS_MODULE;
cdev_init(&myled.cdev,&test_fops);
//绑定设备号
cdev_add(&myled.cdev,myled.devid,1);
//创建类
myled.class = class_create(THIS_MODULE,"chartest2");
//创建设备
myled.device = device_create(myled.class,NULL,myled.devid,NULL,"chartest2");
return 0;
}
static void __exit char_exit(void)
{
printk("注销\n");
cdev_del(&myled.cdev);
unregister_chrdev_region(myled.devid,1);
device_destroy(myled.class,myled.devid);
class_destroy(myled.class);
}
module_init(char_init);
module_exit(char_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("guoweilkd");
MODULE_DESCRIPTION("driver test");
应用测试程序如下。命令行中运行./app_test.o /dev/chartest2 2
便可测试。
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdata[] = {"usr data!"};
int main(int argc,char *argv[])
{
int fd,revalue;
char *filename;
char readbuff[100],writebuff[100];
if(argc != 3){
printf("input error.\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd < 0){
printf("can't open file %s\r\n",filename);
}
if(atoi(argv[2]) == 1){
revalue = read(fd,readbuff,50);
if(revalue < 0){
printf("read file %s failed.\n",filename);
}
else{
printf("read data:%s\n",readbuff);
}
}
if(atoi(argv[2]) == 2){
memcpy(writebuff,usrdata,sizeof(usrdata));
revalue = write(fd,writebuff,50);
if(revalue < 0){
printf("write file %s failed.\n",filename);
}
}
revalue = close(fd);
if(revalue < 0){
printf("can't close file %s. \n",filename);
return -1;
}
return 0;
}
关于技术交流
此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。