打印

字符设备gpio

[复制链接]
134|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
石头张|  楼主 | 2018-9-21 11:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
GPIO, LED, se, ev, ST
经过几个下班业余时间,终于完成了一个比较全面的字符设备了,(注释掉的为阻塞read方法)该字符设备结合了s3c6410的led和key,key为非中断驱动,接下来可以研究异步通知,中断和内核定时器了(这个在工作实际应用中比较常用,比如一个i2c设备如gsensor,不需要中断的时候(节约gpio口),可以结合内核定时器和工作队列来实现固定时间读取)。期待下期吧·······

要应用测试程序的请留言。

参考:linux设备驱动程序(第三版)

    linux设备驱动开发详解

file:gpio.h#ifndef _GPIO_H_

#define _GPIO_H_

  

#include <linux/cdev.h>

#include <linux/wait.h>

#include <linux/semaphore.h>

  

  

#define LED_MAJOR  0

#define DEVICE_NAME "leds"

  

#define GPKCON0 0

#define GPKCON1 1

#define GPKDAT  2

#define GPKPUD  3

#define MAX_NUM 6

struct led_dev{

    struct cdev cdev;

    struct semaphore sem;

    wait_queue_head_t w_wait;

    wait_queue_head_t r_wait;

    unsigned int* base;

    unsigned int key;

    unsigned char rflag;

    unsigned char wflag;

    unsigned int val;

};

  

#define BITSET(name, val)  do{name |= val;}while(0)

#define BITCSET(name, mask, val)  do{ name &= ~mask; \

                                      name |= val }while(0)

#define BITCLR(name, mask)    do{ name &= ~mask;}while(0)

  

#endif复制代码





file:gpio.c

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/slab.h>

#include <linux/device.h>

#include <mach/map.h>

#include <mach/regs-gpio.h>

#include <mach/gpio-bank-k.h>

#include <mach/gpio-bank-n.h>

#include <asm/io.h>

#include <asm/uaccess.h>

#include <linux/sched.h>

#include <linux/poll.h>

#include "gpio.h"

  

#define GPIO_DEBUG

#ifdef GPIO_DEBUG

#define dprintk(fmt, arg...)     printk("###GPIO DEBUG###%s():"fmt, __func__, ##arg)

#else

#define dprintk(fmt, arg...)

#endif

  

static int led_major = LED_MAJOR;

struct led_dev* led;

  

static int led_open(struct inode* inode, struct file* filp)

{

    dprintk("open!\n");

    filp->private_data = led;

    return 0;

}

  

static int led_close(struct inode* inode, struct file* filp)

{

    dprintk("close!\n");

    return 0;

}

  

static ssize_t led_read(struct file* filp, char __user* buff, size_t count, loff_t* offp)

{

    struct led_dev* led = filp->private_data;

    if(down_interruptible(&led->sem))

        return -ERESTARTSYS;

#if 0

    dprintk("led->rflag=%d, led->wflag=%d\n", led->rflag, led->wflag);

    while(!led->rflag){

        DEFINE_WAIT(wait);

        up(&led->sem);

        if(filp->f_flags & O_NONBLOCK)

            return -EAGAIN;

        dprintk("prepare to wait!\n");

        prepare_to_wait(&led->r_wait, &wait, TASK_INTERRUPTIBLE);

        if(led->rflag == 0)

            schedule();

        dprintk("finish wait!\n");

        finish_wait(&led->r_wait, &wait);

        if(signal_pending(current))

            return -ERESTARTSYS;

        if(down_interruptible(&led->sem))

            return -ERESTARTSYS;

    }

#endif

    if(count < 0 || count > 2)

        count = 1;

    led->key = readl(S3C64XX_GPNDAT);

    dprintk("led->key is 0x%x\n", led->key);    //led->key & 0x3f

    if(copy_to_user(buff, &led->key, count))

        return -EFAULT;

    led->rflag = 0;

    if((led->key & 0x3f) != 0x3f){

        led->wflag = 1;

        wake_up_interruptible(&led->w_wait);

    }

    up(&led->sem);

    return count;

}

  

static ssize_t led_write(struct file* filp, const char __user* buff, size_t count, loff_t* offp)

{

    struct led_dev* led = filp->private_data;

    DECLARE_WAITQUEUE(wait, current);

    if(down_interruptible(&led->sem))

        return -ERESTARTSYS;

    add_wait_queue(&led->w_wait, &wait);

    dprintk("led->rflag=%d, led->wflag=%d\n", led->rflag, led->wflag);

    while(!led->wflag){

        if(filp->f_flags & O_NONBLOCK){

            up(&led->sem);

            remove_wait_queue(&led->w_wait, &wait);

            return -EAGAIN;

        }

        __set_current_state(TASK_INTERRUPTIBLE);

        up(&led->sem);

        dprintk("into schedule!\n");

        schedule();

        dprintk("out schedule!\n");

        if(signal_pending(current)){

            up(&led->sem);

            remove_wait_queue(&led->w_wait, &wait);

            set_current_state(TASK_RUNNING);   

            return -ERESTARTSYS;

        }

        if(down_interruptible(&led->sem))

            return -ERESTARTSYS;

    }

    if(count < 0 || count > 2)

        count = 1;

    if(copy_from_user(&led->val, buff, count))

        return -EFAULT;

    dprintk("led->val=0x%x\n", led->val);

    writel(led->val << 4, led->base + GPKDAT);

    led->rflag = 1;

    led->wflag = 0;

    wake_up_interruptible(&led->r_wait);

    up(&led->sem);

    remove_wait_queue(&led->w_wait, &wait);

    set_current_state(TASK_RUNNING);   

    return count;

}

  

static long led_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)

{

    struct led_dev* led = filp->private_data;

    unsigned int tmp;

    switch(cmd){

        case 0:

        case 1:

            dprintk("cmd=%d, arg=%d\n", cmd, arg);

            if(down_interruptible(&led->sem))

                return -ERESTARTSYS;

  

            tmp = readl(led->base + GPKDAT);

            tmp &= ~(1 << (4 + arg));        //clear

            tmp |= ( (!cmd) << (4 + arg) );  //set

            writel(tmp, led->base + GPKDAT);

            up(&led->sem);

            break;

        default:

            break;

    }

    return 0;

}

  

static unsigned int led_poll(struct file* filp, poll_table* wait)

{

    unsigned int mask = 0;

    struct led_dev* led = filp->private_data;

    down(&led->sem);

    poll_wait(filp, &led->w_wait, wait);

    poll_wait(filp, &led->r_wait, wait);

    if(led->rflag)

        mask |= POLLIN | POLLRDNORM;

    if(led->wflag)

        mask |= POLLOUT | POLLWRNORM;

    up(&led->sem);

    return mask;

}

  

static const struct file_operations led_fops = {

    .open           = led_open,

    .release        = led_close,

    .read              = led_read,

    .write             = led_write,

    .unlocked_ioctl = led_ioctl,

    .poll            = led_poll,

};

  

static void led_setup_cdev(struct led_dev* dev, int index)

{

    int err, devno = MKDEV(led_major, 0);

    cdev_init(&dev->cdev, &led_fops);

    dev->cdev.owner = THIS_MODULE;

    err = cdev_add(&dev->cdev, devno, 1);

    if(err)

        printk("error add cdev!\n");

}

  

struct class* led_class;

int led_init(void)

{

    int result;

    unsigned tmp;

    dev_t devno = MKDEV(led_major, 0);

    if(led_major)

        result = register_chrdev_region(devno, 1, DEVICE_NAME);

    else{

        dprintk("alloc_chrdev_region\n");

        result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);

        led_major = MAJOR(devno);

    }

    if(result < 0)

        return result;

    led = kmalloc(sizeof(struct led_dev), GFP_KERNEL);

    if(!led){

        result = -ENOMEM;

        goto malloc_error;

    }

    memset(led, 0, sizeof(struct led_dev));

    led_setup_cdev(led, 0);

    init_MUTEX(&led->sem);

    init_waitqueue_head(&led->w_wait);

    init_waitqueue_head(&led->r_wait);

    led->rflag = 0;

    led->wflag = 0;

    dprintk("S3C64XX_GPK_BASE=0x%x\n", (unsigned int)S3C64XX_GPK_BASE);

    led->base = S3C64XX_GPK_BASE;

    led->val = 0xf << 4;

    dprintk("led->base=0x%x\n", (unsigned int)led->base);

   

    tmp = readl(led->base + GPKCON0);

    tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16);

    writel(tmp, led->base + GPKCON0);

    dprintk("led->base=0x%x\n", (unsigned int)(led->base + GPKDAT));

    dprintk("S3C64XX_GPKDAT=0x%x\n", (unsigned int)S3C64XX_GPKDAT);

    writel(led->val, led->base + GPKDAT);

  

    tmp = readl(S3C64XX_GPNCON);

    tmp &= ~0xfffU;

    writel(tmp, S3C64XX_GPNCON);

   

//    dprintk("key val is 0x%x\n", readl(S3C64XX_GPNDAT));

   

    led_class = class_create(THIS_MODULE, "led_class");

    device_create(led_class, NULL, MKDEV(led_major, 0), NULL, "%s", DEVICE_NAME);

    return 0;

malloc_error:

    unregister_chrdev_region(devno, 1);

    return result;

}

  

void led_exit(void)

{

    cdev_del(&led->cdev);

    device_destroy(led_class, MKDEV(led_major, 0));

    class_destroy(led_class);

    kfree(led);

    unregister_chrdev_region(MKDEV(led_major, 0), 1);

}

  

module_init(led_init);

module_exit(led_exit);

  

MODULE_LICENSE("GPL");

MODULE_AUTHOR("jf.s <jingfeng.shi@emdoor.com>");复制代码

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

446

主题

446

帖子

0

粉丝