一、Linux驱动
- Linux驱动代码结构:
//1、头文件
#include <linux/init.h>
#include <linux/module.h>
//2、驱动入口函数的声明,在内核加载驱动时,执行哪个函数;在内核卸载驱动时,执行哪个函数
//声明:加载时的入口声明
module_init(hello_init);
//声明:卸载时的入口声明
module_exit(hello_exit);
//3、加载函数、卸载函数的实现
//加载函数的实现:当内核加载驱动(内核执行这个驱动时,就会调用的函数)
static int __init hello_init(void)
{
return 0;
}
//卸载函数的实现:当内核卸载驱动(内核删除这个驱动时,就会调用的函数)
static void __exit hello_exit(void)
{
}
//4、协议选择GPL
MODULE_LICENSE("GPL");
- 利用Makefile 编译
KERNLE_PATH=/home/ubuntu/code/kernel/linux-3.14
APP=led_test
obj-m=led_drv.o
all:
make modules -C $(KERNLE_PATH) M=$(shell pwd)
/home/ubuntu/tools/gcc-4.6.4/bin/arm-none-linux-gnueabi-gcc $(APP).c -o $(APP)
install:
cp *.ko $(APP) /nfsdir/rootfs
- Linux内核如何使用模块驱动
加载:
insmod 驱动程序路径.ko
查看:lsmod
//查看当前已经加载的驱动模块
卸载:rmmod 驱动程序名
二、字符设备驱动
(一) 字符设备驱动的要素:
- 必须要有一个设备号,用于在内核中的众多设备驱动进行区分
- 必须要有一个设备文件,用户必须知道设备驱动对应的设备节点(设备文件)
- 驱动对设备的操作,与应用程序中的系统调用关联,其实就是文件操作
应用空间调用open、read、write、ioctl函数,实际在驱动中调用对应的xxx_open、xxx_read等函数
(二)实现字符设备框架
- 创建设备号
int register_chrdev(unsigned int major,const char *name,const struct file_operations *fops)
- 创建一个设备节点(设备文件)
a、手动创建
mknod 设备节点名 设备类型 主设备号 次设备号
b、自动创建(通过udev/mdev机制)
//创建设备节点
struct class * class_create(owner,name);
//创建设备文件
struct device *device_create(struct class *class, struct device *parent,dev_t devt,void *drvdata, const char *fmt, ...);
- 在驱动中实现文件io接口功能(与应用关联)
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 (*iterate) (struct file *, struct dir_context *);
unsigned int (*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 *);
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 (*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 **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
int (*show_fdinfo)(struct seq_file *m, struct file *f);
};//函数指针的集合,
每个函数指针赋值为函数地址,就代表当应用程序调用对应的文件io函数时,驱动就执行函数指针赋值的对应函数
- 应用程序操作驱动,应用程序调用文件io函数去控制驱动
- 应用和驱动传递数据
read、write
copy_to_user
copy_from_user
- 驱动控制硬件,控制外设,其实就是控制地址,通过地址往寄存器写入、读出控制
实现一个简单的Led 驱动:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define GPX1CON 0x11000c20
#define GPX1DAT 0x11000c24
//设计结构体类型来表示整个驱动对象(描述设备驱动信息)
struct led_drv
{
unsigned int major;//主设备号
dev_t devno;//设备号
struct class * cls;//设备文件信息结构体
struct device* dev;//设备文件信息
unsigned int * gpx1con;//设备驱动映射的地址
unsigned int * gpx1dat;
};
//定义全局设备对象(存储信息)
struct led_drv led;
//驱动与应用程序函数关联
int led_open(struct inode * inode, struct file * file)
{
printk("led_open\n");
return 0;
}
int led_close(struct inode * inode, struct file * file)
{
printk("led_close\n");
return 0;
}
ssize_t led_write(struct file * file, const char __user * buf, size_t size, loff_t * ops)
{
int num = 0;
void *p = copy_from_user(&num, buf, size);
printk("addr is = %p\n", p);
if (num == 1)
{
*(led.gpx1dat) |= 1;
}
else
{
*(led.gpx1dat) &= ~1;
}
return 0;
}
const struct file_operations fops = {
.open = led_open,
.release = led_close,
.write = led_write,
};
static int __init led_init(void)
{
//申请设备号
led.major = 250;
led.devno = led.major << 20 | 0;
int ret = -1;
ret = register_chrdev(led.major, "led drv", &fops);
if (ret < 0)
{
printk("register devno error\n");
goto err_1;
}
//创建设备文件
led.cls = class_create(THIS_MODULE, "led cls");
if (IS_ERR(led.cls))
{
printk("class create error\n");
goto err_2;
}
led.dev = device_create(led.cls, NULL, led.devno, NULL, "led");
if (IS_ERR(led.dev))
{
printk("device create erroe\n");
goto err_3;
}
//硬件初始化
led.gpx1con = ioremap(GPX1CON, 4);
if (led.gpx1con == NULL)
{
printk(
"gpx1con ioremap error\n");
goto err_4;
}
led.gpx1dat = ioremap(GPX1DAT, 4);
if (led.gpx1dat == NULL)
{
printk(
"gpx1dat ioremap error\n");
goto err_5;
}
//硬件寄存器初始化
*(led.gpx1con) = *(led.gpx1con) & ~(0xf) | 0x1;
*(led.gpx1dat) |= 1;
return 0;
err_5:
iounmap(led.gpx1con);
err_4:
device_destroy(led.cls, led.devno);
err_3:
class_destroy(led.cls);
err_2:
unregister_chrdev(led.major, "led drv");
err_1:
return -1;
}
static void __exit led_exit(void)
{
//卸载驱动内容
//1、释放映射地址
iounmap(led.gpx1con);
iounmap(led.gpx1dat);
//2、释放设备文件
device_destroy(led.cls,led.devno);
//3、释放设备文件结构体
class_destroy(led.cls);
//4、释放设备号
unregister_chrdev(led.major,"led drv");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
应用程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int fd = open("/dev/led",O_WRONLY);
int i = 0;
int a;
while(i < 30)
{
a = 1;
write(fd,&a,4);
sleep(1);
a = 0;
write(fd,&a,4);
sleep(1);
i++;
}
close(fd);
return 0;
}