Chinaunix首页 | 论坛 | 博客
  • 博客访问: 389320
  • 博文数量: 46
  • 博客积分: 1476
  • 博客等级: 上尉
  • 技术积分: 428
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-19 13:17
文章分类

全部博文(46)

文章存档

2015年(8)

2014年(20)

2013年(7)

2012年(4)

2011年(1)

2010年(2)

2009年(1)

2008年(2)

2006年(1)

我的朋友

分类: LINUX

2009-04-12 12:53:42

/*
 * 2009-04-12
 * kernel version: 2.6.28.8
 */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

MODULE_AUTHOR("sch");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("s3c2410 buttons driver");

#define DEV_COUNT         1
#define DEV_NAME         "s3c2410_buttons"

#define MAX_BUF_LEN        16
#define SIZE            MAX_BUF_LEN

#define INCBUF(x)        (++(x)%(MAX_BUF_LEN))
#define DECBUF(x)        (--(x)%(MAX_BUF_LEN))

struct key_info {  
    unsigned int irq_no;  
    unsigned int gpio_port;  
    int key_no;  
};

struct s3c2410_buttons
{
    unsigned int         flag;
    unsigned char         buf[MAX_BUF_LEN];
    unsigned int         head, tail;
    wait_queue_head_t     wq;
    struct cdev         buttons_cdev;
};

static int minor_num = 0;
static int major_num = 0;

static struct key_info key_info_tab[4] = {
    { IRQ_EINT0,  S3C2410_GPF0,  KEY_3 },
    { IRQ_EINT2,  S3C2410_GPF2,  KEY_2 },
    { IRQ_EINT11, S3C2410_GPG3,  KEY_1 },
    { IRQ_EINT19, S3C2410_GPG11, KEY_ESC },
};

static struct s3c2410_buttons buttons_device;

static struct class *buttons_class;

static irqreturn_t buttons_irq_isr(int irq, void *dev_id);    //中断服务程序

//static void do_task_irq(unsigned long dummy);                //任务队列的执行函数
//DECLARE_TASKLET(task_irq, do_task_irq, 0);                //中断服务下半部分的任务队列

int buttons_open(struct inode *inode, struct file *filp)
{
    try_module_get(THIS_MODULE);        //表示此驱动被使用,记数器增加
   
    return 0;
}

int buttons_release(struct inode *inode,struct file *filp)
{
    module_put(THIS_MODULE);                    //表示此驱动使用完毕,记数器减一

    return 0;
}

ssize_t buttons_read(struct file *filp, char __user *buf, size_t count, loff_t *f_ops)
{
    int err;
    char tmp;
   
retry:
    if (buttons_device.head != buttons_device.tail) {
        tmp = buttons_device.buf[buttons_device.tail];
        err = copy_to_user(buf, &tmp, 1);
        //printk(KERN_DEBUG "buttons_device.buf=0x%x\n", buttons_device.buf[buttons_device.tail]);
        if (err != 0) {
            printk(KERN_ERR "buttons_read copy_to_user error!!\n");
            return 0;
        }
        buttons_device.tail = INCBUF(buttons_device.tail);
        return 1;
    }
    else {
        if (filp->f_flags & O_NONBLOCK) {
            return -EAGAIN;
        }
        wait_event_interruptible(buttons_device.wq, buttons_device.flag);
        buttons_device.flag = 0;
        if (signal_pending(current)) {
            return -ERESTARTSYS;
        }
        goto retry;
    }

    return 1;
}

ssize_t buttons_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_ops)
{
    int err;
   
    memset(buttons_device.buf, 0, sizeof(char)*SIZE);
    err = copy_from_user(buttons_device.buf, buf, count);
    if (err != 0) {
        printk(KERN_DEBUG "buttons_write copy_from_user error !\n");
        return 0;
    }
   
    return count;
}

int buttons_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
    return 1;
}

static irqreturn_t buttons_irq_isr(int irq, void *dev_id)
{  
    int i;
   
    //tasklet_schedule(&task_irq);            //任务调度中断下半部分服务程序
   
    for (i=0; i<4; i++) {
        if (key_info_tab[i].irq_no == irq) {
            buttons_device.buf[buttons_device.head] = key_info_tab[i].key_no;
            buttons_device.head = INCBUF(buttons_device.head);
            buttons_device.flag = 1;
            wake_up_interruptible(&(buttons_device.wq));
            break;           
        }
    }
 
    return IRQ_HANDLED;                        //返回中断已经获得
}

/*
static void do_task_irq(unsigned long dummy)
{
    buttons_device.buf[buttons_device.head] = 'T';
   
    printk(KERN_DEBUG "buttons_device.head=%d\n", buttons_device.head);
    printk(KERN_DEBUG "buttons_device.buf=0x%x\n", buttons_device.buf[buttons_device.head]);
   
    buttons_device.head = INCBUF(buttons_device.head);
    buttons_device.flag = 1;
    wake_up_interruptible(&(buttons_device.wq));
}
*/

struct file_operations buttons_fops =
{
    .owner        = THIS_MODULE,
    .read        = buttons_read,
    .write        = buttons_write,
    .open        = buttons_open,
    .release    = buttons_release,
    .ioctl        = buttons_ioctl,
};

static int __init buttons_init(void)
{
    dev_t dev_num = 0;
    int res;
    int err;
    int i;

    //分配设备号
    if (major_num) {    //手动分配设备号
        dev_num = MKDEV(major_num, minor_num);
        res = register_chrdev_region(dev_num, DEV_COUNT, DEV_NAME);
    }
    else {                //动态分配设备号
        res = alloc_chrdev_region(&dev_num, minor_num, DEV_COUNT, DEV_NAME);
        major_num = MAJOR(dev_num);
    }

    if (res < 0) {
        printk(KERN_ERR "alloc_chrdev_region error !\n");
        return -1;
    }
      
    //申请中断
    for (i=0; i<4; i++) {
        set_irq_type(key_info_tab[i].irq_no, IRQT_FALLING);            //下降沿触发
        res = request_irq(key_info_tab[i].irq_no, &buttons_irq_isr, IRQF_SAMPLE_RANDOM, DEV_NAME, NULL);
        if (res < 0) {
            printk(KERN_ERR "request_irq error_num= %d !\n", res);
            return -1;
        }
    }
  
    //设备初始化
    memset(&buttons_device, 0, sizeof(struct device));
    memset(buttons_device.buf, 0, sizeof(unsigned char)*SIZE);
    buttons_device.flag = 0;
    init_waitqueue_head(&(buttons_device.wq));            //初始化队列
  
    //注册设备
    cdev_init(&(buttons_device.buttons_cdev), &buttons_fops);//cdev 结构嵌入一个自己的结构体需初始化
    buttons_device.buttons_cdev.owner = THIS_MODULE;
    buttons_device.buttons_cdev.ops = &buttons_fops;
    err = cdev_add(&(buttons_device.buttons_cdev), dev_num, 1);
    if (err != 0) {
        printk(KERN_ERR "IRQ cdev_add error!!\n");
    }

    /* Populate sysfs entries */
    buttons_class = class_create(THIS_MODULE, "irq_class");
    /* Send uevents to udev, so it'll create /dev node */
    device_create(buttons_class, NULL, dev_num, NULL, DEV_NAME);
  
    printk(KERN_INFO "registered the %s success\n", DEV_NAME); 
   
    return 0;
}

static void __exit buttons_exit(void)
{
    int i;
    dev_t dev_num;
   
    dev_num = MKDEV(major_num, minor_num);
    cdev_del(&(buttons_device.buttons_cdev));
   
    device_destroy(buttons_class, dev_num);
    class_destroy(buttons_class);
   
    for (i=0; i<4; i++)
        free_irq(key_info_tab[i].irq_no, NULL);
   
    unregister_chrdev_region(dev_num, 1);

    printk(KERN_INFO "unregistered the %s\n", DEV_NAME); 
   
    return;
}

module_init(buttons_init);
module_exit(buttons_exit);


/* arm-linux-gcc test1.c -o test1 */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int main(void)
{
    int buttons_fd;
    char key_value;

    buttons_fd = open("/dev/s3c2410_buttons", 0);
    if (buttons_fd < 0) {
        perror("open device buttons");
        exit(1);
    }

    for (;;) {
        fd_set rds;
        int ret;

        FD_ZERO(&rds);
        FD_SET(buttons_fd, &rds);

        ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);
        if (ret < 0) {
            perror("select");
            exit(1);
        }
       
        if (ret == 0) {
            printf("Timeout.\n");
        }
        else if (FD_ISSET(buttons_fd, &rds)) {
            int ret = read(buttons_fd, &key_value, sizeof key_value);
            if (ret != sizeof key_value) {
                if (errno != EAGAIN)
                    perror("read buttons\n");
                continue;
            }
            else {
                printf("buttons_value: %d\n", key_value);
            }
        }
    }

    close(buttons_fd);
    return 0;
}

 
阅读(1137) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~