Chinaunix首页 | 论坛 | 博客
  • 博客访问: 96744
  • 博文数量: 23
  • 博客积分: 516
  • 博客等级: 中士
  • 技术积分: 315
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-29 16:45
文章分类
文章存档

2012年(11)

2011年(12)

分类: LINUX

2011-10-29 18:12:50

一、设备号的相关知识
 
1、设备号的作用
   主设备号用来标识与设备文件相连的驱动程序。次设备号用来辨别操作的是哪个设备。
   主设备号用来反映设备类型,次设备号用来区分同类型的设备。
 
2、设备号处理的相关用法
   A、dev_t
    内核中使用dev_t来描述一个设备号,其实质为unsigned int 32位整数,其中高12位位主设备  号,低20位为次设备号。
 
    B、主次设备号的提取
    分别使用MAJOR(dev_t dev)与MINOR(dev_t dev)分解出主设备号与次设备号。
  
    C、设备号的申请
     静态申请
     方法:int register_chrdev_region(dev_t from,unsigned count,const char *name)     
     功能:申请使用从from开始的count个设备号(主设备号不变,次设备号增加)
     参数:from:希望申请使用的设备号 count:希望申请使用的设备号数目 name:设备名(体现在proc/devices)
 
     动态分配
     方法:int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)
     功能:请求内核动态分配count个设备号,且此设备号从baseminor开始。
     参数:dev:分配到的设备号 baseminor:起始此设备号 count:需要分配的设备号数目 name:设备名    
    
     D、设备号的注销
     方法:void unregister_chrdev_region(dev_t from,unsigned count)
     功能:释放从from开始的count个设备号
 
 
二、重要的字符设备驱动结构
     1、struct file
     功能:代表一个打开的文件。系统中每个打开的文件在内核空间都有一个关联的struct file它由内核在打开文件时创建,文件关闭后释放。一个文件可以对应多个file结构。
     重要成员:
     loff_t f_pos ;/*表示文件读写位置*/ 
     struct file_operations *f_op
 
      2、struct inode
      功能:用来记录文件的物理上得信息。一个文件只有一个inode结构。
      重要成员:dev_t i_rdev:设备号
 
      3、struct file_operations
      功能:一个函数指针的集合,定义能再设备上进行的操作。结构中德成员指向驱动中德函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL。
       格式:
       Struct file_operations mem_fops={
              .owner = THIS_MODULE,
              .llseek = xx_seek,
              .read = xx_read,
              .write = xx_write,
              .ioctl = xx_ioctl,
              .open = xx_open,
              .release = xx_release, 
       }
 
三、字符设备注册     
    1、在linux 2.6内核中,字符设备使用struct cdev来描述。
    2、步骤:
       A、分配cdev
          方法:struct cdev *cdev_alloc(void)
       B、初始化cdev
          方法:void cdev_init(struct cdev *cdev,counst struct file_operatings *fops)
       C、添加cdev
          方法:int cdev_add(struct cdev *p,dev_t dev,unsigned cont)
          参数:
          p:待添加到内核的字符设备结构
          dev:设备号
          count:添加的设备个数 
 
四、设备操作
     1、Open方法:在大部分驱动程序中主要实现初始化设备以及标明次设备号。
     2、Release方法:关闭设备
     3、Read、Write方法
        函数:ssize_t xxx_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
             ssize_t xxx_write(struct file *filp,char __user *buff,size_t count,loff_t *offp);
        参数:filp是文件指针,count是请求传输的数据缓存,offp指出文件当前的访问位置。  
              参数buff是用户空间指针。因此,它不能被内核代码直接引用。原因如下:
              用户空间指针在内核空间时可能根本无效的--没有那个地址的映射。
              内核提供了专门的函数用于访问用户空间的指针,例如:
              int copy_from_user(void *to,const void __user *from,int n)
              int copy_to_user(void __user *to,const void *from,int n)
 
五、设备注销
     方法:int cdev_del(struct cdev *p)

六、实例

1、memdev.c

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "memdev.h"

static int mem_major = MEMDEV_MAJOR;

module_param(mem_major, int, S_IRUGO);

struct mem_dev *mem_devp;

struct cdev cdev;

int mem_open(struct inode *inode,struct file *filp)
{
    struct mem_dev *dev;

    /*获取次设备号*/
    int num = MINOR(inode->i_rdev);

    if(num >= MEMDEV_NR_DEVS)
        return -ENODEV;   
    dev = &mem_devp[num];

    /*将设备描述结构指针赋值给文件私有数据指针*/
    filp->private_data = dev;

    return 0;
}

int mem_release(struct inode *inode,struct file *filp)
{
    return 0;
}

static ssize_t mem_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)
{
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;

    struct mem_dev *dev = filp->private_data;    /*获取设备结构体指针,实质由open参数inode相关联*/;

    if(p >= MEMDEV_SIZE)
        return 0;
    if(count > MEMDEV_SIZE - p)   
        count = MEMDEV_SIZE - p;

    if (copy_to_user(buf,(void*)(dev->data + p),count))   
    {
        ret = - EFAULT;
    }
    else
    {
        *ppos += count;
        ret = count;
   
        printk(KERN_INFO "read %d bytes(s) from %ld\n",count,p);
    }
    return ret;
}

static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
{
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
   
    struct mem_dev *dev = filp->private_data;

    if(p >= MEMDEV_SIZE)
        return 0;
    if(count > MEMDEV_SIZE - p)
        count = MEMDEV_SIZE - p;

    if(copy_from_user(dev->data + p,buf,count))
        ret = - EFAULT;
    else
    {
        *ppos += count;
        ret = count;
       
        printk(KERN_INFO "written %d bytes(s) from %ld\n",count,p);
    }

    return ret;
}

static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)
{
    loff_t newpos;

    switch(whence)
    {
    case 0:            /*SEEK_SET*/
        newpos = offset;
        break;
    case 1:            /*SEEK_CUR*/
        newpos = filp->f_pos + offset;
        break;
    case 2:            /*SEEK_END*/
        newpos = MEMDEV_SIZE - 1 + offset;
        break;   
    default:        /*cannt happen*/
        return -EINVAL;
        break;
    }

    if ((newpos < 0) || (newpos > MEMDEV_SIZE))
        return -EINVAL;

    filp->f_pos = newpos;   
    return newpos;
}   

static const struct file_operations mem_fops =
{
    .owner = THIS_MODULE,
    .llseek = mem_llseek,
    .read = mem_read,
    .write = mem_write,
    .open = mem_open,
    .release = mem_release,
};

static int memdev_init(void)
{
    int result;
    int i;

    dev_t devno = MKDEV(mem_major,0);

    /*静态申请设备号*/
    if(mem_major)
    {
        result = register_chrdev_region(devno,5,"memdev");
    }
    else    /*动态申请设备号*/
    {
        result = alloc_chrdev_region(&devno,0,5,"memdev");
        mem_major = MAJOR(devno);
    }
    if(result < 0)
        return  result;

    /*初始化cdev结构*/
    cdev_init(&cdev, &mem_fops);
    cdev.owner = THIS_MODULE;
    cdev.ops = &mem_fops;

    /*注册字符设备*/
    cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
   
    /*为设备描述结构分配内存*/
    mem_devp = kmalloc(MEMDEV_NR_DEVS*sizeof(struct mem_dev),GFP_KERNEL);
    if (!mem_devp)    /*申请失败*/
    {
        result = -ENOMEM;
        printk("memdev register failure!\n");
        goto fail_malloc;
    }
    memset(mem_devp,0,sizeof(struct mem_dev));
   
    /*为设备分配内存*/
    for(i = 0; i < MEMDEV_NR_DEVS; i++)
    {
        mem_devp[i].size = MEMDEV_SIZE;
        mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
        memset(mem_devp[i].data,0,MEMDEV_SIZE);        //对内存进行清空
    }
    printk("memdev register success!\n");
    return 0;

    fail_malloc:
    unregister_chrdev_region(devno,1);
}

static void memdev_exit(void)
{
    cdev_del(&cdev);    /*注销设备*/
    kfree(mem_devp);    /*释放设备结构体内存*/   
    unregister_chrdev_region(MKDEV(mem_major,0),2);    /*释放设备号*/
}

MODULE_AUTHOR("wang xiaodong");
MODULE_LICENSE("Dual BSD/GPL");

module_init(memdev_init);
module_exit(memdev_exit);

2、memdev.h

 #ifndef _MEMDEV_H_
#define _MEMDEV_H_

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 5
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif

struct mem_dev
{
    char *data;
    unsigned long size;
};

#endif 
      

3、Makefile

  ifneq ($(KERNELRELEASE),)   
    obj-m:= memdev.o
memdev.o : memdev.c memdev.h
    cc -c memdev.c
else
//KERNELDIR:=/lib/modules/2.6.32-33-generic/build
KERNELDIR:=/usr/src/linux-headers-2.6.32-33-generic
PWD:=$(shell pwd)
all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif 

4、memdev_test.c

#include "stdio.h"

int main()
{
    FILE *fp0 = NULL;
    char Buf[4096];

    strcpy(Buf,"Mem is char dev!");
    printf("BUF:%s \n",Buf);

    fp0 = fopen("/dev/memdev3","r+");
    if(fp0 == NULL)
    {
        printf("Open Error!\n");
        return -1;
    }

    fwrite(Buf,sizeof(Buf),1,fp0);
   
    /*重新定位文件位置*/
    fseek(fp0,0,SEEK_SET);

    strcpy(Buf,"Buf is NULL!");
    printf("BUF:%s\n",Buf);

    fread(Buf,sizeof(Buf),1,fp0);
    printf("BUF:%s\n",Buf);

    return 0;
}

操作:
        make
        sudo insmod memdev.ko
        查看dmesg|tail 输出:memdev register success!
        查看主设备号:cat /proc/devices
        建立设备节点 :sudo mknod /dev/memdev3 c 251 3
        修改设备访问权限:sudo chmod 777 /dev/memdev3
        gcc  memdev_test.c -o test
        ./test
结果:
BUF:Mem is char dev!
BUF:Buf is NULL!
BUF:Mem is char dev!
   
阅读(2272) | 评论(2) | 转发(1) |
0

上一篇:没有了

下一篇:文件操作之c库函数

给主人留下些什么吧!~~

superherogood2011-10-31 17:38:43

不错。

2011-10-31 11:58:56