经过几个下班业余时间,终于完成了一个比较全面的字符设备了,(注释掉的为阻塞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 &quot;leds&quot; 
 
   
 
#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 &quot;gpio.h&quot; 
 
   
 
#define GPIO_DEBUG 
 
#ifdef GPIO_DEBUG 
 
#define dprintk(fmt, arg...)     printk(&quot;###GPIO DEBUG###%s():&quot;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(&quot;open!\n&quot;); 
 
    filp->private_data = led; 
 
    return 0; 
 
} 
 
   
 
static int led_close(struct inode* inode, struct file* filp) 
 
{ 
 
    dprintk(&quot;close!\n&quot;); 
 
    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(&quot;led->rflag=%d, led->wflag=%d\n&quot;, led->rflag, led->wflag); 
 
    while(!led->rflag){ 
 
        DEFINE_WAIT(wait); 
 
        up(&led->sem); 
 
        if(filp->f_flags & O_NONBLOCK) 
 
            return -EAGAIN; 
 
        dprintk(&quot;prepare to wait!\n&quot;); 
 
        prepare_to_wait(&led->r_wait, &wait, TASK_INTERRUPTIBLE); 
 
        if(led->rflag == 0) 
 
            schedule(); 
 
        dprintk(&quot;finish wait!\n&quot;); 
 
        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(&quot;led->key is 0x%x\n&quot;, 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(&quot;led->rflag=%d, led->wflag=%d\n&quot;, 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(&quot;into schedule!\n&quot;); 
 
        schedule(); 
 
        dprintk(&quot;out schedule!\n&quot;); 
 
        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(&quot;led->val=0x%x\n&quot;, 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(&quot;cmd=%d, arg=%d\n&quot;, 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(&quot;error add cdev!\n&quot;); 
 
} 
 
   
 
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(&quot;alloc_chrdev_region\n&quot;); 
 
        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(&quot;S3C64XX_GPK_BASE=0x%x\n&quot;, (unsigned int)S3C64XX_GPK_BASE); 
 
    led->base = S3C64XX_GPK_BASE; 
 
    led->val = 0xf << 4; 
 
    dprintk(&quot;led->base=0x%x\n&quot;, (unsigned int)led->base); 
 
    
 
    tmp = readl(led->base + GPKCON0); 
 
    tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16); 
 
    writel(tmp, led->base + GPKCON0); 
 
    dprintk(&quot;led->base=0x%x\n&quot;, (unsigned int)(led->base + GPKDAT)); 
 
    dprintk(&quot;S3C64XX_GPKDAT=0x%x\n&quot;, (unsigned int)S3C64XX_GPKDAT); 
 
    writel(led->val, led->base + GPKDAT); 
 
   
 
    tmp = readl(S3C64XX_GPNCON); 
 
    tmp &= ~0xfffU; 
 
    writel(tmp, S3C64XX_GPNCON); 
 
    
 
//    dprintk(&quot;key val is 0x%x\n&quot;, readl(S3C64XX_GPNDAT)); 
 
    
 
    led_class = class_create(THIS_MODULE, &quot;led_class&quot;); 
 
    device_create(led_class, NULL, MKDEV(led_major, 0), NULL, &quot;%s&quot;, 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(&quot;GPL&quot;); 
 
MODULE_AUTHOR(&quot;jf.s <jingfeng.shi@emdoor.com>&quot;);复制代码 |   
     
  
 |