Chinaunix首页 | 论坛 | 博客
  • 博客访问: 33055
  • 博文数量: 21
  • 博客积分: 338
  • 博客等级: 一等列兵
  • 技术积分: 165
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-31 09:41
文章分类

全部博文(21)

文章存档

2011年(21)

我的朋友

分类: LINUX

2011-03-18 00:06:32

linux设备驱动开发详解
模块编译的过程:先进入Linux内核的目录,并编译出.o文件,运行MODPOST会生成临时文件.mod.c,在编译出.mod.o文件,之后连接.o文件和.mod.o文件得到.ko文件
若一个模块包含多个.c文件,应该如下编写Makefile:
obj-m := modulename.o
module-objs := file1.o file2.o
__________________________________________________
112页
要是确实需要将内核空间的某个地址映射到用户空间呢?应该怎么办?
我在2.6的内核下面将ADC_PHY_START 处的SDRAM映射到了用户空间,
通过ioremap把这个物理地址转换为逻辑地址,然后将采集的数据放入其中,在用户空间mmap之后,可以取到写入的数。
楼上的说ioremap操作设备内存,但是为什么这里也可以操作SDRAM呢?
我只知道这样可以用,但是原因不太清楚,请指教。

static int adc_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long physical = ADC_PHY_START >> PAGE_SHIFT;
unsigned long vsize = vma->vm_end - vma->vm_start;

adc_res->buffer=(unsigned int *)ioremap(ADC_PHY_START,vsize);
if (remap_pfn_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot))
{
return -EAGAIN;
}
return 0;

}
——--------------------------------------------------————————————————————————--
例子中,用户态程序的KERNEL_VIRT_ADDR就是内核模块打印的地址p
这里是hard coding(先加载内核模块,再把打印的地址赋值给KERNEL_VIRT_ADDR),
可以采用其他的方式传递。

2.6内核验证。

内核模块-----------------------


#include

#include

#include

#include



MODULE_LICENSE("GPL");

MODULE_AUTHOR("Wheelz");

MODULE_DESCRIPTION("mmap demo");



static unsigned long p = 0;



static int __init init(void)

{

    //分配共享内存(一个页面)

    p = __get_free_pages(GFP_KERNEL, 0);

    SetPageReserved(virt_to_page(p));



    printk("<1> p = 0x%08x\n", p);



    //在共享内存中写上一个字符串

    strcpy(p, "Hello world!\n");



    return 0;

}



static void __exit fini(void)

{

    ClearPageReserved(virt_to_page(p));

    free_pages(p, 0);    

}



module_init(init);

module_exit(fini);



用户态程序---------------------------------

#include

#include

#include

#include

#include



#define PAGE_SIZE (4*1024)



#define PAGE_OFFSET        0xc0000000

#define KERNEL_VIRT_ADDR    0xc5e3c000



int main()

{

    char *buf;

    int fd;

    unsigned long phy_addr;



    fd=open("/dev/mem",O_RDWR);

    if(fd == -1)

        perror("open");

    phy_addr=KERNEL_VIRT_ADDR - PAGE_OFFSET;



    buf=mmap(0, PAGE_SIZE,

        PROT_READ|PROT_WRITE, MAP_SHARED,

        fd, phy_addr);

    if(buf == MAP_FAILED)

        perror("mmap");

    puts(buf);//打印共享内存的内容

    munmap(buf,PAGE_SIZE);



    close(fd);

    return 0;

}
    
-----------------------------------------------------------------------------------------
http://blog.csdn.net/do2jiang/archive/2010/04/05/5450839.aspx
-----------------------------------------------------------------------------------------
#define AT91_BASE_SYS            0xffffea00
#define AT91_PIOA    (0xfffff400 - AT91_BASE_SYS)
#define AT91_PIOB    (0xfffff600 - AT91_BASE_SYS)
#define AT91_PIOC    (0xfffff800 - AT91_BASE_SYS)

#define AT91_IO_PHYS_BASE    0xFFF78000
#define AT91_IO_VIRT_BASE    (0xFF000000 - AT91_IO_SIZE)
#else
/*
 * Identity mapping for the non MMU case.
 */
#define AT91_IO_PHYS_BASE    AT91_BASE_SYS
#define AT91_IO_VIRT_BASE    AT91_IO_PHYS_BASE
#endif

#define AT91_IO_SIZE        (0xFFFFFFFF - AT91_IO_PHYS_BASE + 1)

 /* Convert a physical IO address to virtual IO address */
#define AT91_IO_P2V(x)        ((x) - AT91_IO_PHYS_BASE + AT91_IO_VIRT_BASE)

/*
 * Virtual to Physical Address mapping for IO devices.
 */
#define AT91_VA_BASE_SYS    AT91_IO_P2V(AT91_BASE_SYS)
#define AT91_VA_BASE_EMAC    AT91_IO_P2V(AT91RM9200_BASE_EMAC)

data->chipbase = PIN_BASE + i * 32;
data->regbase = data->offset + (void __iomem *)AT91_VA_BASE_SYS;

# define __iomem    __attribute__((noderef, address_space(2)))
# define __user        __attribute__((noderef, address_space(1)))


void __iomem *
__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
{
    unsigned long last_addr;
     unsigned long offset = phys_addr & ~PAGE_MASK;
     unsigned long pfn = __phys_to_pfn(phys_addr);

     /*
      * Don't allow wraparound or zero size
     */
    last_addr = phys_addr + size - 1;
    if (!size || last_addr < phys_addr)
        return NULL;

     return __arm_ioremap_pfn(pfn, offset, size, mtype);
}
EXPORT_SYMBOL(__arm_ioremap);

void __iomem *__arm_ioremap_pfn(unsigned long pfn, unsigned long offset,
                size_t size, unsigned int mtype)
{
    if (pfn >= (0x100000000ULL >> PAGE_SHIFT))
        return NULL;
    return (void __iomem *) (offset + (pfn << PAGE_SHIFT));
}
EXPORT_SYMBOL(__arm_ioremap_pfn);



/*
 * Remap an arbitrary physical address space into the kernel virtual
 * address space. Needed when the kernel wants to access high addresses
 * directly.
 *
 * NOTE! We need to allow non-page-aligned mappings too: we will obviously
 * have to convert them into an offset in a page-aligned mapping, but the
 * caller shouldn't need to know that small detail.
 *
 * 'flags' are the extra L_PTE_ flags that you want to specify for this
 * mapping.  See for more information.
 */
void __iomem *
__arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size,
          unsigned int mtype)
{
    const struct mem_type *type;
    int err;
    unsigned long addr;
     struct vm_struct * area;

    /*
     * High mappings must be supersection aligned
     */
    if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))
        return NULL;

    type = get_mem_type(mtype);
    if (!type)
        return NULL;

    /*
     * Page align the mapping size, taking account of any offset.
     */
    size = PAGE_ALIGN(offset + size);

     area = get_vm_area(size, VM_IOREMAP);
     if (!area)
         return NULL;
     addr = (unsigned long)area->addr;

#ifndef CONFIG_SMP
    if (DOMAIN_IO == 0 &&
        (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
           cpu_is_xsc3()) && pfn >= 0x100000 &&
           !((__pfn_to_phys(pfn) | size | addr) & ~SUPERSECTION_MASK)) {
        area->flags |= VM_ARM_SECTION_MAPPING;
        err = remap_area_supersections(addr, pfn, size, type);
    } else if (!((__pfn_to_phys(pfn) | size | addr) & ~PMD_MASK)) {
        area->flags |= VM_ARM_SECTION_MAPPING;
        err = remap_area_sections(addr, pfn, size, type);
    } else
#endif
        err = remap_area_pages(addr, pfn, size, type);

    if (err) {
         vunmap((void *)addr);
         return NULL;
     }

    flush_cache_vmap(addr, addr + size);
    return (void __iomem *) (offset + addr);
}



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