Chinaunix首页 | 论坛 | 博客
  • 博客访问: 256757
  • 博文数量: 52
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1538
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-24 07:45
个人简介

生活就像海洋,只有意志坚强的人,才能到达彼岸。

文章存档

2013年(52)

分类: LINUX

2013-08-23 18:12:01

一、mmap设备操作

1>mmap系统调用
    
    void* mmap(void* addr, size_t len, int prot, int flags, int fd,off_t offset)

内存映射函数mmap,负责把文件内容映射到进程的虚拟内存空间,通过这段内存的读取和修改,来实

现对文件的读取和修改,而不需要再调用read,write等操作。

参数:
    addr:指定映射的起始地址(一般不指定,设为NULL由系统指定)

    length:映射到内存的文件长度。

    prot:映射区的保护方式,可以是:

        PROT_EXEC:映射区可被执行

        PROT_READ:映射区可被读取

        PROT_WRITE:映射区可被写入。

    flags:映射区的特性,可以是:
   
        MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享。
        
        MAP_PRIVATE:对映射区的写入擦做会产生一个映射区的复制(copy-on-write),对此区域

所做的修改不会写回原文件。

    fd:由open返回的文件描述符,代表要映射的文件。

    offset:以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件头开始映射。

2>解除映射

int munmap(void* start, size_t length)

功能
    取消参数start所指向的映射内存,参数length表示欲取消的内存大小。

返回值
    解除成功返回0,否则返回-1,错误原因存于errno中。

3>mmap系统调用的使用

/****************mmap.c*******************/

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

#define MEMDEV_SIZE 4096

int main(void)
{
int fd, ret;
char buf[MEMDEV_SIZE];
    char *pmdev;

    if (-1==(fd=open ("/dev/memdev0", O_RDWR))) {
        printf("open dev0 error\n");
        _exit(EXIT_FAILURE);
    }

    printf("\nTest for mmap(): ...\n");

    //sleep(30);
    pmdev = (char*)mmap(NULL, MEMDEV_SIZE, PROT_READ|PROT_WRITE, 
                        MAP_SHARED, fd, 0);
    printf("mmap() return: pmdev=%p\n", pmdev);
    sleep(20);
    printf("begin to use pmdev ...\n");
    bzero(pmdev, MEMDEV_SIZE);
    strcpy(buf, "Test for mmap() -- write by write()");
    printf("[use write()] write to mdev: %s\n", buf);
    ret = write(fd, buf, strlen(buf));
    printf("[use pmdev] content in mdev: %s\n", pmdev);
    
    bzero(buf, MEMDEV_SIZE);
    strcpy(buf, "write through pmdev, read by read()");
    printf("[use pmdev] store to mdev(offset=%d): %s\n", ret, buf);
    strcat(pmdev, buf);
    ret = read(fd, buf, strlen(buf));
    buf[ret] = 0;
    printf("[use read()] read from mdev: %s\n", buf);

    //sleep(30);
_exit(EXIT_SUCCESS);
}

4>虚拟内存区域
    1、虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围

一个进程的内存映像由下面几部分组成:程序代码、数据、BSS和栈区域,以及内存映射的区域。

程的内存区域可以通过查看/proc/pid/maps.

每一行的域为:
start end perm offset major:minor inode

起始虚拟地址 结束虚拟地址 读写执行权限 被映射部分在文件中的起始地址 主次设备号 索引结点

    2、Linux内核使用结构vm_area_struct()来描述虚拟内存区域,其中主要成

员如下:unsigned long vm_start:虚拟内存区域起始地址 unsigned long vm_end :虚拟内存区域

结束地址 unsigned long vm_flags:该区域的标记。如VM_IO和VM_RESERVED。VM_IO将该VMA标记为内

存映射的IO区域,VM_IO会阻止系统将将该区域包含在进程的存放转存(core dump)中,

VM_RESERVED标志内存区域不能被换出。

5>mmap设备操作

    映射一个设备是指把用户空间的一段地址关联到设备内存上。当程序读写这段用户空间的地址

时,它实际上是在访问设备。mmap方法是file_oprations结构的成员,在mmap系统调用发出时被调

用。在此之前,内核已经完成了很多工作,mmap设备方法所需要做的就是建立虚拟地址到物理地址的

页表int(*mmap)(struct file*,struct vm_area_struct*).

    mmap建立页表有两种方法:

    1、使用remap_pfn_range一次建立所有页表

    2、使用nopage VMA方法每次建立一个页表。

构造页表的函数原型:

int remap_pfn_range(struct vm_area_struct* vma,unsigned long addr,unsigned long pfn,unsigned long size,pgprot_t prot)

参数:

vma:虚拟内存区域指针

vir_addr:虚拟地址的起始值

pfn:要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到。

size:要映射的区域大小

prot:VMA的保护属性。

实现实例:

int memdev_mmap(struct file* filp,struct vm_area_struct* vma)
{
    vma->vm_flags |= VM_IO;
    vma->vm_flags |= VM_RESERVED ;


    if(remap_pfn_range(vma,vma->vm_start,virt_to_phys(dev->data)>>PAGE_SHIFT,size,vma->vm_page_prot))

return -EAGAIN;

return 0;

}

二、硬件访问

1>寄存器和内存

    寄存器和RAM的主要不同在于寄存器操作有副作用(side effect或边际效果)读取某个地址时

可能导致该地址内容发生变化,比如很多设备的中断状态寄存器只要一读取,便自动清零。

2>内存与I/O

    在X86处理器中存在I/O空间的概念,I/O空间是相对内存空间而言的,他们是彼此独立的地址空

间,在32位的X86系统中,I/O空间大小为64K,内存空间大小为4G。X86支持内存空间、I/O空间,

ARM、MIPS、PowerPC只支持内存空间。

3>IO端口和IO内存

    IO端口:当一个寄存器或内存位于IO空间时,称其为IO端口。

    IO内存:当一个寄存器或内存位于内存空间时,称其为IO内存。

4>操作I/O端口

对I/O端口的操作需按如下步骤完成:1.申请、2.访问、3.释放。

1、内核提供了一套函数来驱动申请它需要的I/O端口,其中核心函数是:

struct resource* request_region(unsigned long first,unsigned long n,const char* name)

这个函数告诉内核,你要使用从first开始的n个端口,name参数是设备的名字,如果申请成功,返回

非NULL,申请失败,返回NULL。系统中端口的分配情况记录在/proc/ioports中展示。如果不能分

配需要的端口,可以来这里查看谁在使用。


2、I/O端口可分为8位,16位,32位端口。Linux内核头文件(体系依赖的头文件)定义了下列内联函数来访问I/O端口:

unsigned inb(unsigned port)    读字节端口(8位宽)。

void outb(unsigned char byte,unsigned port) 写字节端口(8位)。

unsigned inw(unsigned port)
void outw(unsigned short word,unsigned port)   存取16-位端口。

unsigned inl(unsigned port)
void outl(unsigned longword,unsigned port)   存取32-位端口。

3、当用完一组I/O端口(通常在驱动卸载时),应使用如下函数把他们返还系统:

void release_region(unsigned long start, unsigned long n)

5>操作I/O内存

1、申请:内核提供了一套函数来允许驱动申请它需要的I/O内存,其中核心的函数是:

struct resource* request_mem_region(unsigned long start,unsigned long len , char* name)

这个函数申请一个从start开始,长度为len字节的内存区。如果成功,返回非NULL;否则返回NULL,

所有已经在使用的I/O内存在/proc/iomem中列出。

2、映射:在访问I/O内存之前,必须进行物理地址到虚拟地址的映射,ioremap函数具有此功能:

    void* ioremap(unsigned long phys_addr,unsigned long size)

3、访问:访问I/O内存的正确方法是通过一系列内核提供的函数:
读I/O内存读,使用下列之一:

unsigned ioread8(void* addr)
unsigned ioread16(void* addr)
unsigned ioread32(void* addr)

写I/O内存,使用下列之一:

void iowrite8(u8 value, void* addr)
void iowrite16(u16 value, void* addr)
void iowrite32(u32 value, void* addr)


4:释放:I/O内存不在需要使用时应当释放,步骤如下:

1、void iounmap(void* addr):解除映射

2、void release_mem_region(unsigned long start,unsigned long len)

三、混杂设备驱动

1>定义:在Linux系统中,存在一类字符设备,它们共享一个主设备号(10),但次设备号不同,我

们称这类设备为混杂设备(miscdevice)。所有的混杂设备形成一个链表,对设备访问时内核根据

设备号查找到相应的miscdevice设备。

2>设备描述
    Linux内核使用struct miscdevice来描述一个混杂设备。

struct miscdevice{
    int minor;/*次设备号*/
    const char* name;/*设备名*/
    const struct file_operations* fops;/*文件操作*/
    struct list_head list;
    struct device* parent;
    struct device* this_device;
};

3>设备注册
    Linux内核使用misc_register函数来注册一个混杂设备驱动。

int misc_register(struct miscdevice* misc)

4>设备资源
    设备资源使用struct resource来描述
struct resource{
    resource_size_t start; //资源的起始物理地址
    resource_size_t end; //资源的结束物理地址
    const char* name;//资源的名称
    unsigned long flags;//资源的类型,比如MEM。IO,IRQ类型
    struct resource *parent,*sibling,*child;//资源链表指针
}

四、LED驱动程序设计

1>上拉/下拉电阻
    上拉是将不确定的信号通过一个电阻与电源相连,固定在高电平下拉是将不确定的信号通过一

个电阻与地相连,固定在低电平。上拉是对器件注入电流,下拉是输出电流。当一个接有上拉电阻的

I/O端口设为输入状态时,它的常态为高电平,可用于检测低电平的输入。

点击(此处)折叠或打开

  1. #ifndef __LED_H__
  2. #define __LED_H__

  3. #include <linux/ioctl.h>

  4. #define DEVICE_NAME "up6410_led"

  5. /*幻数*/
  6. #define led_IOC_MAGIC '1'

  7. /*led命令*/

  8. #define LED_IOCGETDAT _IOR(led_IOC_MAGIC,1,int)
  9. #define LED_IOCSETDAT _IOW(led_IOC_MAGIC,2,int)

  10. #define LED_IOC_MAXNR    2

  11. #endif

点击(此处)折叠或打开

  1. #include <linux/miscdevice.h>
  2. #include <linux/kernel.h>
  3. #include <linux/module.h>
  4. #include <linux/init.h>
  5. #include <linux/fs.h>
  6. #include <linux/types.h>
  7. #include <linux/errno.h>
  8. #include <linux/ioctl.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <liux/ioport.h>
  12. #include <linux/io.h>
  13. #include <linuxe/uaccess.h>

  14. #include "led.h"

  15. unsigned long GPIOM_VA_BASE;

  16. #define GPIOM_CON_VA    GPIO_VA_BASE
  17. #define GPIOM_DAT_VA    GPIO_VA_BASE + 0x4
  18. #define GPIOM_PUD_VA    GPIO_VA_BASE + 0x8

  19. #define GPIO_VA_BASE     0x7f008820


  20. struct resource ok6410_led_resource = {
  21.     .name = "led io_mem",
  22.     .strat = GPIO_PA_BASE,
  23.     .end = GPIO_PA_BASE + 0xc,
  24.     .flags = IORESOURCE_MEM,    
  25. };
  26. static void ok6410_led_pin_setup(void)
  27. {
  28.     unsigned long start = ok6410_led_resource.start;
  29.     unsigned long size = ok6410_led_resource.end - start;
  30.     unsigned long tmp;
  31.     
  32.     /*申请IO内存*/
  33.     request_mem_region(start, size,ok6410_led_resource,name);
  34.     
  35.     /*映射IO内存*/
  36.     GPIOM_VA_BASE = (unsigned long)ioremap(start, size);
  37.     
  38.     printk("<1>[GPIOM_VA_BASE = 0x%lx]\n",GPIO_VA_BASE);
  39.     
  40.     /*访问*/
  41.     tmp = readl(GPIOM_CON_VA);
  42.     tmp = (tmp & ~(0xffffU))|(0x1111U);//设置IO端口为输出功能
  43.     writel(tmp,GPIO_CON_VA);
  44.     
  45.     tmp = readl(GPIOM_DAT_VA);
  46.     tmp |= 0xf;
  47.     writel(tmp, GPIOM_DAT_VA);
  48. }
  49. static unsigned long ok6410_led_getdat()
  50. {
  51.     return (readl(GPIOM_DAT_VA)&0xF);
  52. }

  53. static void ok6410_led_setdat(int dat)
  54. {
  55.         unsigned long tmp;
  56.         
  57.         tmp = readl(GPIOM_DAT_VA);
  58.         tmp = (tmp & ~0xF) | (dat&0xF);
  59.         writel(tmp, GPIOM_DAT_VA);
  60. }
  61. static long led_ioctl(struct file* filp,unsigned long cmd,unsigned long arg)
  62. {
  63.     int ioarg, ret;
  64.     
  65.     if(_IOC_TYPE(cmd) != LED_IOC_MAGIC)
  66.         return -EINVAL;
  67.     if(_IOC_NR(cmd) > LED_IOC_MAXNR)
  68.         return -EINVAL;
  69.         
  70.     switch(cmd){
  71.         case LED_IOCGETDAT:
  72.             ioarg = ok6410_led_getdat();
  73.             ret = put_user(ioarg,(int*)arg);
  74.             break;
  75.         case LED_IOCSETDAT:
  76.             ret = get_user(ioarg,(int*)arg);
  77.             ok6410_led_setdat(ioarg);
  78.             break;
  79.         default:
  80.             return -EINVAL;
  81.             
  82.         }    
  83.         
  84.         return ret;
  85. }

  86. static void ok6410_led_pin_release(void)
  87. {
  88.     /*解除映射*/
  89.     iounmap((void*)GPIOM_VA_BASE);
  90.     release_mem_region(ok6410_led_resource.start,ok6410_led_resource.end - ok6410_led_resource.start);
  91.         
  92. }

  93. staitc struct file_operations dev_fops = {
  94.     .owner = THIS_MODULE,
  95.     .unlocked_ioctl = led_ioctl,    
  96. }

  97. struct miscdevice misc = {
  98.     .minor = MISC_DYNAMIC_MINOR,
  99.     .name = DEVICE_NAME,
  100.     .fops = &dev_fops,
  101. }

  102. static int __init dev_init(void)
  103. {
  104.     int ret;
  105.     
  106.     ok_6410_led_pin_setup();//设置引脚功能
  107.     
  108.     ret = misc_register(&misc);//混杂设备注册
  109.     
  110.     return ret;
  111.     
  112. }

  113. static void __exit dec_exit(void)
  114. {
  115.     ok6410_led_pin_release();//释放引脚
  116.     misc_deregister(&misc);//注销设备
  117. }

  118. module_init(dev_init);
  119. module_exit(dev_exit);
  120. MODULE_LICENSE("GPL");
  121. MODULE_AUTHOR("XIAHAIJUN");


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