Chinaunix首页 | 论坛 | 博客
  • 博客访问: 469110
  • 博文数量: 134
  • 博客积分: 3056
  • 博客等级: 中校
  • 技术积分: 1150
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-14 15:53
文章分类
文章存档

2013年(1)

2010年(133)

我的朋友

分类: LINUX

2010-07-29 15:50:07

1. 基础知识学习
1)的第三章-字符设备驱动程序
2)
深入浅出Linux设备驱动编程(宋宝华编著)的第三讲-字符设备驱动程序(网页版)

需要说明的是:在内核2.4中引入的devfs文件系统(其中有两篇文章值得推荐,一、devfs的介绍 ; 二、把内核2.2移植到内核2.4(如何使用devfs))。我的开发板SDK包里的busybox不能使用'mknod命令',所以,只能使用devfs

2. 简单的字符驱动源码 globalvar.c

这是参考深入浅出Linux设备驱动编程(宋宝华编著)
的第三讲-字符设备驱动程序上的源码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");

#define MAJOR_NUM 254 //主设备号


static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

//初始化字符设备驱动的file_operations结构体

struct file_operations globalvar_fops =
{
 read: globalvar_read, write: globalvar_write,
};
static int global_var = 0; //"globalvar"设备的全局变量


static int __init globalvar_init(void)
{
 int ret;

 //注册设备驱动

 ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
 if (ret)
 {
  printk("globalvar register failure");
 }
 else
 {
  printk("globalvar register success");
 }
 return ret;
}

static void __exit globalvar_exit(void)
{
 int ret;

 //注销设备驱动

 ret = unregister_chrdev(MAJOR_NUM, "globalvar");
 if (ret)
 {
  printk("globalvar unregister failure");
 }
 else
 {
  printk("globalvar unregister success");
 }
}

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
 //将global_var从内核空间复制到用户空间

 if (copy_to_user(buf, &global_var, sizeof(int)))
 {
  return - EFAULT;
 }
 return sizeof(int);
}

static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
 //将用户空间的数据复制到内核空间的global_var

 if (copy_from_user(&global_var, buf, sizeof(int)))
 {
  return - EFAULT;
 }
 return sizeof(int);
}

module_init(globalvar_init);
module_exit(globalvar_exit);


这是我修改能在我开发板上运行的驱动源码

#include <linux/init.h>        /* __init __exit */
#include <linux/module.h>    /* Needed by all modules */
#include <linux/kernel.h>    /* printk() */
#include <linux/config.h>    /*configuration generated when you configure your linux kernel source*/
#include <linux/fs.h>        /* file_operation */
#include <asm/uaccess.h>    /* copy_to_user, copy_from_user */
#include <linux/devfs_fs_kernel.h> /* devfs_mk_dir, devfs_register, devfs_regster_chrdev*/

/*
    KERN_EMERG        用于紧急事件,一般是系统崩溃前的提示信息
    KERN_ALERT        用于需要立即采取动作的场合
    KERN_CRIT        临界状态,通常设计验证的硬件或软件操作失败
    KERN_ERR        用于报告错误状态.设备驱动程序通常会用它报告来自硬件的问题
    KERN_WARNING    就可能出现的问题提出警告.这些问题通常不会对系统造成严重破坏
    KERN_NOTICE        有必要提示的正常情况.许多安全相关的情况用这个级别汇报
    KERN_INFO         提示性信息.有很多驱动程序在启动时用这个级别打印相关信息
    KERN_DEBUG        用于调试的信息
*/


MODULE_LICENSE("GPL");

#define MAJOR_NUM 249 //主设备号(在开发板终端输入命令'cat /proc/devices'查看,挑一个可用的即可)


static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

/*初始化字符设备驱动的file_operations结构体*/
struct file_operations globalvar_fops =
{
    read: globalvar_read,
    write: globalvar_write,
};

/* Module Init & Exit function */
static int global_var = 0; /*"globalvar"设备的全局变量*/
#ifdef CONFIG_DEVFS_FS
// devfs_handle_t devfs_myDriver_dir

devfs_handle_t devfs_myDriver_raw;
#endif
static int __init globalvar_init(void)
{
    int ret;
    
#ifdef CONFIG_DEVFS_FS
    ret = devfs_register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
    //devfs_myDriver_dir = devfs_mk_dir(NULL, "globalvar2", NULL);

    devfs_myDriver_raw = devfs_register(NULL,"globalvar", DEVFS_FL_DEFAULT, MAJOR_NUM, 0, S_IFCHR | S_IRUSR | S_IWUSR, &globalvar_fops, NULL);
    printk("add dev file to devfs OK!\n");
#else
    /*注册设备驱动*/
    ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
    if (ret)
    {
        printk("globalvar register failure");
    }
    else
    {
        printk("globalvar register success");
    }
#endif
    return ret;
}

static void __exit globalvar_exit(void)
{
    int ret;

#ifdef CONFIG_DEVFS_FS
    ret = devfs_unregister_chrdev(MAJOR_NUM, "globalvar");
    devfs_unregister(devfs_myDriver_raw);
    //devfs_unregister(devfs_myDriver_dir);

    printk("delete dev file from devs OK!\n");
#else
    /*注销设备驱动*/
    ret = unregister_chrdev(MAJOR_NUM, "globalvar");
    if (ret)
    {
        printk("globalvar unregister failure");
    }
    else
    {
        printk("globalvar unregister success");
    }
#endif

}

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
    /*将global_var从内核空间复制到用户空间*/
    if (copy_to_user(buf, &global_var, sizeof(int)))
    {
        return - EFAULT;
    }
    return sizeof(int);
}

static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t    *off)
{
    /*将用户空间的数据复制到内核空间的global_var*/
    if (copy_from_user(&global_var, buf, sizeof(int)))
    {
        return - EFAULT;
    }
    return sizeof(int);
}

module_init(globalvar_init);
module_exit(globalvar_exit);



3. 我的开发板上的busybox里有'tftp',所以,我先编译好内核驱动模块globalvar.o,通过tftp从我的PC机下载到目标板上。然后,通过'insmod globalvar.o'动态插入内核,以验证其有效性。而不用编译进内核,然后再烧录内核镜像到目标板。我的Makefile文件如下:


TARGET := globalvar
INCLUDE := -I/home/michael/work/project/org_source/linux-2.4.x/include/ #开发板内核源码所在目录

CFLAGS := -O2 -Wall -DMODULE -DUSEFIFO -D__KERNEL__ -DLINUX\
     -mno-abicalls -fno-pic -mips32 -DMODULE -mlong-calls -G 0
CC := /opt/buildroot-gdb/bin/mipsel-linux-gcc #交叉编译器所在位置

${TARGET}.o: ${TARGET}.c
    $(CC) $(CFLAGS) ${INCLUDE} -c ${TARGET}.c

install:
    $(shell cp ./${TARGET}.o /tftpboot) #我已经安装并启动tftp系统服务

clean:
    rm -rf *.o *~core .depend .*.cmd *.ko *.mod.c .tmp_versions


在当前目录下输入make,生成globalvar.o

4. 编写测试测序globalvar_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
main()
{
    int fd, num;
    //打开"/dev/globalvar"

    fd = open("/dev/globalvar", O_RDWR, S_IRUSR | S_IWUSR);
    if (fd != -1 )
    {
        //初次读globalvar

        read(fd, &num, sizeof(int));
        printf("The globalvar is %d\n", num);

        //写globalvar

        printf("Please input the num written to globalvar\n");
        scanf("%d", &num);
        write(fd, &num, sizeof(int));

        //再次读globalvar

        read(fd, &num, sizeof(int));
        printf("The globalvar is %d\n", num);

        //关闭"/dev/globalvar"

        close(fd);
    }
    else
    {
        printf("Device open failure\n");
    }
}


输入命令:

mipsel-linux-gcc -o globalvar_test globalvar_test.c


生成globalvar_test elf目标文件

5. 在目标板终端输入命令:

cd /tmp
tftp -gr globalvar.o 192.168.1.222 #我的主机IP
tftp -gr globalvar_test 192.168.1.222

insmod globalvar.o
#然后打印
add dev file to devfs

#运行globalvar_test
./globalvar_test
#输出
The globalvar is 0
Please input the num written to globalvar
#输入22
The globalvar is 22


6. globalvar.c还可以这么写

#include <linux/init.h>        /* __init __exit */
#include <linux/module.h>    
#include <linux/kernel.h>    /* printk() */
#include <linux/config.h>
#include <linux/fs.h>        /* file_operation */
#include <asm/uaccess.h>    /* copy_to_user, copy_from_user */
#include <linux/devfs_fs_kernel.h> /* devfs_mk_dir, */

MODULE_LICENSE("GPL");

#define MAJOR_NUM 250 //主设备号


static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);

/*初始化字符设备驱动的file_operations结构体*/
struct file_operations globalvar_fops =
{
    read: globalvar_read,
    write: globalvar_write,
};

/* Module Init & Exit function */
static int global_var = 0; /*"globalvar"设备的全局变量*/
#ifdef CONFIG_DEVFS_FS
devfs_handle_t devfs_myDriver_dir;
devfs_handle_t devfs_myDriver_raw;
#endif
static int __init globalvar_init(void)
{
    int ret;

    /*注册设备驱动*/
    ret = register_chrdev(MAJOR_NUM, "globalvar", &globalvar_fops);
    if (ret)
    {
        printk("globalvar register failure");
    }
    else
    {
        printk("globalvar register success");
    }
#ifdef CONFIG_DEVFS_FS
     devfs_myDriver_dir = devfs_mk_dir(NULL, "globalvar", NULL);
     devfs_myDriver_raw = devfs_register(devfs_myDriver_dir, "raw0", DEVFS_FL_DEFAULT, MAJOR_NUM, 0, S_IFCHR | S_IRUSR | S_IWUSR, &globalvar_fops, NULL);
     printk("add dev file to devfs OK!\n");
#endif
    return ret;
}

static void __exit globalvar_exit(void)
{
    int ret;

    /*注销设备驱动*/
    ret = unregister_chrdev(MAJOR_NUM, "globalvar");
    if (ret)
    {
        printk("globalvar unregister failure");
    }
    else
    {
        printk("globalvar unregister success");
    }

#ifdef CONFIG_DEVFS_FS
    devfs_unregister(devfs_myDriver_raw);
    devfs_unregister(devfs_myDriver_dir);
#endif
}

static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
    /*将global_var从内核空间复制到用户空间*/
    if (copy_to_user(buf, &global_var, sizeof(int)))
    {
        return - EFAULT;
    }
    return sizeof(int);
}

static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t    *off)
{
    /*将用户空间的数据复制到内核空间的global_var*/
    if (copy_from_user(&global_var, buf, sizeof(int)))
    {
        return - EFAULT;
    }
    return sizeof(int);
}

module_init(globalvar_init);
module_exit(globalvar_exit);


如果使用这种方式得驱动测试程序globalvar_test.c的这一行要这样改写

fd = open("/dev/globalvar/raw0", O_RDWR, S_IRUSR | S_IWUSR);


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