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);
}
阅读(636) | 评论(0) | 转发(0) |