字符设备驱动(五)按键优化休眠

目录

  • 字符设备驱动(五)按键优化

    • 按键值读取
    • 休眠读取
      • 程序设计
      • 测试


title: 字符设备驱动(五)按键优化

tags: linux

date: 2018-11-23 17:56:57

toc: true

---

字符设备驱动(五)按键优化

按键值读取

Linux内部有系统函数s3c2410_gpio_getpin能够读取GPIO的值

unsigned int s3c2410_gpio_getpin(unsigned int pin)
{
    void __iomem *base = S3C24XX_GPIO_BASE(pin);
    unsigned long offs = S3C2410_GPIO_OFFSET(pin);

    return __raw_readl(base + 0x04) & (1<< offs);
}

在中断处理函数中有个参数是dev_id,这个是由request_irq初始化的,处理函数可以用这个结构体传递参数

static irqreturn_t buttons_irq(int irq, void *dev_id) 

也就是说一个中断绑定了一个dev_id,可用作传递参数,卸载中断函数

request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
free_irq(IRQ_EINT0, &pins_desc[0]);

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    struct pin_desc * pindesc = (struct pin_desc *)dev_id;
    unsigned int pinval;

    pinval = s3c2410_gpio_getpin(pindesc->pin); //这里就读取到了
}

休眠读取

程序设计

程序设计目的:App去读取按键值,如果有按键中断触发(键值有改变)则打印,否则休眠.

read(key)
    if change
        printf
    else
        sleep

也就是说App函数依然不变,但是需要修改驱动的读取函数,驱动的读取函数中有休眠的动作

// app.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fd;
    unsigned char key_val;
    fd = open("/dev/xyz0", O_RDWR);
    if (fd < 0)
    {
        printf("can‘t open!\n");
    }
    while (1)
    {
        read(fd, &key_val, 1);
        printf("key_val = 0x%x\n", key_val);
    }
    return 0;
}

驱动程序中需要设计休眠,中断发生来唤醒首先定义一个等待队列,下述是一个宏

//生成一个等待队列头wait_queue_head_t,名字为name
DECLARE_WAIT_QUEUE_HEAD(name) 

// 定义一个名为`button_waitq`的队列
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

休眠函数如下,condition=0才休眠,定义在include/linux/wait.h

#define wait_event_interruptible(wq, condition)             ({                                      int __ret = 0;                              if (!(condition))                               __wait_event_interruptible(wq, condition, __ret);       __ret;                              })

唤醒也是一个宏,参数是等待队列,放置在中断函数处理中,定义在include/linux/wait.h

wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

完整代码如下

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
//#include <linux/interrupt.h>

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

static struct class *drv_class;
static struct class_device  *drv_class_dev;

// 定义一个名为`button_waitq`的队列
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
// flag=1 means irq happened and need to update
int flag=0;

struct pin_desc{
    unsigned int pin;
    unsigned int key_val;
};

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;

struct pin_desc pins_desc[4] = {
    {S3C2410_GPF0, 0x01},
    {S3C2410_GPF2, 0x02},
    {S3C2410_GPG3, 0x03},
    {S3C2410_GPG11, 0x04},
};

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    printk("irq%d\r\n",irq);

    struct pin_desc * pindesc = (struct pin_desc *)dev_id;
    unsigned int pinval;

    pinval = s3c2410_gpio_getpin(pindesc->pin);

    if (pinval)
    {
        /* 松开 */
        key_val = 0x80 | pindesc->key_val;
    }
    else
    {
        /* 按下 */
        key_val = pindesc->key_val;
    }

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
    flag=1;

    return IRQ_RETVAL(IRQ_HANDLED);
}

static int drv_open(struct inode *inode, struct file *file)
{
    /* 配置GPF0,2为输入引脚 */
    /* 配置GPG3,11为输入引脚 */
    request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
    request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
    request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
    request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
    return 0;
}

int drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT0, &pins_desc[0]);
    free_irq(IRQ_EINT2, &pins_desc[1]);
    free_irq(IRQ_EINT11,&pins_desc[2]);
    free_irq(IRQ_EINT19,&pins_desc[3]);
    return 0;
}

static ssize_t drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    //int minor =  MINOR(file->f_dentry->d_inode->i_rdev);
    //printk("drv_write=%d\n",minor);
    return 0;
}

static ssize_t drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    if (size != 1)
    return -EINVAL;

    /* 如果没有按键动作, 休眠 */
    wait_event_interruptible(button_waitq, flag);

    /* 如果有按键动作, 返回键值 */
    copy_to_user(buf, &key_val, 1);
    flag = 0;

    return 1;
}

static struct file_operations drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   drv_open,
    .write  =   drv_write,
    .read   =   drv_read,
    .release =  drv_close,
};

static int major;
static int drv_init(void)
{
    int minor=0;
    major=register_chrdev(0, "drv", &drv_fops); // 注册, 告诉内核
    drv_class = class_create(THIS_MODULE, "drv");
    drv_class_dev = class_device_create(drv_class, NULL, MKDEV(major, 0), NULL, "xyz%d", minor);

    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    gpfdat = gpfcon + 1;
    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    gpgdat = gpgcon + 1;
    return 0;
}

static void drv_exit(void)
{
    unregister_chrdev(major, "drv"); // 卸载
    class_device_unregister(drv_class_dev);
    class_destroy(drv_class);
    iounmap(gpfcon);
    iounmap(gpgcon);
}

module_init(drv_init);
module_exit(drv_exit);
MODULE_AUTHOR("xxx");
MODULE_VERSION("0.1.0");
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");
MODULE_LICENSE("GPL");

测试

测试运行./text /dev/xyz0 & 后台运行

# ./test /dev/xyz0 &
# irq55
key_val = 0x3
irq55
key_val = 0x83
irq18
key_val = 0x2
irq18
key_val = 0x82
irq16
key_val = 0x1
irq16
key_val = 0x81
irq63
key_val = 0x4
irq63
key_val = 0x84

使用top查看占用

Load average: 0.00 0.01 0.00
  PID  PPID USER     STAT   VSZ %MEM %CPU COMMAND
  782   770 0        R     3096   5%   0% top
  770     1 0        S     3096   5%   0% -sh
  781   770 0        S     1312   2%   0% ./test /dev/xyz0

使用ps查看任务为S状态 休眠状态

# ps
  PID  Uid        VSZ Stat Command
    1 0          3092 S   init
  781 0          1312 S   ./test /dev/xyz0
  783 0          3096 R   ps

原文地址:https://www.cnblogs.com/zongzi10010/p/10009182.html

时间: 2024-08-29 15:27:34

字符设备驱动(五)按键优化休眠的相关文章

字符设备驱动(六)按键poll机制

title: 字符设备驱动(六)按键poll机制 tags: linux date: 2018-11-23 18:57:40 toc: true --- 字符设备驱动(六)按键poll机制 引入 在字符设备驱动(五)按键休眠中的App中虽然使用了休眠,但是如果Read没有返回的话会一直死等,类似阻塞,我们期望等待一段时间后自动返回,等待的时候程序依然是睡眠的,这里引入poll机制 应用程序的open/close/write/read都有对应的系统内核的sys_open/sys_close/sys

字符设备驱动(七)按键异步通知

目录 按键驱动方式对比 进程间发信号 目标 如何让驱动通知应用 程序编写 驱动程序 应用程序 完整代码如下 测试 title: 字符设备驱动(七)按键异步通知 tags: linux date: 2018-11-24 16:39:47 toc: true --- 按键驱动方式对比 查询:耗资源 中断: 没有超时机制,当没有中断作为生产者,read函数一直休眠 poll机制,加入超时机制 上述三种都是app主动去获取按键,使用异步通知的形式可以使按键发生后,通知app去读取 进程间发信号 以前使用

Linux内核分析(五)----字符设备驱动实现

原文:Linux内核分析(五)----字符设备驱动实现 Linux内核分析(五) 昨天我们对linux内核的子系统进行简单的认识,今天我们正式进入驱动的开发,我们今后的学习为了避免大家没有硬件的缺陷,我们都会以虚拟的设备为例进行学习,所以大家不必害怕没有硬件的问题. 今天我们会分析到以下内容: 1.      字符设备驱动基础 2.      简单字符设备驱动实现 3.      驱动测试 l  字符设备基础 1.       字符设备描述结构 在linux2.6内核中,使用cdev结构体描述一

linux字符设备驱动

一.字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系. 如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主.次设备号)以确定字符设备的唯一性.通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open().read().write()等. 在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获

linux 字符设备驱动开发详解

一.设备的分类及特点 1.字符设备 字符设备是面向数据流的设备,没有请求缓冲区,对设备的存取只能按顺序按字节的存取而不能随机访问.    Linux下的大多设备都是字符设备.应用程序是通过字符设备节点来访问字符设备的.通常至少需要实现 open, close, read, 和 write 等系统调用.    设备节点一般都由mknod命令都创建在/dev目录下,包含了设备的类型.主/次设备号以及设备的访问权限控制等,如:crw-rw----  1 root  root 4, 64 Feb 18

深入浅出~Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备

字符设备之中断按键

从题目就可以意会到这一节还是关于字符设备的驱动,不过是另一种技巧:中断.这个词一点都不陌生. 一.先来分析今天的重量级函数request_irq(),看看他的函数原型就行了,先不进行深入分析. int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev_id); 关键是参数的意义: ①irq:中断号,在irqs.h中定义,每一条中断线上面对应一个中

【转】深入浅出:Linux设备驱动之字符设备驱动

深入浅出:Linux设备驱动之字符设备驱动 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称

字符设备驱动模型

1.设备描述结构cdev驱动模型种类繁多,这就需要我从众多的模型中提取出他们的一些共性:a.驱动初始化a.1 分配设备描述结构a.2 初始化设备描述结构a.3 注册设备描述结构a.4 硬件初始化b.实现设备操作c.驱动注销 ------------------------------------------------------------------ 设备描述结构:在任何一种驱动模型中,设备都会用的内核中的一种结构来描述,我们的字符设备在内核中使用struct cdev 来来描述.struc