Chinaunix首页 | 论坛 | 博客
  • 博客访问: 859379
  • 博文数量: 133
  • 博客积分: 7117
  • 博客等级: 少将
  • 技术积分: 1846
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-16 21:30
文章分类

全部博文(133)

文章存档

2012年(1)

2011年(4)

2010年(2)

2009年(57)

2008年(69)

分类: LINUX

2008-09-25 17:03:26

首先你要有一份2.6内核的源码,然后编译并运行它。
在已运行了你编译的内核的系统里编译此驱动程序: $> make
编译成功后,以root身份运行加载脚本: $> ./chardev_load.sh
一切完成后,就可以测试你的驱动程序了。
此驱动程序的输出全部写到了 /var/log/debug 文件中,你可以如下查看:
$> tail -f /var/log/debug
我用的是 Linux debian 2.6.18.3,一切运行良好。
/*

   *  chardev.c  kf701  2006/12/05

   *  /dev/kf701_chardev0  /proc/devices

 */

 

/*  init.h中定义了宏module_init(x)module_exit(x)

* 'kernel.h' contains some often-used function prototypes etc

* module.h :Dynamic loading of modules into the kernel.包括try_module_get, module_put

*fs.h包括struct file_operations的定义

*uaccess.h包括User space memory access functions,put_user,get user

*cdev.h包括字符设备结构struct cdev定义

*/

#include  

#include

#include

#include

#include

#include

 

 

static int      chardev_open(struct inode *, struct file *);

static int      chardev_release(struct inode *, struct file *);

static ssize_t  chardev_read(struct file *, char __user *, size_t, loff_t *);

static ssize_t  chardev_write(struct file *, const char __user *, size_t, loff_t *);

#define SUCCESS 0

#define DEVICE  "kf701_chardev"

int major = 233;

int minor = 0;

struct file_operations fops = {

    .owner =        THIS_MODULE,   //THIS_MODULE包含在module.h当中,是一个宏

    .read =         chardev_read,

    .write =        chardev_write,

    .open =         chardev_open,

    .release =      chardev_release,

};

struct my_dev{

    uint8_t *buf;

    uint32_t size;

    uint32_t index;

    struct semaphore sem;

    struct cdev cdev;

} kf701_dev;

 

/*

__init__exit标记函数,__initdata__exitdata标记数据。此宏定义可知标记后的函数与数据其实是放到了特定的(代码或数据)段中。标记为初始化的函数表明公在初始化期间使用。在模块装载之后,模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。__exit修饰词标记函数只在模块卸载时使用。如果模块被直接编进内核或内核不允许卸载模块。被此标记的函数将被简单地丢弃。

*/

static int __init chardev_init(void){

/*1. 分配主设备号,MKDEVKdev_t.h中定义的一个宏

Types.h中有:typedef __kernel_dev_t dev_t ;  dev_t unsigned int类型

Coda.h中有:typedef u_long dev_t

   */

dev_t devno = MKDEV( major, minor );

  

//fs.h中有语句:extern int register_chrdev_region(dev_t, unsigned, char *);

//但不知register_chrdev_region具体是在什么地方定义实现的?

 int ret = register_chrdev_region( devno, 1, DEVICE );

    if( ret < 0 )

{

 /*

kernel.h中:#define  KERN_DEBUG "<7>"  /* debug-level messages 

spinlock.h中定义:asmlinkage int printk(const char * fmt, ...)

      __attribute__ ((format (printf, 1, 2)));   

Modpost.h中有下面的声明,modpost.c中有其定义:

void __attribute__((format(printf, 2, 3)))

buf_printf(struct buffer *buf, const char *fmt, ...);    

*/

printk(KERN_DEBUG "register major number failed with %d\n", ret);

        return ret;

    }

    printk(KERN_DEBUG "%s:register major number OK\n",DEVICE);

    // 2. 注册设备

    cdev_init( &kf701_dev.cdev, &fops );

    kf701_dev.cdev.ops = &fops;

    kf701_dev.cdev.owner = THIS_MODULE;

    ret = cdev_add( &kf701_dev.cdev, devno, 1 );

    if( ret < 0 )

    {

    printk(KERN_DEBUG "register device failed with %d\n", ret);

    return ret;

    }

    printk(KERN_DEBUG "%s:register device OK\n",DEVICE);

    // 3. 分配本驱动要使用的内存

    kf701_dev.index = 0;

    kf701_dev.size = 1024;

    kf701_dev.buf = kmalloc( kf701_dev.size, GFP_KERNEL );

    if( NULL == kf701_dev.buf )

    {

    printk(KERN_DEBUG "kmalloc failed\n");

    return -ENOMEM;

    }

    printk(KERN_DEBUG "%s:kmalloc buffer OK\n",DEVICE);

    // 初始化信号量

    init_MUTEX( &(kf701_dev.sem) );

    printk(KERN_DEBUG "%s:init semaphore OK\n",DEVICE);

    return SUCCESS;

    }

static void __exit chardev_exit(void)

    {

    dev_t devno = MKDEV( major, minor );

    cdev_del( &kf701_dev.cdev );

    unregister_chrdev_region( devno, 1 );

    printk(KERN_DEBUG "%s:unregister device OK\n",DEVICE);

}

 

/*kernel.h中定义: #define container_of(ptr, type, member) ({                    \

        const typeof( ((type *)0)->member ) *__mptr = (ptr);  \

        (type *)( (char *)__mptr - offsetof(type,member) );})

  container_of是给定结构体某个成员的地址反推其结构体地址。ptr是一个member的地址

#define list_entry(ptr, type, member) \
 
 container_of(ptr, type, member)

 container_of是个宏,定义如下:

#define container_of(ptr, type, member) ({   \
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \      // typeof
gcc扩展,用于         得知数据类型的,这句话作用大概是看ptr是否是结构体里成

//员变量member的类型,不是编译时将报错,类型检测的
        (type *)( (char *)__mptr - offsetof(type,member) );})      

offsetof是计算成员member在结构体的type里面的偏移数,__mptr的地址减去偏移地址就可以得到type结构体的开始地址

 

*/

 

int chardev_open(struct inode *inode, struct file *file)

    {

    struct my_dev *dev = container_of(inode->i_cdev, struct my_dev, cdev);

     file->private_data = dev;

     printk(KERN_DEBUG "%s:open OK\n",DEVICE);

     return SUCCESS;

     }

int chardev_release(struct inode *inode, struct file *file)

{

    printk(KERN_DEBUG "%s:release OK\n",DEVICE);

    return SUCCESS;

}

ssize_t chardev_read(struct file *file, char __user *buf, size_t count,loff_t *offset)

{

    struct my_dev *dev = file->private_data;

    ssize_t retval = 0;

    if( down_interruptible(&dev->sem) )

    return -ERESTARTSYS;

    printk(KERN_DEBUG "%s:read down sem OK\n",DEVICE);

    if( 0 == dev->index )

    goto out;

    if( *offset >= dev->index )

    goto out;

    if( (*offset+count) > dev->index )

    count = dev->index - *offset;

    if( copy_to_user(buf, dev->buf + *offset, count) )

    {

       retval = -EFAULT;

       goto out;

    }

    *offset += count;

    retval = count;

 out:

    up(&dev->sem);

    printk(KERN_DEBUG "%s:read up sem OK\n",DEVICE);

    return retval;

    }

    //  omit offset argument for write

    ssize_t chardev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)

    {

     struct my_dev *dev = file->private_data;

     ssize_t retval = -ENOMEM;

     if( down_interruptible(&dev->sem) )

        return -ERESTARTSYS;

     printk(KERN_DEBUG "%s:write down sem OK\n",DEVICE);

    if( count > dev->size )

    goto out;

    if( copy_from_user(dev->buf, buf, count) )

    {

    retval = -EFAULT;

   goto out;

    }

    dev->index = count;

    retval = count;

 out:

    up(&dev->sem);

    printk(KERN_DEBUG "%s:write up sem OK\n",DEVICE);

    return retval;

    }

    module_init(chardev_init);

    module_exit(chardev_exit);

    MODULE_AUTHOR("kf701.ye at gmail.com");

    MODULE_DESCRIPTION("Study for kf701");

    MODULE_SUPPORTED_DEVICE(DEVICE);

    MODULE_LICENSE("GPL");
下面是Makefile内容:
 
  
   ###
# study
    # kf_701
   # 2006-12-4
    ###
  
 #obj-m += hello.o
    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

加载脚本代码如下:

     1  #!/bin/sh
     2
     3  module="chardev"
     4  device="kf701_chardev"
     5  mode=666
     6
     7  echo "insmod now"
     8  /sbin/insmod ./$module.ko || exit 1
     9
    10  rm -f /dev/${device}0
    11
    12  major=$(awk "{ if(\$2 == \"${device}\") print \$1;}" /proc/devices)
    13
    14  echo "mknod now"
    15  mknod /dev/${device}0 c $major 0
    16
    17  group="staff"
    18  grep -q '^staff:' /etc/group || group="wheel"
    19
    20  echo "chgrp now"
    21  chgrp $group /dev/${device}0
    22  chmod $mode /dev/${device}0
    23

下面是读测试方法:
$> cat /dev/kf701_chardev0

写测试如下命令:

$> echo "only for test" > /dev/kf701_chardev0

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

chinaunix网友2009-06-03 13:39:36

呵呵,非常感谢!!!

chinaunix网友2009-05-10 20:12:52

介绍的很详细,不过有点泛,假如要研究kernel里某一特定字符驱动,该如何呢?