linux驱动之按键

key.c

<span style="font-size:14px;">#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/module.h>		/* For module specific items */
#include <linux/moduleparam.h>		/* For new moduleparam's */
#include <linux/types.h>		/* For standard types (like size_t) */
#include <linux/errno.h>		/* For the -ENODEV/... values */
#include <linux/kernel.h>		/* For printk/panic/... */
#include <linux/fs.h>			/* For file operations */
#include <linux/ioport.h>		/* For io-port access */
#include <linux/platform_device.h>	/* For platform_driver framework */
#include <linux/init.h>			/* For __init/__exit/... */
#include <linux/uaccess.h>		/* For copy_to_user/put_user/... */
#include <linux/io.h>			/* For inb/outb/... */
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/cdev.h>

struct timer_list button_timer;
struct cdev cdev;
dev_t devno;
volatile unsigned long gpncon;
volatile unsigned long gpndat;
unsigned char key;//键值
struct key_desc {
	int irq;
	char *name;
	int pin;           /* GPNDAT哪位 */
	unsigned char val; /* 按键值 */
};

static DECLARE_WAIT_QUEUE_HEAD(button_waiq);

static struct key_desc key_desc[] = {
	{IRQ_EINT(0), "S2", 0, 2},
	{IRQ_EINT(1), "S3", 1, 3},
	{IRQ_EINT(2), "S4", 2, 4},
	{IRQ_EINT(3), "S5", 3, 5},
	{IRQ_EINT(4), "S6", 4, 6},
	{IRQ_EINT(5), "S7", 5, 7},
};

static ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	wait_event_interruptible(button_waiq, key);

	copy_to_user(buf, &key, 1);
	key = 0;
	return 1;
}

static struct file_operations button_fops = 
{
	.owner	=	THIS_MODULE,
	.read	=	button_read,
};

static irqreturn_t button_function(int irq, void *dev_id)
{
	struct key_desc *key_desc = (struct key_desc *)dev_id;
	disable_irq_nosync(key_desc->irq);//关闭按键中断
	mod_timer(&button_timer, jiffies + HZ/100);// 定时10ms
	button_timer.data = (unsigned long)dev_id;
	return IRQ_HANDLED;
}

static int button_time_function(unsigned long data)
{
	unsigned long value = readl(gpndat);
	struct key_desc *key_desc = (struct key_desc *)data;
	if(value & (1 << key_desc->pin))//如果已经松开,说明是抖动
	{
		enable_irq(key_desc->irq);//打开按键中断
		return 0;
	}
	else
	{
		wake_up_interruptible(&button_waiq);//唤醒等待队列
		key = key_desc->val;//记录键值	
	}
	return 1;
}

static int button_init()
{
	int i = 0;
	cdev_init(&cdev, &button_fops);//初始化字符设备
	alloc_chrdev_region(&devno, 0, 6, "mykey");//动态分配设备号,6个次设备号
	cdev_add(&cdev, devno, 6);//设备注册

	for(; i < 6; i++)//注册中断
	{
		request_irq(key_desc[i].irq, button_function, IRQF_TRIGGER_FALLING, key_desc[i].name, &key_desc[i]);
	}

	gpncon = ioremap(0x7f008830, 4);
	gpndat = gpncon + 1;

	init_timer(&button_timer);
	button_timer.function = button_time_function;
	add_timer(&button_timer);

	return 0;
}

static void button_exit()
{
	int i = 0;
	del_timer(&button_timer);

//	iounmap(gpncon);
//	iounmap(gpndat);
	
	for(; i < 6; i++)//注销中断
	{
		free_irq(key_desc[i].irq, &key_desc[i]);
	}
	
	cdev_del(&cdev);//卸载设备
	unregister_chrdev_region(devno, 6);//释放设备号

}

module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");</span>
当我们执行insmod key.ko时,程序执行button_init()函数,首先注册设备,可以通过cat /proc/devices查看所分配的设备号,执行mknod /dev/mykey c 252 0创建设备节点,应用程序以文件操作的方式处理该节点。

以struct cdev *cdev方式创建的是指针,只是在全局区或栈分配指针的空间,需要为struct cdev动态分配空间,在内核里使用kmalloc和kfree。

使用ctags查看内核源码:安装yum install ctags,ctags -R递归生成该目录下的所有函数,vi -t function查找函数,crtl+]跳到光标所在函数源码,ctrl+t返回,shift+K跳到光标所在函数手册。

按键可以使用这些中断号,这些中断号是VIC0、VIC1所分配的64个中断号之外的中断号。

主要知识:

1.字符设备结构体

<span style="font-size:14px;">struct cdev
{
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
};</span>
<span style="font-size:14px;">void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
    memset(cdev, 0, sizeof *cdev); 
    INIT_LIST_HEAD(&cdev->list);
    kobject_init(&cdev->kobj, &ktype_cdev_default);
    cdev->ops = fops; 
}
</span>
cdev_init()为cdev结构体绑定操作函数fops

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name)
{
    struct char_device_struct *cd;
    cd = __register_chrdev_region(0, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    *dev = MKDEV(cd->major, cd->baseminor);
    return 0;
}
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
    struct char_device_struct *cd;
    dev_t to = from + count;
    dev_t n, next;

    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        if (next > to)
            next = to;
        cd = __register_chrdev_region(MAJOR(n), MINOR(n),
                   next - n, name);
        if (IS_ERR(cd))
            goto fail;
    }
    return 0;
fail:
    to = n;
    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
    }
    return PTR_ERR(cd);
}
alloc_chrdev_region动态分配设备号, register_chrdev_region 指定分配的设备号,两者最终都将调用 __register_chrdev_region
dev_t是一个32位的数 ,高12位表示主设备号,其余20位表示次设备号,可以使用MKDEV得到dev_t,也可以通过MAJOR(dev_t dev);和MINOR(dev_t dev);得到主次设备号
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;
    p->count = count;
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}

2.时钟

3.中断

key_app.c

<span style="font-size:14px;">#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>

int main(int argc, char **argv)
{
	int fd = open("/dev/mykey", O_RDWR);
	char buf[2];//用于采集信息
	while(1)
	{
		read(fd, buf, 1);
		printf("the key %d is on\n",(int)buf[0]);
	}
	return 0;
}</span>

Makefile

<span style="font-size:14px;">obj-m := key.o
KDIR := /home/f/Desktop/share/linux-ok6410/
all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
</span>

KDIR是编译目录,PWD是当前路径

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值