2.6内核字符设备驱动程序2

1794阅读 0评论2009-12-27 ubuntuer
分类:LINUX

从上到下,一个软件系统可以分为:应用程序, 库, 操作系统, 驱动程序。开发人员可以专注于自己熟悉的部分,对于相邻层,只需要了解它的接口,无需关注它的实现细节。这四层软件的协作关系如下:
      
应用程序    open  raed  write  ioctl ...

       
库        执行swi指令进入内核 

     

内核       系统调用的异常处理
       
      
驱动程序   open  read  write   ioctl  ...
                                 
                                 
硬件设备
          
字符设备的驱动开发是最基础的。对于学习嵌入式Linux的人而言,它是一个很好的入口。
字符设备是能够像字节流一样被访问的设备,就是说它的读写是以字节为单位的。
以下程序是基于2.6内核的字符设备驱动程序的例子,它创建了一个字符设备,我们也写了一个用户态下的程序去测试这个字符设备。



#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lan");

#define DP_MAJOR 251//主设备号
#define DP_MINOR 0  //次设备号

static int char_read(struct file *filp, char __user *buffer, size_t, loff_t*);
static int char_open(struct inode*, struct file*);
static int char_write(struct file *filp, const char __user *buffer, size_t, loff_t*);
static int char_release(struct inode*, struct file*);

static int chropen;
struct cdev *chardev;
static int len;

static char *to;
static const struct file_operations char_ops={
    .read = char_read,
    .write = char_write,
    .open = char_open,
    .release = char_release,
};

static int __init char_init(void)
{
    dev_t dev;
    printk(KERN_ALERT"Initing......\n");
    dev = MKDEV(DP_MAJOR, DP_MINOR);
    chardev = cdev_alloc();

    if(chardev == NULL){
        return -1;
    }
    if(register_chrdev_region(dev, 10, "chardev")){
        printk(KERN_ALERT"Register char dev error\n");
        return -1;
    }
    chropen = 0;
    len = 0;
    cdev_init(chardev, &char_ops);
    if(cdev_add(chardev, dev, 1)){
        printk(KERN_ALERT"Add char dev error!\n");
    }
    return 0;
}

static int char_open(struct inode *inode, struct file *file)
{
    if(chropen == 0)
        chropen++;
    else{
        printk(KERN_ALERT"Another process open the char device\n");
        return -1;
    }
    try_module_get(THIS_MODULE);
    return 0;
}

static int char_release(struct inode *inode,struct file *file)
{
    chropen--;
    module_put(THIS_MODULE);
    return 0;
}

static int char_read(struct file *filp,char __user *buffer,size_t length,loff_t *offset)
{
    unsigned long nn;
    nn = copy_to_user(buffer, to, length);
    printk("nn = %ld\n", nn);
    printk("buffer = %s\n", buffer);
    return length;
}

static int char_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset)
{
    unsigned long n;
    to = (char *)kmalloc((sizeof(char)) * (length+1), GFP_KERNEL);
    memset(to, '\0', length+1);
    n = copy_from_user(to, buffer, length);
    printk("n = %ld\n", n);
    printk("to = %s\n", to);
    return length;
}

static void __exit module_close(void)
{
        len=0;
        printk(KERN_ALERT"Unloading..........\n");

        unregister_chrdev_region(MKDEV(DP_MAJOR,DP_MINOR),10);
        cdev_del(chardev);
}

module_init(char_init);
module_exit(module_close);


Makefile代码如下:

obj-m += chardev.o
all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


使用make通过后我们将它插入内核:
#sudo insmod chardev.ko
在使用测试程序前我们要创建一个字符设备:
#mknod  /dev/chardev0  c  251  0
测试程序代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(int args, char *argv[])
{
    int testdev;
    int i, rf = 0;
    char buf[15];

    memset(buf, '\0', 15);
    testdev = open("/dev/chardev0", O_RDWR);
    if(testdev == -1){
        perror("open\n");
        exit(0);
    }
    if((write(testdev, "1111111111", 10)) < 0){
        perror("Write error!\n");
        exit(0);
    }
    close(testdev);
    printf("write finish!\n");
    testdev = open("/dev/chardev0", O_RDWR);    
    rf = read(testdev, buf, 3);
    if(rf < 0)
        perror("read error\n");
    printf("Read: %s\n", buf);
    close(testdev);
    
    return 0;
}


运行程序:
#sudo ./测试程序
Read: 111
看一下内核信息:
#dmesg
最后几行信息如下:
nn = 0
buffer = 1111111111
n = 0
to = 111
这就说明设备注册正确,从用户到内核copy_from_user和从内核到用户copy_to_user都顺利完成了。

上一篇:字符设备驱动程序1
下一篇:防止变量重复定义;头文件重复包含、嵌套包含