您的位置:首页 > 产品设计 > UI/UE

字符设备驱动之Buttons-中断上下部(工作队列-workqueue)

2011-09-16 19:15 399 查看
buttons.c

#include <linux/fs.h>

#include <linux/module.h>

#include <linux/errno.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/cdev.h>

#include <linux/ioport.h>

#include <linux/pci.h>

#include <asm/uaccess.h>

#include <asm/io.h>

#include <asm/irq.h>

#include <linux/interrupt.h>

#include <asm/mach/irq.h>

#include <asm/arch/regs-gpio.h>

#include <linux/poll.h>

#include <linux/delay.h> /* for udelay() */

#include <linux/time.h>

#include <asm/delay.h> /* for udelay() */

static int major = 0;

static struct class *cls;

/* gpecon 0x56000040 */

/* gpfcon 0x56000050 */

/* gpgcon 0x56000060 */

static volatile unsigned long *gpecon;

static volatile unsigned long *gpedat;

static volatile unsigned long *gpfcon;

static volatile unsigned long *gpfdat;

static volatile unsigned long *gpgcon;

static volatile unsigned long *gpgdat;

struct key_desc {

int irq;

int pin;

char *name;

char key_val;

int irq_pin_val;

};

struct key_desc key_desc[] = {

{IRQ_EINT0, S3C2410_GPF0, "K10", 1}, /* 松开: 1, 按下: 0x81 */

{IRQ_EINT2, S3C2410_GPF2, "K7", 2}, /* 松开: 2, 按下: 0x82 */

{IRQ_EINT11, S3C2410_GPG3, "K4", 3}, /* 松开: 3, 按下: 0x83 */

{IRQ_EINT19, S3C2410_GPG11, "K1", 4}, /* 松开: 4, 按下: 0x84 */

};

volatile char key = 0;

static wait_queue_head_t button_waitq;

#define BUF_LEN 10

static char key_buf[BUF_LEN];

static volatile int r = 0, w = 0;

struct fasync_struct *buttons_async;

static struct timer_list buttons_timer;

struct key_desc *cur_kd;

static int isEmpty(void)

{

return (r == w);

}

static int isFull(void)

{

return (r == ((w+1)%BUF_LEN));

}

static int putData(char val)

{

if (isFull())

{

return -1;

}

else

{

key_buf[w] = val;

w = (w+1)%BUF_LEN;

return 0;

}

}

static int getData(char *p)

{

if (isEmpty())

{

return -1;

}

else

{

*p = key_buf[r];

r = (r+1)%BUF_LEN;

return 0;

}

}

/*内核会在适当时候来调用这个处理函数*/

void buttons_work_func(struct work_struct *work)

{

/*具体事情,根据实际情况去定*/

int i;

static int cnt = 0;

int pre_jiffies, after_jiffies;

printk("enter buttons_work_func: %d\n", cnt++);

printk("task %s pid = %d\n", current->comm, current->pid);

pre_jiffies = jiffies;

for (i = 0; i < 10000; i++)

udelay(1000); /* 10 s*/

//printk("after jiffies = %d\n", jiffies);

after_jiffies = jiffies;

printk("two jiffies = %d, %d, sub: %d\n", pre_jiffies, after_jiffies, after_jiffies-pre_jiffies);

}

/*定义一个工作队列*/

static struct work_struct buttons_work;

static irqreturn_t buttons_irq(int irq, void *dev_id)

{

/* 执行上半部 */

static int cnt = 0;

cur_kd = (struct key_desc *)dev_id;

cur_kd->irq_pin_val = s3c2410_gpio_getpin(cur_kd->pin);

printk("enter buttons_irq: %d\n", cnt++);

mod_timer(&buttons_timer, jiffies+5);

/*加载到内和链表,告诉内核*/

schedule_work(&buttons_work);

return IRQ_HANDLED;

}

static void buttons_timer_function(unsigned long data)

{

/* 确定按键: 哪个按键,按下还是松开 */

// for (i = 0; i < 10000; i++); /* 浪费CPU */

/* 启动一个定时器:

* 两要素:

* 1. 时间

* 2. 处理函数

*/

char key;

int up;

if (!cur_kd)

{

return;

}

up = s3c2410_gpio_getpin(cur_kd->pin); // gpfdat, gpgdat

if (up != cur_kd->irq_pin_val)

{

return;

}

if (up)

{

key = cur_kd->key_val;

}

else

{

key = cur_kd->key_val | 0x80;

}

// printk("key = 0x%x\n", key);

putData(key);

/* 唤醒应用程序 */

wake_up_interruptible(&button_waitq);

kill_fasync(&buttons_async, SIGIO, POLL_IN);

//printk("buttons_irq current %s , pid = %d \n", current->comm, current->pid);

}

//static atomic_t button_can_open = ATOMIC_INIT(1);

static DECLARE_MUTEX(button_sem);

int buttons_open(struct inode *inode, struct file *file)

{

int i;

unsigned long flags;

if (file->f_flags & O_NONBLOCK)

{

if (down_trylock(&button_sem))

return -EBUSY;

}

else

{

down(&button_sem);

}

/* 如果之前没有程序获取信号量, 则成功

* 否则, 休眠

*/

/* 注册中断 */

for (i = 0; i < 4; i++)

{

request_irq(key_desc[i].irq, buttons_irq,

IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,

key_desc[i].name, &key_desc[i]);

}

/* 设置GPIO为中断引脚

* 设置触发方式

* 使能中断

*/

/* 设置KSCAN0(GPE11)为输出引脚,输出0 */

*gpecon &= ~(0x3 << 22);

*gpecon |= (1 << 22);

*gpedat &= ~(1<<11);

return 0;

}

ssize_t buttons_read(struct file *file, char __user *buf, size_t size, loff_t *offset)

{

/* 如果没有按键发生, 休眠 */

/* key 等于 0, 才会休眠

* key 非0, 不会休眠

*/

// command

//printk("current %s , pid = %d before sleep\n", current->comm, current->pid);

char key;

if (isEmpty() && (file->f_flags & O_NONBLOCK))

return -EAGAIN;

wait_event_interruptible(button_waitq, !isEmpty());

//printk("current %s , pid = %d after sleep\n", current->comm, current->pid);

/* 被唤醒后, 把按键值返回给用户程序 */

getData(&key);

copy_to_user(buf, &key, 1);

/* 发生了中断, key=xxx */

//key = 0;

return 1;

}

int buttons_close(struct inode *inode, struct file *file)

{

int i;

for (i = 0; i < 4; i++)

{

free_irq(key_desc[i].irq, &key_desc[i]);

}

/* 释放信号量, 并唤醒在等待这个信号量的进程 */

up(&button_sem);

return 0;

}

static unsigned int buttons_poll(struct file *file, struct poll_table_struct *wait)

{

static int cnt = 0;

printk("buttons_poll cnt = %d\n", cnt++);

poll_wait(file, &button_waitq, wait); /* 不会休眠, 只是挂入队列 */

return isEmpty()? 0 : POLLIN | POLLRDNORM;

}

static int buttons_fasync(int fd, struct file *filp, int on)

{

int retval;

retval = fasync_helper(fd, filp, on, &buttons_async);

if (retval < 0)

return retval;

return 0;

}

static const struct file_operations buttons_fops = {

.owner = THIS_MODULE,

.read = buttons_read,

.open = buttons_open, /* 设置引脚,申请资源 */

.release = buttons_close,

.poll = buttons_poll,

.fasync = buttons_fasync,

};

int buttons_init(void)

{

int i;

major = register_chrdev(0, "buttons", &buttons_fops);

/* sysfs ==> 挂接到/sys */

cls = class_create(THIS_MODULE, "buttons_class");

class_device_create(cls, NULL, MKDEV(major, 0), NULL, "buttons");

// mdev会根据/sys下的这些内容创建/dev/buttons

gpecon = ioremap(0x56000040, 4096);

gpedat = gpecon + 1;

gpfcon = gpecon + 4;

gpfdat = gpfcon + 1;

gpgcon = gpecon + 8;

gpgdat = gpgcon + 1;

init_waitqueue_head(&button_waitq);

init_timer(&buttons_timer);

buttons_timer.function = buttons_timer_function;

buttons_timer.expires = 0;

add_timer(&buttons_timer);

/*初始化我们的工作队列*/

INIT_WORK(&buttons_work,
buttons_work_func);

return 0;

}

void buttons_exit(void)

{

unregister_chrdev(major, "buttons");

class_device_destroy(cls, MKDEV(major, 0));

class_destroy(cls);

iounmap(gpecon);

del_timer(&buttons_timer);

}

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: