文章目录
1.内核模块
1.模块的基本使用
1.驱动模块的入口和出口函数
static int __init 入口函数名(void)
{
//当驱动加载时 入口函数会调用
}
static void __exit 出口函数名(void)
{
//当驱动卸载时,出口函数会调用
}
module_init(入口函数名);
module_exit(出口函数名);
2.驱动加载卸载命令
insmod xxxx.ko 加载驱动模块
lsmod 查看已加载的模块
rmmod xxxx 卸载驱动模块片
2.字符设备驱动
1.字符设备驱动开发步骤
模块加载
0.封装
struct s5pv210_led_dev{
unsigned int major; //主设备号
struct device * led_dev; //设备
struct class * led_class; //类
dev_t dev_no; //静态注册使用
volatile unsigned long * GPC0_CON;
volatile unsigned long * GPC0_DAT;
};
1. 创建全局的结构体对象,并分配空间
kzalloc(size_t size, gfp_t flags)
2. 申请/注册设备号
register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
unregister_chrdev(unsigned int major, const char * name)
3. 创建类
class_create(owner, name)
class_destroy(struct class * cls)
4. 创建设备节点
device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt, ...)
device_destroy(struct class * class, dev_t devt)
5. 硬件初始化
ioremap(cookie, size)
iounmap();
6. 实现file_operations里面对应的函数
copy_from_user(void * to, const void __user * from, unsigned long n)
copy_to_user(void __user * to, const void * from, unsigned long n)
模块卸载
1.取消内存映射
iounmap();
2.注销设备节点
device_destroy(struct class * class, dev_t devt)
3.注销类
class_destroy(struct class * cls)
4.注销设备号
unregister_chrdev(unsigned int major, const char * name)
5.释放对象空间
kfree(void *dev)
2.设备号注册
静态注册
1.获取设备号
dev_t dev_num = MKDEV(250, 0); 250为主设备号,找一个未被占用的
设备号总共32位,主设备号占20位,次设备号占8位,类型为dev_t
设备号= MKDEV(主设备号, 次设备号);
主设备号= MAJOR(设备号)
次设备号= MKDEV(设备号)
2.注册设备号
register_chrdev_region(dev_num, 1, "drv_led");
参数1:设备号(主设备号+次设备号)
参数2:需要注册的个数
参数3:描述字符串,自定义
返回值:0表示成功,负数表示失败
动态注册
1.定义设备号变量
dev_t dev_num;
2.分配设备号
alloc_chrdev_region(&dev_num, 0, 1, "drv_led");
参数1:设备号的地址
参数2:次设备号的起始
参数3:分配的设备号的个数
参数4:描述字符串,自定义
返回值:0表示成功,负数表示失败
3.创建cdev对象
struct cdev *cdev = cdev_alloc();
4.初始化cdev对象
cdev_init(cdev, &fops);
参数1:分配好的cdev
参数2:file_operation
5.把cdev对象注册到内核
cdev_add(cdev, dev_num, 1);
参数1: 分配好的cdev
参数2: 设备号
参数3: 注册到内核的个数
3.创建类、设备节点、内存映射
1.创建类
struct class * led_class = class_create(THIS_MODULE, "led_class");
参数1: THIS_MODULE,表示当前模块
参数2: /sys/class/下创建的目录名称,自定义
2.创建设备节点
struct device * led_dev = device_create(led_class, NULL, MKDEV(major, 0), NULL, "drv_led");
手动创建设备节点的方法: mknod /dev/drv_led c 250 0
参数1: 创建的类
参数2: 设备的父类
参数3: 设备号,包括了主设备号和次设备号,用MKDEV来合成
参数4: 设备文件的私有数据
参数5: 设备节点名称 /dev/xxx
3.内存映射
ioremap() //获取指定物理地址间空对应的虚拟地址空间/include/asm/io.h 文件中
static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
//offset:要映射给的物理起始地址。
//size:要映射的内存空间大小。
//返回值:返回虚拟地址
iounmap()//卸载驱动的时候需要使用iounmap 函数释放掉 ioremap 函数所做的映
iounmap()//取消映射
device_destroy()//注销设备
class_destroy()//注销类
kfree()释放空间
4.实现fops(open,relase,write,read)
1.函数原型
ssize_t led_write(
struct file * filp, //内核空间的buff地址
const char __user * buff,//用户空间的buff地址
size_t count, //传递的长度
loff_t * offset) //相对于文件首地址的偏移
//返回0成功
2.函数的使用
ssize_t led_write(struct file * filp, const char __user * buff, size_t count, loff_t * offset)
{
ssize_t ret;
int led_cmd;
ret = copy_from_user(&led_cmd, buff, count); //从用户空间拷贝到内核空间
//copy_to_user(void __user * to, const void * from, unsigned long n)从内核空间到用户空间的拷贝
if(led_cmd == 0)
//操作
else
//操作
}
硬件控制
1.方式1
*GPC0_DAT &= ~(0x03 << 3);
2.方式2
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), 0);
gpio_free(S5PV210_GPC0(3));
}
3.方式3
value = __raw_readl(s5pv210_dev->GPC0_DAT);
value &= ~(0x03 << 3);
__raw_writel(value, s5pv210_dev->GPC0_DAT);
5.GPIP函数,内存读取函数
1.请求GPIO权限
gpio_request(unsigned gpio, const char * label)
2.释放GPIO的操作权限
gpio_free(unsigned gpio)
3.设置GPIO输出低或者高电平
gpio_direction_output(unsigned gpio, int value)
4.设置GPIO输入
gpio_direction_input(unsigned gpio)
5.获取GPIO的值
gpio_get_value()
6.设置GPIO引脚的值
gpio_set_value()
7.设置为内部上拉
gpio_set_pull_up(struct wm8350 * wm8350, int gpio, int up)
8.设置为内部下拉
gpio_set_pull_down(struct wm8350 * wm8350, int gpio, int down)
9.把GPIO引脚设置成特定功能
s3c_gpio_cfgpin(S5PV210_MP01(1), S3C_GPIO_SFN(2));
内存读写函数
u32 readl(const volatile void __iomem *addr)
void writel(u32 value, volatile void __iomem *addr)
6. Linux内核中file cdev inode之间的关系
1.struct file结构体:描述进程打开(open)一个文件的信息:文件名,标志(可读写), 文件偏移
struct file {
struct path f_path;
const struct file_operations *f_op;
unsigned int f_flags;
loff_t f_pos;
void *private_data; //私有数据,万能指针
}
2.struct inode结构体:描述文件系统中某个文件的属性(文件权限,类型,gid,uid, 修改时间)
struct inode {
umode_t i_mode;
uid_t i_uid;
gid_t i_gid;
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct cdev *i_cdev;
}
3.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;
};
struct task_struct
|
struct files_struct *files;//描述进程打开一个文件的信息:文件名,标志(可读写), 文件偏移
|
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
|
struct path f_path;
|
struct dentry *dentry;
|
struct inode *d_inode;//文件的属性
|
struct cdev *i_cdev;//字符设备对象信息
7.杂项设备驱动的简单写法
1.杂项设备要赋值的结构体
struct miscdevice {
int minor; // 次设备号
const char *name; // 设备节点
const struct file_operations *fops; //file_operations
//以下可不写
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
2.赋值
struct miscdevice misc_led = {
.minor = 199,
.name = "drv_led",
.fops = &fops,
};
3.申请设备
misc_register(&misc_led);
4.硬件初始化
....
return 0;
5.注销设备
misc_deregister(&misc_led);
3.高级IO操作
1.使用ioctl替换write
1.函数
long led_ioctl(
struct file * filp, //打开的文件
unsigned int cmd, //4字节的数据来生成命令
00- 07位:命令码
08-15位:全局唯一的魔幻数
16-29位:如果命令带参数 表示参数所占内存的大小
30-31位:00不带参数,10命令从驱动读参数,01写方向,11读写双向
unsigned long args) //传递的参数
2.使用
long led_ioctl(struct file * filp, unsigned int cmd, unsigned long args)
{
unsigned int value;
switch(cmd)
{
case ALL_LED_ON:
//操作1
break;
case ALL_LED_OFF:
//操作2
break;
case SEL_LED_ON:
if(args == SEL_LED1)
//操作3
else if(args == SEL_LED2)
//操作4
break;
default:
break;
}
}
3.内核定义了如下的宏,帮我们生成命令:
#define ALL_LED_ON _IO('A', 0x01)
#define ALL_LED_OFF _IO('A', 0x02)
#define SEL_LED_ON _IOW('S', 0x03, int)
#define SEL_LED_OFF _IOW('S', 0x04, int)
#define SEL_LED1 0x01
#define SEL_LED2 0x02
2.io非阻塞方式
0.用到的函数
init_waitqueue_head(&wait)//初始化
wake_up_interruptible(&wait);//唤醒
wait_event_interruptible(wait, havedata);//检查是真唤醒还是假唤醒
wait_queue_head_t wait;//定义一个等待队列头
int havedata;//定义一个条件
1.申请杂项设备号
...
2.申请中断
...
3.初始化队列头
init_waitqueue_head(&wait);//初始化队列头
havedata = 0;
4.中断函数
irqreturn_t button_irq_handler(int irq_no, void * data)
{
//获取按键类型和值放入void *data中
...
havedata = 1;
//
wake_up_interruptible(&wait);
//返回中断已处理
return IRQ_HANDLED;
}
5.非阻塞方式
ssize_t button_read(struct file *filp, char __user *buff, size_t count, loff_t *offset)
{
//非阻塞方式O_NONBLOCK不阻塞 havedata = 0
if((filp->f_flags & O_NONBLOCK) && (!havedata))
return -EAGAIN;
//如果没有按键按下,则返回的是-EAGALN, 但没有进入执行下面的语句
//休眠让出CPU但并不返回,havedata = 1不体眠 havedata = 0休眠
//若检查到havedata为真 则唤醒,否则继续休眠
wait_event_interruptible(wait, havedata);
//唤醒
copy_to_user(buff, &report_data, count);
havedata = 0;
return 0;
}
3.poll多路复用
1.在上一部分基础增加
const struct file_operations fops ={
.poll = button_poll,//增加函数
};
unsigned int button_poll(struct file *filp, struct poll_table_struct *poll_table)
{
unsigned int ret = 0;
//将等待队列头注册到轮询表
poll_wait(filp, &wait, poll_table);
if(button_dev->havedata)
ret |= POLLIN;
return ret;
}
struct pollfd {
int fd; //要监控的fd
short events; //要监控的事件
short revents; //实际发生的事件
};
-----------------未写完此部分------------------
4.异步通知
一旦硬件设备准备好就主动通知应用程序而不需要应用主动查询状态
1、应用程序中:使用signal()注册信号处理函数;
2、信号谁来发:驱动发;
3、怎么发:通过kill_fasync()函数来发送;
4、发给谁:发给应用程序,但应用程序必须通过fcntl()事先告诉驱动PID;
F_GETFD :读取文件描述词标志。
F_SETFD :设置文件描述词标志。
F_GETFL :读取文件状态标志。
F_SETFL :设置文件状态标志。
//应用程序
1.通过F_SETOWN控制指令设置设备文件的拥有者为本进程,这样从设备驱动中发出的信号才能被本进程收到。
fcntl(fd, F_SETOWN, getpid());
2.通过F_SETFLIO控制命令设置设备文件支持FASYNC,即异步通知模式。这时系统就会自动调用驱动程序的fasync方法。
flags = fcntl(fd, F_GETFL);//原来支持的
fcntl(fd, F_SETFL, flags|FASYNC);
3.通过signal()链接信号和信号处理函数。
signal(SIGIO, key_signal_func);
//驱动程序
struct fasync_struct *fasync;
kill_fasync(&fasync, SIGIO, POLL_IN);//fasync包含进程ID,SIGIO是信号,
int button_fasync(int fd, struct file * filp, int on)
{
//将该设备登记到fasync_queue队列中去
return fasync_helper(fd, filp, on, &fasync);
}
阻塞:一直等待设备可访问
非阻塞:轮询设备是否可访问
异步通知:设备通知应用可访问
4.中断和时间管理
1.中断使用
1.申请中断
request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key_up_irq", NULL);
//参数1:中断号
//参数2:中断处理函数
//参数3:触发方式
//参数4:中断源的描述
//参数5:传递给中断处理函数的参数
//返回值: 0--成功 负数--失败to err_request_irq;
//free_irq(IRQ_EINT(0), NULL);注销中断
2.中断处理函数
irqreturn_t button_irq_handler(int no, void * data)
{
//中断处理操作
//...
//返回中断已处理标志
return IRQ_HANDLED;
}
2.中断下半部
1.申请中断
request_irq(irq, button_irq_handler,flags,name, NULL);
//参数1:中断号
//参数2:中断处理函数
//参数3:触发方式
//参数4:中断源的描述
//参数5:传递给中断处理函数的参数
//返回值: 0--成功 负数--失败to err_request_irq;
//free_irq(irq, NULL);注销中断
2.初始化tasklet
struct tasklet_struct tasklet;
tasklet_init(&tasklet, button_irq_tasklet_func, 0);
//参数1:struct tasklet_struct
//参数2:下半部的处理函数
//参数3:传递给下半部处理函数的参数
3.初始化work
struct work_struct work;
INIT_WORK(&button_dev->work, button_irq_work_func);
//参数1:待初始化的work
//参数2:下半部的处理函数
4.中断处理函数
irqreturn_t button_irq_handler(int irq_no, void * data)
{
struct button_attribute * key = (struct button_attribute *)data;
button_dev->report_data.code_value = key->code_value;
button_dev->report_data.button_press = !(gpio_get_value(key->gpio));
//调度tasklet下半部执行
tasklet_schedule(&tasklet);
//调度work下半部执行
schedule_work(&work);
return IRQ_HANDLED;
}
5.下半部处理函数work和tasklet
void button_irq_work_func(struct work_struct *work)
{
printk("%s\n",__FUNCTION__);
}
void button_irq_tasklet_func(unsigned long data)
{
printk("%s\n",__FUNCTION__);
}
3.定时器
1.初始化定时器
struct timer_list button_timer;
init_timer(&button_timer);
2.绑字定时操作函数
button_timer.function = button_irq_timer;
3.添加定时器
add_timer(&button_timer);
4.中断函数
irqreturn_t button_irq_handler(int irq_no, void * data)
{
mod_timer(&button_timer, jiffies + HZ/100);
return IRQ_HANDLED;
}
5.定时结束后执行
void button_irq_timer(unsigned long data)
{
//
}
6.注销定时器
del_timer(&button_timer);
5.同步和互斥
1.原子变量
1.设置变量
atomic_t open_flag = ATOMIC_INIT(1);
2.原子变量加/减1之后是否为0
atomic_dec_and_test(&open_flag))
atomic_inc_and_test(&open_flag))
3.原子变量加/减1
atomic_dec(&open_flag))
atomic_inc(&open_flag))
2.自旋锁
自旋锁最多只能被一个可执行线程持有。
如果一个执行线程试图获得一个被已经持有(争用)的自旋锁,那么该线程就会一直进行忙循环-旋转-等待锁
要是锁未被争用,请求锁的执行线程就可以立即得到它,继续执行。
1.定义锁变量
struct spinlock lock;
2.初始化锁
spin_lock_init(&lock);
3.获取锁,若获取失败则自旋等待
spin_lock();
4.尝试获取锁,若没有获取到立即返回非0值
spin_try_lock();
5.释放锁
spin_unlock();
3.读写锁
//定义锁
rwlock_t lock;
rwlock_init(&lock);
//获取写锁,其它进程不能获取读锁或写锁
write_lock_irqsave(&lock, flags);
i++;
write_unlock_irqrestore(&lock, flags);
//获取读锁,其它进程可以获取读锁不能获取写锁
read_lock_irqsave(&lock, flags);
v = i;
read_unlock_irqrestore(&lock, flags);
4.顺序锁
读读不阻塞,读写不阻簺,写写阻塞
seqlock_t lock;
--------未完成-----------
5.信号量
信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
信号量的值为大于0,说明它空闲。所测试的线程可以锁定而使用它。
信号量的值为0,说明它被占用,但没有进程等待这个保护的资源
信号量的值小于0,资源不可用,并至少有一个进程等待资源
1.信号量的定义:
struct semaphore {
spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
2.定义初始化信号量
struct semaphore sem;
sema_init(&sem, 1);//1个进程同时获取信号量
3.信号量的原子操作
down(&sem); //获取信号量(减1操作)
down_interruptible(&em);
down_trylock(&sem); //试图获取信号量,获取失败返回非0
up(&sem); //释放信号量(加1操作)如果在该信号量的等待队列中有进程在等待该资源,则唤醒一个进程,否则释放一个资源
6.互斥量
1.定义互斥锁
struct mutex lock;
2.初始化
mutex_init(&lock);
3.上锁
mutex_lock(&lock);
//mutex_trylock(&lock);
4.解锁
mutex_unlock(&lock);
6.内存和DMA(未完成此部分)
1.mmap函数
addr = (char *)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
//应用程序:
//参数1: 指定要映射的虚拟地址的起始,如果传NULL表示由系统来指定
//参数2: 映射大小
//参数3: 访问权限
//参数4: 表示该映射的内存是共享还是私有
//参数5: 指定这个映射所对应的文件描述符
//参数6: 映射相对于起始地址的偏移
//驱动程序,实现mmap函数
//虚拟地址转化成物理地址
addr = (unsigned long)__virt_to_phys(button_dev->vma_addr);
// 2.实现内存映射
//参数1:虚拟地址区域的描述对象
//参数2:要映射的虚拟地址的起始
//参数3:映射的页帧号
//参数4:映射的大小
//参数5:访问权限
ret = remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, PAGE_SIZE, vma->vm_page_prot);
7.代码整理
1.字符设备驱动
1.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>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/slab.h>
struct s5pv210_led
{
dev_t devno;
struct cdev *cdev;
struct class *clz;
struct device *dev;
volatile unsigned long * GPC0_CON;
volatile unsigned long * GPC0_DAT;
};
struct s5pv210_led *led_dev;
ssize_t led_drv_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
printk("%s\n", __FUNCTION__);
return 0;
}
ssize_t led_drv_write(struct file *filp, const char __user *buff, size_t size, loff_t *flags)
{
ssize_t ret;
int led_cmd;
printk("%s\n", __FUNCTION__);
ret = copy_from_user(&led_cmd, buff, size);
if(ret > 0)
{
printk("copy_from_user error\n");
return ret;
}
if(led_cmd == 1 || led_cmd == 0)
{
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), led_cmd);
gpio_free(S5PV210_GPC0(3));
}
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), led_cmd);
gpio_free(S5PV210_GPC0(4));
}
}
return 0;
}
int led_drv_open(struct inode * inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int led_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
struct file_operations fops = {
.open = led_drv_open,
.release = led_drv_release,
.read = led_drv_read,
.write = led_drv_write,
};
static int __init led_drv_init(void)
{
//1.创建全局空间
int ret;
printk("%s\n", __FUNCTION__);
led_dev = kzalloc(sizeof(struct s5pv210_led), GFP_KERNEL);
if(IS_ERR(led_dev))
{
printk("kzalloc error\n");
return PTR_ERR(led_dev);
}
//2.申请设备号
ret = alloc_chrdev_region(&led_dev->devno, 5, 1, "led_drv");
if(ret < 0)
{
printk("alloc_chrdev_region error\n");
goto err_kfree;
}
//3.申请注册cdev
led_dev->cdev = cdev_alloc();
if(IS_ERR(led_dev->cdev))
{
printk("cdev_alloc error\n");
ret = PTR_ERR(led_dev->cdev);
goto err_chrdev_register;
}
cdev_init(led_dev->cdev, &fops);
cdev_add(led_dev->cdev, led_dev->devno, 1);
//4.创建类
led_dev->clz = class_create(THIS_MODULE, "led_class");
if(IS_ERR(led_dev->clz))
{
printk("class_create error\n");
ret = PTR_ERR(led_dev->clz);
goto err_cdev_del;
}
//5.创建设备
led_dev->dev = device_create(led_dev->clz, NULL, led_dev->devno, NULL, "led_device");
if(IS_ERR(led_dev->dev))
{
printk("device_create error\n");
ret = PTR_ERR(led_dev->dev);
goto err_class_destroy;
}
//6.硬件初始化
//请求GPIO操作权限
if(0 ==gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
//让GPIO输出为低电平
gpio_direction_output(S5PV210_GPC0(3), 0);
//释放操作权限
gpio_free(S5PV210_GPC0(3));
}
if(0 ==gpio_request(S5PV210_GPC0(4), "gpc0_3"))
{
//让GPIO输出为低电平
gpio_direction_output(S5PV210_GPC0(4), 0);
//释放操作权限
gpio_free(S5PV210_GPC0(4));
}
return 0;
err_class_destroy:
class_destroy(led_dev->clz);
err_cdev_del:
cdev_del(led_dev->cdev);
err_chrdev_register:
unregister_chrdev_region(led_dev->devno, 1);
err_kfree:
kfree(led_dev);
return ret;
}
static void __exit led_drv_exit(void)
{
printk("%s\n", __FUNCTION__);
iounmap(led_dev->GPC0_CON);
device_destroy(led_dev->clz, led_dev->devno);
class_destroy(led_dev->clz);
cdev_del(led_dev->cdev);
unregister_chrdev_region(led_dev->devno, 1);
kfree(led_dev);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int fd;
fd = open("dev/led_device", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
int on;
while(1)
{
printf("请输入命令:1开灯 0关灯\n");
scanf("%d", &on);
write(fd, &on, sizeof(on));
}
return 0;
}
2.杂项设备驱动
//杂项驱动设备写法
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <asm-generic/uaccess.h>
//1.1
ssize_t led_misc_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
printk("%s\n", __FUNCTION__);
return 0;
}
ssize_t led_misc_write(struct file *filp, const char __user *buff, size_t size, loff_t *flags)
{
ssize_t ret;
int led_cmd;
printk("%s\n", __FUNCTION__);
ret = copy_from_user(&led_cmd, buff, size);
if(ret > 0)
{
printk("copy_from_user error\n");
return ret;
}
if(led_cmd == 1 || led_cmd == 0)
{
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), led_cmd);
gpio_free(S5PV210_GPC0(3));
}
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), led_cmd);
gpio_free(S5PV210_GPC0(4));
}
}
return 0;
}
int led_misc_open(struct inode * inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int led_misc_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
//1.1
struct file_operations fops = {
.open = led_misc_open,
.release = led_misc_release,
.read = led_misc_read,
.write = led_misc_write,
};
//1.赋值
struct miscdevice led_misc_dev = {
.minor = 100,
.name = "led_misc",
.fops = &fops,
};
static int __init led_misc_drv_init(void)
{
//2.申请杂项设备
int ret;
printk("%s\n", __FUNCTION__);
ret = misc_register(&led_misc_dev);
if(ret < 0)
{
printk("misc_register error\n");
return 0;
}
//3.硬件初始化
//请求GPIO操作权限
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), 0);
gpio_free(S5PV210_GPC0(3));
}
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), 0);
gpio_free(S5PV210_GPC0(4));
}
return 0;
}
static void __exit led_misc_drv_exit(void)
{
printk("%s\n", __FUNCTION__);
misc_deregister(&led_misc_dev);
}
module_init(led_misc_drv_init);
module_exit(led_misc_drv_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
int fd;
fd = open("dev/led_misc", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
int on;
while(1)
{
printf("请输入命令:1开灯 0关灯\n");
scanf("%d", &on);
write(fd, &on, sizeof(on));
}
return 0;
}
2.高级IO操作
1.ioctl
//杂项驱动设备写法
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <asm-generic/uaccess.h>
#include <asm-generic/ioctl.h>
/*
_IO: 定义不带参数的 ioctl 命令
_IOW: 定义带写参数的 ioctl 命令(copy_from_user)
_IOR: 定义带读参数的ioctl命令(copy_to_user)
_IOWR: 定义带读写参数的 ioctl 命令
*/
#define LED1 _IOW('S', 0x01, int)
#define LED2 _IOW('S', 0x02, int)
#define LED_ALL _IOW('S', 0x03, int)
#define LED_ON 0x01
#define LED_OFF 0x02
//1.1
ssize_t led_misc_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
printk("%s\n", __FUNCTION__);
return 0;
}
ssize_t led_misc_write(struct file *filp, const char __user *buff, size_t size, loff_t *flags)
{
ssize_t ret;
int led_cmd;
printk("%s\n", __FUNCTION__);
ret = copy_from_user(&led_cmd, buff, size);
if(ret > 0)
{
printk("copy_from_user error\n");
return ret;
}
if(led_cmd == 1 || led_cmd == 0)
{
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), led_cmd);
gpio_free(S5PV210_GPC0(3));
}
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), led_cmd);
gpio_free(S5PV210_GPC0(4));
}
}
return 0;
}
int led_misc_open(struct inode * inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int led_misc_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
//ioctl
long led_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
switch(cmd)
{
case LED1:
if(args == LED_ON)
{
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), 1);
gpio_free(S5PV210_GPC0(3));
}
}
else if(args == LED_OFF)
{
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), 0);
gpio_free(S5PV210_GPC0(3));
}
}
break;
case LED2:
if(args == LED_ON)
{
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), 1);
gpio_free(S5PV210_GPC0(4));
}
}
else if(args == LED_OFF)
{
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), 0);
gpio_free(S5PV210_GPC0(4));
}
}
break;
case LED_ALL:
if(args == LED_ON)
{
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), 1);
gpio_free(S5PV210_GPC0(3));
}
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), 1);
gpio_free(S5PV210_GPC0(4));
}
}
else if(args == LED_OFF)
{
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), 0);
gpio_free(S5PV210_GPC0(3));
}
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), 0);
gpio_free(S5PV210_GPC0(4));
}
}
break;
default:
break;
}
return 0;
}
//1.1
struct file_operations fops = {
.open = led_misc_open,
.release = led_misc_release,
.read = led_misc_read,
.write = led_misc_write,
.unlocked_ioctl = led_misc_ioctl,
};
//1.赋值
struct miscdevice led_misc_dev = {
.minor = 100,
.name = "led_misc",
.fops = &fops,
};
static int __init led_misc_drv_init(void)
{
//2.申请杂项设备
int ret;
printk("%s\n", __FUNCTION__);
ret = misc_register(&led_misc_dev);
if(ret < 0)
{
printk("misc_register error\n");
return 0;
}
//3.硬件初始化
//请求GPIO操作权限
if(0 == gpio_request(S5PV210_GPC0(3), "gpc0_3"))
{
gpio_direction_output(S5PV210_GPC0(3), 0);
gpio_free(S5PV210_GPC0(3));
}
if(0 == gpio_request(S5PV210_GPC0(4), "gpc0_4"))
{
gpio_direction_output(S5PV210_GPC0(4), 0);
gpio_free(S5PV210_GPC0(4));
}
return 0;
}
static void __exit led_misc_drv_exit(void)
{
printk("%s\n", __FUNCTION__);
misc_deregister(&led_misc_dev);
}
module_init(led_misc_drv_init);
module_exit(led_misc_drv_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <asm-generic/ioctl.h>
#define LED1 _IOW('S', 0x01, int)
#define LED2 _IOW('S', 0x02, int)
#define LED_ALL _IOW('S', 0x03, int)
#define LED_ON 0x01
#define LED_OFF 0x02
int main()
{
int fd;
fd = open("dev/led_misc", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
while(1)
{
ioctl(fd, LED1, LED_ON);
sleep(2);
ioctl(fd, LED1, LED_OFF);
sleep(1);
ioctl(fd, LED2, LED_ON);
sleep(2);
ioctl(fd, LED2, LED_OFF);
sleep(1);
ioctl(fd, LED_ALL, LED_ON);
sleep(2);
ioctl(fd, LED_ALL, LED_OFF);
sleep(1);
}
return 0;
}
2.非阻塞
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
//定义一个等待队列头
wait_queue_head_t wait;
int wait_data;
unsigned int key_value;
//2.实现fops
//阻塞方式读取
ssize_t button_drv_read(struct file *filp, char __user *buff, size_t size, loff_t * flags)
{
int ret;
if((filp->f_flags & O_NONBLOCK) && (!wait_data)) //如果是阻塞且没有数据
{
return -EAGAIN;
}
wait_event_interruptible(wait, wait_data);//等待事件
ret = copy_to_user(buff, &key_value, size);
if(ret > 0)
{
printk("copy_to_user error\n");
return ret;
}
wait_data = 0;
return 0;
}
int button_drv_open(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
struct file_operations fops = {
.open = button_drv_open,
.release = button_drv_release,
.read = button_drv_read,
};
//1.定义杂项设备
struct miscdevice button_dev = {
.minor = 10,
.name = "button_dev",
.fops = &fops,
};
//5.中断处理函数
irqreturn_t button_irq_handler(int no, void *data)
{
key_value = gpio_get_value(S5PV210_GPH0(0));
wait_data = 1;
//唤醒等待队列
wake_up_interruptible(&wait);
return IRQ_HANDLED;
}
static int __init button_irq_init(void)
{
int ret;
printk("%s\n", __FUNCTION__);
//3.注册杂项设备
ret = misc_register(&button_dev);
if(ret < 0)
{
printk("misc_register error\n");
return ret;
}
//4.申请中断
ret = request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_misc_deregistr;
}
//5.初始化等待队列头
init_waitqueue_head(&wait);
wait_data = 0;
return 0;
err_misc_deregistr:
misc_deregister(&button_dev);
return ret;
}
static void __exit button_irq_exit(void)
{
printk("%s\n", __FUNCTION__);
//注销中断
free_irq(IRQ_EINT(0), NULL);
//注销杂项设备
misc_deregister(&button_dev);
}
module_init(button_irq_init);
module_exit(button_irq_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("dev/button_dev", O_RDWR | O_NONBLOCK);
if(fd < 0)
{
perror("open");
exit(1);
}
unsigned int key_value;
while(1)
{
read(fd, &key_value, sizeof(key_value));
if(key_value == 0)
{
printf("按键按下\n");
key_value = -1;
}
else if(key_value == 1)
{
printf("按键抬起\n");
key_value = -1;
}
}
close(fd);
return 0;
}
3.多路复用poll
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/poll.h>
unsigned int key_value;
wait_queue_head_t wait;
int wait_data;
irqreturn_t button_irq_handler(int no, void *args)
{
key_value = gpio_get_value(S5PV210_GPH0(0));
wait_data = 1;
wake_up_interruptible(&wait);
return IRQ_HANDLED;
}
//poll多路复用
unsigned int button_drv_poll(struct file *filp, struct poll_table_struct *poll_table)
{
unsigned int ret = 0;
poll_wait(filp, &wait, poll_table);
if(wait_data)
{
ret |= POLLIN;
}
return ret;
}
int button_drv_open(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
ssize_t button_drv_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
int ret;
if((filp->f_flags & O_NONBLOCK) && wait_data == 0)
return -EAGAIN;
wait_event_interruptible(wait, wait_data);
if(key_value == 1)
key_value = 0;
else
key_value = 1;
ret = copy_to_user(buff, &key_value, size);
if(ret < 0)
{
printk("copy_to_user error\n");
return ret;
}
wait_data = 0;
return 0;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.open = button_drv_open,
.release = button_drv_release,
.read = button_drv_read,
.poll = button_drv_poll,
};
struct miscdevice button_dev = {
.minor = 20,
.name = "button_dev",
.fops = &fops,
};
static int __init button_drv_init(void)
{
int ret;
printk("%s\n", __FUNCTION__);
ret = misc_register(&button_dev);
if(ret < 0)
{
printk("misc_register error\n");
return ret;
}
ret = request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_misc_deregister;
}
init_waitqueue_head(&wait);
wait_data = 0;
return 0;
err_misc_deregister:
misc_deregister(&button_dev);
return ret;
}
static void __exit button_drv_exit(void)
{
printk("%s\n", __FUNCTION__);
free_irq(IRQ_EINT(0), NULL);
misc_deregister(&button_dev);
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
int main()
{
struct pollfd fds[2];
char buff[128] = {0};
int len;
int ret;
int key_value;
int fd = open("dev/button_dev", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
fds[0].fd = 0;//写
fds[0].events = POLLIN;
fds[1].fd = fd;//按键事件
fds[1].events = POLLIN;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
while(1)
{
ret = poll(fds, sizeof(fds)/sizeof(fds[0]), -1);
if(ret > 0)
{
if(fds[0].revents & POLLIN)
{
len = read(0, buff, sizeof(buff));
buff[len] = '\0';
printf("stdin:%s",buff);
bzero(buff, sizeof(buff));
}
if(fds[1].revents & POLLIN)
{
read(fd, &key_value, sizeof(key_value));
if(key_value == 1)
printf("键按下\n");
else if(key_value == 0)
printf("键抬起\n");
}
}
}
close(fd);
}
4.异步通知fasync
驱动程序
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <asm-generic/signal.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
struct fasync_struct *button_fasync;
unsigned int key_value;
irqreturn_t button_irq_handler(int no, void *args)
{
key_value = gpio_get_value(S5PV210_GPH0(0));
kill_fasync(&button_fasync, SIGIO, POLLIN);
return IRQ_HANDLED;
}
int button_drv_open(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
int ret;
ret = copy_to_user(buff, &key_value, size);
if(ret > 0)
{
printk("copy_to_user error\n");
return ret;
}
return 0;
}
ssize_t button_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd, filp, on, &button_fasync);
}
struct file_operations fops = {
.open = button_drv_open,
.read = button_drv_read,
.release = button_drv_release,
.fasync = button_drv_fasync,
};
struct miscdevice button_dev = {
.minor = 10,
.name = "button_dev",
.fops = &fops,
};
static int __init button_drv_init(void)
{
int ret;
printk("%s\n", __FUNCTION__);
ret = misc_register(&button_dev);
if(ret < 0)
{
printk("misc_register error\n");
}
ret = request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "button_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_misc_deregister;
}
return 0;
err_misc_deregister:
misc_deregister(&button_dev);
return ret;
}
static void __exit button_drv_exit(void)
{
printk("%s\n", __FUNCTION__);
free_irq(IRQ_EINT(0), NULL);
misc_deregister(&button_dev);
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
应用程序
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
int fd;
void button_read(int signal)
{
int key_value;
read(fd, &key_value, sizeof(key_value));
if(key_value == 0)
printf("键按下\n");
else if(key_value == 1)
printf("键抬起\n");
}
int main()
{
int flags;
fd = open("dev/button_dev", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
fcntl(fd, F_SETOWN, getpid());
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);
signal(SIGIO, button_read);
while(1)
{
sleep(1000);
}
close(fd);
return 0;
}
3.中断与定时器
1.中断
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
//1.1杂项设备的fops
//1.定义杂项设备
unsigned int irq_num;
static int key_value;
ssize_t button_drv_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
int ret;
ret = copy_to_user(buff, &key_value, size);
if(ret > 0)
{
printk("copy_to_user error\n");
return ret;
}
return 0;
}
int button_drv_open(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
struct file_operations fops = {
.open = button_drv_open,
.release = button_drv_release,
.read = button_drv_read,
};
struct miscdevice button_dev = {
.minor = 10,
.name = "button_dev",
.fops = &fops,
};
//4.中断处理函数
irqreturn_t button_irq_handler(int irq_num, void *data)
{
printk("%s\n", __FUNCTION__);
key_value = gpio_get_value(S5PV210_GPH0(0));
if(key_value)
key_value = 0;
else
key_value = 1;
return IRQ_HANDLED;
}
static int __init button_drv_init(void)
{
//2.注册杂项设备
int ret;
printk("%s\n", __FUNCTION__);
ret = misc_register(&button_dev);
if(ret < 0)
{
printk("misc_register error\n");
return ret;
}
//3.申请中断
ret = request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_misc_deregister;
}
return 0;
err_misc_deregister:
misc_deregister(&button_dev);
return ret;
}
static void __exit button_drv_exit(void)
{
//注销杂项设备
printk("%s\n", __FUNCTION__);
free_irq(IRQ_EINT(0), NULL);
misc_deregister(&button_dev);
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
//1.1杂项设备的fops
//1.定义杂项设备
unsigned int irq_num;
static int key_value;
ssize_t button_drv_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
int ret;
ret = copy_to_user(buff, &key_value, size);
if(ret > 0)
{
printk("copy_to_user error\n");
return ret;
}
return 0;
}
int button_drv_open(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
struct file_operations fops = {
.open = button_drv_open,
.release = button_drv_release,
.read = button_drv_read,
};
struct miscdevice button_dev = {
.minor = 10,
.name = "button_dev",
.fops = &fops,
};
//4.中断处理函数
irqreturn_t button_irq_handler(int irq_num, void *data)
{
printk("%s\n", __FUNCTION__);
key_value = gpio_get_value(S5PV210_GPH0(0));
if(key_value)
key_value = 0;
else
key_value = 1;
return IRQ_HANDLED;
}
static int __init button_drv_init(void)
{
//2.注册杂项设备
int ret;
printk("%s\n", __FUNCTION__);
ret = misc_register(&button_dev);
if(ret < 0)
{
printk("misc_register error\n");
return ret;
}
//3.申请中断
ret = request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_misc_deregister;
}
return 0;
err_misc_deregister:
misc_deregister(&button_dev);
return ret;
}
static void __exit button_drv_exit(void)
{
//注销杂项设备
printk("%s\n", __FUNCTION__);
free_irq(IRQ_EINT(0), NULL);
misc_deregister(&button_dev);
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
2.中断下半部
//中断下半部的使用
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
unsigned int key_value; //按键状态
struct tasklet_struct button_tasklet; //中断下半部tasklet
struct work_struct button_work; //中断下半部work
//中断下半部tasklet处理函数
void button_irq_tasklet(unsigned long data)
{
printk("中断下半部:%s\n", __FUNCTION__);
key_value = gpio_get_value(S5PV210_GPH0(0));
if(key_value == 0)
key_value = 1;
else
key_value = 0;
}
void button_irq_work(struct work_struct *work)
{
printk("中断下半部:%s\n", __FUNCTION__);
}
//2.实现fops
ssize_t button_drv_read(struct file *filp, char __user *buff, size_t size, loff_t * flags)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_open(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
struct file_operations fops = {
.open = button_drv_open,
.release = button_drv_release,
.read = button_drv_read,
};
//6..实现中断
irqreturn_t button_irq_handler(int no, void *data)
{
printk("%s\n", __FUNCTION__);
//7.调度中断下半部执行
tasklet_schedule(&button_tasklet);
schedule_work(&button_work);
return IRQ_HANDLED;
}
//1.杂项设备对象
struct miscdevice button_dev = {
.minor = 10,
.name = "button_dev",
.fops = &fops,
};
static int __init button_irq_init(void)
{
int ret;
printk("%s\n", __FUNCTION__);
//3.申请对象
ret = misc_register(&button_dev);
if(ret < 0)
{
printk("misc_register error\n");
return ret;
}
//4.注册中断
ret = request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_misc_deregister;
}
//5.初始化中断下半部
tasklet_init(&button_tasklet, button_irq_tasklet, 0);
INIT_WORK(&button_work, button_irq_work);
return 0;
err_misc_deregister:
misc_deregister(&button_dev);
return ret;
}
static void __exit button_irq_exit(void)
{
printk("%s\n", __FUNCTION__);
free_irq(IRQ_EINT(0), NULL);
misc_deregister(&button_dev);
}
module_init(button_irq_init);
module_exit(button_irq_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
int fd;
int button_value;
fd = open("/dev/button_dev", O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
while(1)
{
read(fd, &button_value, sizeof(button_value));
if(button_value == 1)
{
printf("键按下\n");
}
else if(button_value == 0)
{
printf("键抬起\n");
}
}
return 0;
}
3.定时器timer
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <linux/timer.h>
unsigned int irq_num;
static int key_value;
//1.定义定时器
struct timer_list button_time;
ssize_t button_drv_read(struct file *filp, char __user *buff, size_t size, loff_t *flags)
{
int ret;
ret = copy_to_user(buff, &key_value, size);
if(ret > 0)
{
printk("copy_to_user error\n");
return ret;
}
return 0;
}
int button_drv_open(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
int button_drv_release(struct inode *inode, struct file *filp)
{
printk("%s\n", __FUNCTION__);
return 0;
}
struct file_operations fops = {
.open = button_drv_open,
.release = button_drv_release,
.read = button_drv_read,
};
struct miscdevice button_dev = {
.minor = 10,
.name = "button_dev",
.fops = &fops,
};
void button_irq_timer(unsigned long data)
{
printk("----------%s-----------\n",__FUNCTION__);
key_value = gpio_get_value(S5PV210_GPH0(0));
if(key_value)
key_value = 0;
else
key_value = 1;
}
irqreturn_t button_irq_handler(int irq_num, void *data)
{
printk("%s\n", __FUNCTION__);
//3.等待10ms
mod_timer(&button_time, jiffies + HZ/100);
return IRQ_HANDLED;
}
static int __init button_drv_init(void)
{
int ret;
printk("%s\n", __FUNCTION__);
ret = misc_register(&button_dev);
if(ret < 0)
{
printk("misc_register error\n");
return ret;
}
ret = request_irq(IRQ_EINT(0), button_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_misc_deregister;
}
//2.初始化定时器
init_timer(&button_time);
button_time.function = button_irq_timer;
add_timer(&button_time);
return 0;
err_misc_deregister:
misc_deregister(&button_dev);
return ret;
}
static void __exit button_drv_exit(void)
{
//注销杂项设备
printk("%s\n", __FUNCTION__);
del_timer(&button_time);
free_irq(IRQ_EINT(0), NULL);
misc_deregister(&button_dev);
}
module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");
4.同步与互斥
1.原子锁(未完成)
在这里插入代码片
2.自旋锁(未完成)
3.读写锁(未完成)
在这里插入代码片
4.顺序锁(未完成)
在这里插入代码片
5.信号量(未完成)
在这里插入代码片
6.互斥量(未完成)
在这里插入代码片
8.platform总线框架
1.设备部分相关成员和函数
1.平台设备对象定义如下
struct platform_device {
const char * name; //设备对象描述,和驱动名字一样可匹配
int id; //-1
struct device dev; //设备父类,需要实现其下的release方法
u32 num_resources; //资源数量
struct resource * resource; //平台资源
const struct platform_device_id *id_entry;//id
struct mfd_cell *mfd_cell;
struct pdev_archdata archdata;
};
2.平台设备对象的成员resource对象定义如下
struct resource {
resource_size_t start; //资源的起始
resource_size_t end; //资源的中止
const char *name; //资源的名字
unsigned long flags; //资源的类型
};
3.注册对象
int platform_device_register(struct platform_device *pdev)
4.释放对象
void platform_device_unregister(struct platform_device *pdev)
2.设备部分实现步骤
1.实例化对象struct platform_device (name, id, num_resources, resource, dev(.release)).
2.实现struct platform_device中的dev(.release) 实现struct resource led_resources[] (.start, .end, .flags)
3.申请对象platform_device_register()
4.释放对象platform_device_unregister()
3.设备部分代码实现
//平台总线设备部分的实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
//硬件资源地址
#define GPC0_BASE 0xE0200060
#define GPC0_SIZE 8
struct resource led_plat_resource[] = {
[0] = {
.start = GPC0_BASE,
.end = GPC0_BASE + GPC0_SIZE - 1,
.flags = IORESOURCE_MEM,//地址资源类型
},
//以下为举例
[1] = {
.start = 0x1234,
.end = 0x1234,
.flags = IORESOURCE_IRQ,//中断资源类型
},
};
void led_pdev_release(struct device *dev)
{
//此函数须实现,否则卸载模块出错
printk("%s\n",__FUNCTION__);
}
//1.实例化设备对象
struct platform_device led_pdev ={
.name = "s5pv210_led", //用于匹配平台驱动
.id = -1,
.dev =
{
.release = led_pdev_release,
},
.num_resources = ARRAY_SIZE(led_plat_resource),//资源数目
.resource = led_plat_resource, //平台资源
};
static int __init led_plat_drv_init(void)
{
//2.注册对象
printk("%s\n",__FUNCTION__);
return platform_device_register(&led_pdev);
}
static void __exit led_plat_dev_exit(void)
{
//3.注销对象
printk("%s\n",__FUNCTION__);
platform_device_unregister(&led_pdev);
}
module_init(led_plat_drv_init);//模块加载函数
module_exit(led_plat_dev_exit);//模块卸载函数
MODULE_LICENSE("GPL");
4.驱动部分相关的成员和函数
1.平台驱动对象定义如下
struct platform_driver {
int (*probe)(struct platform_device *);//驱动与设备匹配成功后此函数会执行
int (*remove)(struct platform_device *);//模块卸载执行
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; //继承基类device_driver,需实现其成员name
const struct platform_device_id *id_table;//用于匹配
};
2.平台驱动对象其下的id_table成员
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];//此名字用于与平台设备匹配
kernel_ulong_t driver_data
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
3.probe函数,匹配设备成功后调用
int (*probe)(struct platform_device *device);
4.获取平台资源
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
dev:设备对象
type:资源类型
num:第几个资源
int platform_get_irq(struct platform_device *dev, unsigned int num)
dev:设备对象
num:第几个中断资源
5.驱动部分实现步骤
1.定义platform_driver对象并实现其中的成员
2.实现platform_driver内的probe()
2.1获取platform_device对象内的资源
platform_get_resource()
2.2申请全局空间
kzalloc()
2.2申请设备号
alloc_chrdev_region()
2.3创建cdev对象
cdev_alloc()
cdev_init()
cdev_add()
2.4实现cdev内的fops
2.5创建类
class_create()
2.6创建设备节点
device_create()
2.7硬件初始化
ioremap()
3.实现platform_driver内的remove()
3.1.iounmap()
3.2.device_destory()
3.3.class_destory()
3.4.cdev_del()
3.5.unregister_chrdev_region()
3.6.kfree()
3.注册驱动对象
platform_driver_register();
4.注销驱动对象
platform_driver_unregisger()
6.驱动部分代码实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/io.h>
#define GPC0_SIZE 8
//2.1设计全局设备对象类型
struct s5pv210_led{
dev_t devno;
struct class *clz;
struct device *dev;
struct cdev *cdev;
void * base_res;
};
struct s5pv210_led *led_dev;
//2.4.1
int led_pdrv_open(struct inode *inode, struct file *filp)
{
return 0;
}
//2.4.1
ssize_t led_pdrv_write(struct file *filp, const char __user *buf, size_t size, loff_t *flags)
{
return 0;
}
//2.4.1
int led_pdrv_close(struct inode *inode, struct file *filp)
{
return 0;
}
//2.4.1实现fops对象
struct file_operations fops = {
.open = led_pdrv_open,
.write = led_pdrv_write,
.release= led_pdrv_close,
};
//2.实现platform_driver中的probe()
int led_pdrv_probe(struct platform_device *pdev)
{
struct resource *res1,*res2,*res3;
int irqno;
int ret;
printk("%s\n",__FUNCTION__);
//2.1获取platform_device对象中的资源
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取第一个地址资源
res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); //获取第二个地址资源
res3 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); //获取第一个中断资源
irqno = platform_get_irq(pdev, 0); //另一个获取中断资源的方法
printk("res1->start = %x\n",res1->start);
printk("res2->start = %x\n",res2->start);
printk("res3->start = %x\n",res3->start);
printk("res3->end = %x\n",res3->end);
printk("irqno = %x\n",irqno);
// 2.2申请全局对象空间
led_dev = kzalloc(sizeof(struct s5pv210_led), GFP_KERNEL);
if(IS_ERR(led_dev)){
printk("kzalloc error\n");
return -ENOMEM;
}
//2.3申请设备号
ret = alloc_chrdev_region(&led_dev->devno, 5, 1, "led_pdrv");
if(ret < 0){
printk("alloc_chrdev_region error\n");
goto err_kfree;
}
//2.4创建cdev对象
led_dev->cdev = cdev_alloc();
if(IS_ERR(led_dev->cdev)){
printk("cdev_alloc error\n");
ret = PTR_ERR(led_dev->cdev);
goto err_unregister;
}
cdev_init(led_dev->cdev, &fops);
cdev_add(led_dev->cdev, led_dev->devno, 1);
//2.5创建类
led_dev->clz = class_create(THIS_MODULE, "led_clz");
if(led_dev->clz == NULL){
printk("class_create error\n");
ret = -EAGAIN;
goto err_cdev;
}
//2.6创建设备节点
led_dev->dev = device_create(led_dev->clz, NULL, led_dev->devno,NULL, "led2");
if(IS_ERR(led_dev->dev)){
printk("device_create error");
ret = PTR_ERR(led_dev->dev);
goto err_class;
}
//2.7硬件初始化,地址映射
led_dev->base_res = ioremap(res1->start, GPC0_SIZE);
if(!led_dev->base_res){
printk("ioremap error\n");
ret = -EINVAL;
goto err_device;
}
return 0;
err_device:
device_destroy(led_dev->clz, led_dev->devno);
err_class:
class_destroy(led_dev->clz);
err_cdev:
cdev_del(led_dev->cdev);
err_unregister:
unregister_chrdev_region(led_dev->devno, 1);
err_kfree:
kfree(led_dev);
return ret;
}
//3.实现platform_driver中的remove()
int led_pdrv_remove (struct platform_device *pdev)
{
printk("%s\n",__FUNCTION__);
iounmap(led_dev->base_res);
device_destroy(led_dev->clz, led_dev->devno);
class_destroy(led_dev->clz);
cdev_del(led_dev->cdev);
unregister_chrdev_region(led_dev->devno, 1);
kfree(led_dev);
return 0;
}
//实现1.1id_table
struct platform_device_id led_id_table[] = {
{"s5pv210_led",0x1122},
{"s3c2410_led",0x3344},
{"s3c4416_led",0x5566},
};
// 1.实例化platform_driver对象
struct platform_driver led_pdrv = {
.probe = led_pdrv_probe,
.remove = led_pdrv_remove,
.driver = {
.name = "s5pv210_led_platform", //该name必须初始化 ,不用于匹配 ,ls /sys/bus/platform/drivers/s5pv210_led/
},
.id_table = led_id_table,
};
static int __init led_plat_drv_init(void)
{
// 3.注册driver对象
printk("%s\n",__FUNCTION__);
return platform_driver_register(&led_pdrv);
}
static void __exit led_plat_drv_exit(void)
{
//4.注销platform_driver对象
printk("%s\n",__FUNCTION__);
platform_driver_unregister(&led_pdrv);
}
module_init(led_plat_drv_init);//模块加载
module_exit(led_plat_drv_exit);//模块卸载
MODULE_LICENSE("GPL");
9.input输入子系统
1.相关的对象和函数
struct input_dev *key_dev;//输入设备对象
2.驱动层实现步骤
1.定义struct input_dev对象
struct input_dev *key_dev;
2.申请变量空间
key_dev = input_allocate_device();
3.将struct input_dev对象初始化(输入事件,触发方式)
key_dev->evbit[0] = BIT_MASK(EV_KEY);
key_dev->keybit[BIT_WORD(KEY_DOWN)] = BIT_MASK(KEY_DOWN);
4.注册对象
input_register_device(key_dev);
5.申请中断
request_irq()
6.中断处理函数上报输入字件
input_report_key(key_dev, KEY_DOWN, 0);//按键事件写法
input_event(key_dev,EV_KEY,KEY_DOWN,1);//通用写法
input_sync(key_dev); //上报数据结束
7.卸载(注销中断,注销设备,注销空间)
free_irq(irq_num, NULL);
input_unregister_device(key_dev);
input_free_device(key_dev);
3.代码实现
3.1驱动程序
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
struct input_dev *key_dev;
unsigned int irq_num;
//1.5中断处理函数上报事件
static irqreturn_t key_irq_handler(int irq, void *dummy)
{
int value;
printk("key_irq_handler\n");
value = gpio_get_value(S5PV210_GPH0(1));
if(value)//按下
{
input_report_key(key_dev, KEY_DOWN, 0);//只适用于key
}
else//松开
{
input_event(key_dev,EV_KEY,KEY_DOWN,1);//通用写法
}
input_sync(key_dev);//表示上报完成
return IRQ_HANDLED;//中断处理完成
}
//1.加载模块
static int __init input_key_drv_init(void)
{
int ret;
//1.1申请对象空间
key_dev = input_allocate_device();
if(IS_ERR(key_dev))
{
printk("input_allocate_device error\n");
return PTR_ERR(key_dev);
}
//1.2设置输入事件
key_dev->evbit[0] = BIT_MASK(EV_KEY);
key_dev->keybit[BIT_WORD(KEY_DOWN)] = BIT_MASK(KEY_DOWN);
//1.3注册输入设备
ret = input_register_device(key_dev);
if(ret)
{
printk("input_register_device error\n");
goto err_free_device;
}
//1.4申请中断
irq_num = IRQ_EINT(1);
ret = request_irq(irq_num, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key_inet1", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_input_unregister_device;
}
return 0;
err_input_unregister_device:
input_unregister_device(key_dev);
err_free_device:
input_free_device(key_dev);
return ret;
}
//2.卸载模块
static void __exit input_key_drv_exit(void)
{
free_irq(irq_num, NULL);//注销中断
input_unregister_device(key_dev);//注销输入设备
input_free_device(key_dev);//释放输入设备空间
}
module_init(input_key_drv_init);
module_exit(input_key_drv_exit);
3.2应用程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
int main(void)
{
int fd;
int ret;
struct input_event event;//事件对象
if((fd = open("/dev/event0",O_RDWR)) < 0){
perror("open");
exit(1);
}
while(1)
{
if((ret = read(fd,&event,sizeof(event))) < 0)
{
perror("read");
exit(1);
}
if(event.type == EV_KEY)
{
if(event.code == KEY_DOWN)
{
if(event.value)
printf("按下--->下键\n");
else
printf("松开--->下键\n");
}
}
}
close(fd);
return 0;
}
4.与平台总线结合步骤
1.设备部分,在platform_device基础上增加
1.定义平台总线设备对象
struct platform_device input_dev = {
.dev = {
.platform_data = &key_data, //方法一
};
2.将硬件资源封装放入私有数据input_dev->key_data
//定义的自定义数据
struct input_pdata key_pdata = {
.keyset = key_set,//存放按键描述,事件类型,值,触发方式等
.keysize = ARRAY_SIZE(key_set),//资源大小
};
3.注册对象
platform_device_register(&input_pdev);
2.驱动部分
1.定义platform_driver对象并实现其中的成员
2.实现platform_driver内的probe()
2.1获取platform_device对象内的资源
获取平台的自定义数据,包括按键描述,触发方式,按键值
platform_get_resource()
2.2申请全局空间
kzalloc()
2.2申请设备号
alloc_chrdev_region()
2.3创建cdev对象
cdev_alloc()
cdev_init()
cdev_add()
2.4实现cdev内的fops
2.5创建类
class_create()
2.6创建设备节点
device_create()
2.7硬件初始化
ioremap()
3.实现platform_driver内的remove()
3.1.iounmap()
3.2.device_destory()
3.3.class_destory()
3.4.cdev_del()
3.5.unregister_chrdev_region()
3.6.kfree()
3.注册驱动对象
platform_driver_register();
4.注销驱动对象
platform_driver_unregisger()
10.i2c子系统
1.相关的对象和函数
1.适配器对象
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
};
2.适配器算法
struct i2c_algorithm {
//数据传输函数
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
3.发送的数据包描述
struct i2c_msg {
__u16 addr; /* i2c设备地址 */
__u16 flags; /* 读/写。一般1为读,也就是I2C_M_RD,0为写 */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
4.描述i2c设备信息
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
int irq;
};
5.注册适配器过程(给设备分配编号,注册到/sys/devices/i2c-%d)
int i2c_add_adapter(struct i2c_adapter *adapter)//注册
int i2c_register_adapter(struct i2c_adapter *adap)//注册
void i2c_scan_static_board_info(struct i2c_adapter *adapter)//扫描并创建i2c设备
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)//添加i2c设备
struct i2c_client *client = kzalloc(sizeof *client, GFP_KERNEL);
client->adapter = adap;
client->dev.platform_data = info->platform_data;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
client->dev.parent = &client->adapter->dev; /* 设置设备的适配器 */
client->dev.bus = &i2c_bus_type; /* 设置所属总线 */
client->dev.type = &i2c_client_type; /* 设置设备类型 */
client->dev.of_node = info->of_node;
strlcpy(client->name, info->type, sizeof(client->name));
device_register(&client->dev);
void i2c_del_adapter(struct i2c_adapter * adap)
=================================================================
2.实现步骤
要自已实现的部分
1.描述驱动i2c驱动的对象struct i2c_driver
struct i2c_driver {
unsigned int class;
int (*probe)(struct i2c_client *, const struct i2c_device_id *);//匹配成功后调用
int (*remove)(struct i2c_client *);
struct device_driver driver;
const struct i2c_device_id *id_table;
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
2.用于匹配 i2c_deice_id,
struct i2c_device_id {
char name[I2C_NAME_SIZE];
kernel_ulong_t driver_data /* Data private to the driver */
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
3.实例化驱动对象,需要实现probe(),remove()
static struct i2c_driver xxx_driver = {
.probe = xxx_probe,
.remove = xxx_remove,
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = xxx_of_match,
},
.id_table = xxx_id,
};
3.1实现struct i2c_drive内的probe()函数
int at24_i2c_probe(struct i2c_client *client, const struct
i2c_device_id *dev_id)
{
3.1.1申请全局对象
kzalloc()
3.1.2申请设备号
alloc_chrdev_region()
3.1.3创建cdev(实现fops)
cdev_alloc()
cdev_init()
cdev_add()
/*实现fops*/
1.read()
从从设备中读取数据到内核空间
int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
client从设备
buf内核空间数据接收
count数据大小
将内核空间数据写入到用户空间
copy_to_user()
2.write()
从用户空间获取数据到内核空间
copy_from_user()
将内核空间数据写入到从设备
int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
client从设备
buf内核空间数据接收
count数据大小
3.open()
4.release()
3.1.4创建类
class_create()
3.1.5创建设备节点
device_create()
3.1.6保存i2c_client对象
at24_dev->client = client;
}
3.2实现struct i2c_drive内的remove()函数
3.2.1 device_destroy()
3.2.2 class_destroy()
3.2.3 cdev_del()
3.2.4 unregister_chrdev_region()
3.2.5 kfree()
4.注册i2c_驱动对象
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver( struct i2c_driver *driver)//另外一种写法
5.注销i2c_驱动对象
void i2c_del_driver(struct i2c_driver *driver)
11.framebuffer子系统(未完成)
12.代码(整理中…)
1.触摸屏驱动
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#define TOUCH_MAX_WIDTH 800
#define TOUCH_MAX_HEIGHT 480
struct s5pv210_gt811
{
int irqno;
struct input_dev *i_dev;
struct i2c_client *client;
struct work_struct ts_work;
};
struct s5pv210_gt811 *gt811_dev;
void gt811_touch_report(uint8_t id, uint16_t x, uint16_t y, uint8_t p)
{
input_report_abs(gt811_dev->i_dev, ABS_MT_POSITION_X, x);
input_report_abs(gt811_dev->i_dev, ABS_MT_POSITION_X, y);
input_report_abs(gt811_dev->i_dev, ABS_MT_PRESSURE, p);
input_report_abs(gt811_dev->i_dev, ABS_MT_TRACKING_ID, id);
input_sync(gt811_dev->i_dev);
printk("input_x = %d,input_y = %d,input_p = %d,input_id = %d\n",x,y,p,id);
}
int gt811_read_all(struct i2c_client * client,char *buf,int count)
{
int ret;
struct i2c_msg msg[2];
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = &buf[0];
msg[1].addr = client->addr;
msg[1].flags = 1;
msg[1].len = count - 2;
msg[1].buf = &buf[2];
ret = i2c_transfer(client->adapter, msg, 2);
if(ret != 2)
{
return -1;
}
return 0;
}
void gt811_ts_work(struct work_struct *work)
{
int count,i;
uint8_t point_data[36] = {0x7, 0x21, 0};
uint8_t position;
uint8_t check_sum = 0;
uint8_t tmp;
uint8_t track_id[5];
uint8_t point_count = 0;
uint8_t point_position;
uint8_t ts_p;
uint16_t ts_x,ts_y;
if(gt811_read_all(gt811_dev->client,point_data,ARRAY_SIZE(point_data)) < 0)
{
printk(" gt811_read_combine error\n");
return;
}
tmp = point_data[2] & 0x1f;
switch(tmp)
{
case 0:
position = 4;
for(count = 2; count < position; count++)
{
check_sum += point_data[count];
}
break;
case 1:
position = 9;
for(count = 2; count < position; count++)
{
check_sum += point_data[count];
}
break;
case 2:
case 3:
position = 14;
for(count = 2; count < position; count++)
{
check_sum += point_data[count];
}
break;
default:
position = 35;
for(count = 2; count < position; count++)
{
check_sum += point_data[count];
}
}
if(check_sum != point_data[position])
{
printk("check_sum error!\n");
return;
}
for(i = 0; i < 5; i++)
{
if(tmp & 0x1)
{
track_id[point_count++] = i;
}
tmp >>= 1;
}
for(i = 0; i < point_count; i++)
{
if(track_id[i] != 3)
{
if(track_id[i] < 3)
{
point_position = 4+ 5 * track_id[i];
}
else
{
point_position = 30;
}
ts_x = point_data[point_position]<< 8 | point_data[point_position+1];
ts_y = point_data[point_position+2]<< 8 | point_data[point_position+3];
ts_p = point_data[point_position+4];
}
else
{
point_position = 19;
ts_x = point_data[point_position]<< 8 | point_data[point_position+7];
ts_y = point_data[point_position+8]<< 8 | point_data[point_position+9];
ts_p = point_data[point_position+10];
}
gt811_touch_report(track_id[i],ts_x,ts_y,ts_p);
}
input_sync(gt811_dev->i_dev);
}
irqreturn_t gt811_irq_handler(int no, void *args)
{
schedule_work(>811_dev->ts_work);
return IRQ_HANDLED;
}
static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, int len)
{
struct i2c_msg msg;
int ret = -1;
msg.flags = 0;
msg.addr=client->addr;
msg.len=len;
msg.buf=data;
ret = i2c_transfer(client->adapter,&msg, 1);
return ret;
}
int gt811_ts_init(void)
{
int ret;
uint8_t config_info[] = {
0x06,0xA2,
0x12,0x10,0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,
0x05,0x55,0x15,0x55,0x25,0x55,0x35,0x55,0x45,0x55,
0x55,0x55,0x65,0x55,0x75,0x55,0x85,0x55,0x95,0x55,
0xA5,0x55,0xB5,0x55,0xC5,0x55,0xD5,0x55,0xE5,0x55,
0xF5,0x55,0x1B,0x03,0x00,0x00,0x00,0x13,0x13,0x13,
0x0F,0x0F,0x0A,0x50,0x30,0x0D,0x03,0x00,0x05,0x58,
0x02,0x00,0x04,0x00,0x00,0x32,0x2C,0x34,0x2E,0x00,
0x00,0x04,0x14,0x22,0x04,0x00,0x00,0x00,0x00,0x00,
0x20,0x14,0xEC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x0C,0x30,0x25,0x28,0x14,0x00,
0x00,0x00,0x00,0x00,0x00,0x01
};
config_info[62] = TOUCH_MAX_WIDTH >> 8;
config_info[61] = TOUCH_MAX_WIDTH & 0xff;
config_info[64] = TOUCH_MAX_HEIGHT >> 8;
config_info[63] = TOUCH_MAX_HEIGHT & 0xff;
ret = i2c_write_bytes(gt811_dev->client, config_info, ARRAY_SIZE(config_info));
if(ret < 0)
{
dev_info(>811_dev->client->dev, "GT811 Send config failed!\n");
return ret;
}
return 0;
}
int gt811_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id_table)
{
int ret;
unsigned char value;
printk("%s\n", __FUNCTION__);
gt811_dev = kzalloc(sizeof(struct s5pv210_gt811), GFP_KERNEL);
if(IS_ERR(gt811_dev))
{
printk("kzalloc error\n");
ret = PTR_ERR(gt811_dev);
return ret;
}
gt811_dev->client = client;
gt811_dev->i_dev = input_allocate_device();
if(IS_ERR(gt811_dev->i_dev))
{
printk("input_allocate_device error\n");
ret = PTR_ERR(gt811_dev->i_dev);
goto err_kfree;
}
__set_bit(EV_ABS, gt811_dev->i_dev->evbit);
__set_bit(ABS_X, gt811_dev->i_dev->absbit);
__set_bit(ABS_Y, gt811_dev->i_dev->absbit);
__set_bit(ABS_PRESSURE, gt811_dev->i_dev->absbit);
input_set_abs_params(gt811_dev->i_dev, ABS_X, 0, 480, 0, 0);
input_set_abs_params(gt811_dev->i_dev, ABS_Y, 0, 800, 0, 0);
input_set_abs_params(gt811_dev->i_dev, ABS_PRESSURE, 0, 255, 0, 0);
gt811_dev->i_dev->name = "gt811_ts";
gt811_dev->i_dev->phys = "ts_gt811";
gt811_dev->i_dev->uniq = "/ts/gt811";
gt811_dev->i_dev->id.bustype = 0x2;
gt811_dev->i_dev->id.product = 0x4;
gt811_dev->i_dev->id.vendor = 0x6;
gt811_dev->i_dev->id.version = 0x8;
ret = input_register_device(gt811_dev->i_dev);
if(ret < 0)
{
printk("input_register_device error\n");
goto err_free_device;
}
gpio_request(S5PV210_GPH1(6), "TS_INT");
gpio_direction_input(S5PV210_GPH1(6));
gpio_free(S5PV210_GPH1(6));
gpio_request(S5PV210_GPD0(3), "TS_RESET");
s3c_gpio_setpull(S5PV210_GPD0(3), S3C_GPIO_PULL_UP);
gpio_free(S5PV210_GPD0(3));
msleep(5);
gpio_request(S5PV210_GPD0(3), "TS_RESET");
gpio_direction_output(S5PV210_GPD0(3), 0);
msleep(1);
gpio_direction_input(S5PV210_GPD0(3));
gpio_free(S5PV210_GPD0(3));
msleep(80);
value = 1;
ret = i2c_master_send(gt811_dev->client, &value, 1);
if(ret != 1)
{
printk("i2c_master_send error\n");
ret = ENODEV;
goto err_unregister_device;
}
gt811_ts_init();
gt811_dev->irqno = IRQ_EINT(14);
ret = request_irq(IRQ_EINT(14), gt811_irq_handler, IRQF_TRIGGER_FALLING, "gt811_irq", NULL);
if(ret < 0)
{
printk("request_irq error\n");
goto err_unregister_device;
}
INIT_WORK(>811_dev->ts_work, gt811_ts_work);
return 0;
err_unregister_device:
input_unregister_device(gt811_dev->i_dev);
err_free_device:
input_free_device(gt811_dev->i_dev);
err_kfree:
kfree(gt811_dev);
return ret;
}
int gt811_i2c_remove(struct i2c_client *client)
{
printk("%s\n", __FUNCTION__);
free_irq(gt811_dev->irqno, NULL);
input_unregister_device(gt811_dev->i_dev);
input_free_device(gt811_dev->i_dev);
kfree(gt811_dev);
return 0;
}
struct i2c_device_id gt811_id_table[] = {
{"gt811_ts", 0x1234},
};
struct i2c_driver gt811_drv = {
.probe = gt811_i2c_probe,
.remove = gt811_i2c_remove,
.driver = {
.name = "gt811_s5pv210",
},
.id_table = gt811_id_table,
};
static int __init gt811_i2c_init(void)
{
printk("module_init name:%s\n", __FUNCTION__);
i2c_add_driver(>811_drv);
return 0;
}
static void __exit gt811_i2c_exit(void)
{
printk("module_exit name:%s\n", __FUNCTION__);
i2c_del_driver(>811_drv);
}
module_init(gt811_i2c_init);
module_exit(gt811_i2c_exit);
MODULE_LICENSE("GPL");
创建于2021.01.23
创建于2021.01.14
修改于2021.01.23