Android深度探索:HAL与驱动开发学习笔记--内存管理(学习重点)

来源:互联网 发布:微信视频强制分享源码 编辑:程序博客网 时间:2024/05/16 09:27

Linux内存管理机制没有用过,以前在vxworks系统下做过内存管理的模块,这里就先介绍些重点学习内容,以后需要查找一些实例,结合来学习!

1、分页机制等等

这部分看得有点晕头转向,以后用到再谈理解;


2、内存分配函数

kmalloc、kfree
vmalloc、vfree

3、slab


4、内存池


5、I/O端口及I/O内存

1、  访问I/O内存的流程是:

request_mem_region()-> ioremap() -> ioread8()/iowrite8() -> iounmap()->release_mem_region() 。

       前面说过,IO内存是统一编址下的概念,对于统一编址,IO地址空间是物理主存的一部分,对于编程而言,我们只能操作虚拟内存,所以,访问的第一步就是要把设备所处的物理地址映射到虚拟地址,Linux2.6下用ioremap():

       void *ioremap(unsigned long offset, unsigned long size);

然后,我们可以直接通过指针来访问这些地址,但是也可以用Linux内核的一组函数来读写:

ioread8(),iowrite16(), ioread8_rep(), iowrite8_rep()......

 

2、  访问I/O端口

       访问IO端口有2种途径:I/O映射方式(I/O-mapped)、内存映射方式(Memory-mapped)。前一种途径不映射到内存空间,直接使用 intb()/outb()之类的函数来读写IO端口;后一种MMIO是先把IO端口映射到IO内存(“内存空间”),再使用访问IO内存的函数来访问 IO端口。

       void ioport_map(unsigned long port, unsigned int count);

通过这个函数,可以把port开始的count个连续的IO端口映射为一段“内存空间”,然后就可以在其返回的地址是像访问IO内存一样访问这些IO端口。

request_region()函数用于申请IO端口,

request_mem_region用于申请IO内存(一般为控制寄存器)。

这两个函数申请之后还要通过ioremap函数去映射到内核虚拟地址。

voidrequest_region(unsigned long from, unsigned long num, const char *name) 

这个函数用来申请一块输入输出区域。 

如果这段I/O端口没有被占用,在我们的驱动程序中就可以使用它。在使用之前,必须向系统登记,以防止被其他程序占用。登记后,在/proc/ioports文件中可以看到你登记的io口。 

参数1:io端口的基地址。 

参数2:io端口占用的范围。 

参数3:使用这段io地址的设备名。 

  在对I/O口登记后,就可以放心地用inb(), outb()之类的函来访问了。********************************************************************************************

request_region()用于内核为驱动“分配”端口,这里分配的意思是,记录该端口已经被某个进程使用,要是其它进程试图访问它,就会产生“忙”错误。所以目的在于实现资源的互斥访问。

反之, 如果一个资源只被一个进程访问,不会导致资源的争用,这时request_region()是可选的。

 

Linux下的IO端口和IO内存

 

CPU对外设端口物理地址的编址方式有两种:一种是IO映射方式,另一种是内存映射方式。 

 Linux将基于IO映射方式的和内存映射方式的IO端口统称为IO区域(IO region)。

  IO region仍然是一种IO资源,因此它仍然可以用resource结构类型来描述。

  Linux管理IO region:

 

1)    request_region()

把一个给定区间的IO端口分配给一个IO设备。

2)     check_region()

检查一个给定区间的IO端口是否空闲,或者其中一些是否已经分配给某个IO设备。

3)     release_region()

释放以前分配给一个IO设备的给定区间的IO端口。

  Linux中可以通过以下辅助函数来访问IO端口:

  inb(),inw(),inl(),outb(),outw(),outl()

  “b”“w”“l”分别代表8位,16位,32位。


实例:

#include <asm/io.h>

#include <linux/delay.h>

#include <linux/errno.h>

#include <linux/ioport.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/gameport.h>

 

#define L4_PORT                        0x201

#define L4_SELECT_ANALOG  0xa4

#define L4_SELECT_DIGITAL   0xa5

#define L4_SELECT_SECONDARY    0xa6

#define L4_CMD_ID                  0x80

#define L4_CMD_GETCAL                 0x92

#define L4_CMD_SETCAL                  0x93

#define L4_ID                     0x04

#define L4_BUSY                        0x01

#define L4_TIMEOUT                80     /* 80 us*/

 

MODULE_AUTHOR("Vojtech Pavlik<vojtech@ucw.cz>");

MODULE_DESCRIPTION("PDPI Lightning 4gamecard driver");

MODULE_LICENSE("GPL");

 

struct l4 {

         structgameport *gameport;

         unsignedchar port;

};

 

static struct l4 l4_ports[8];

 

/*

 *l4_wait_ready() waits for the L4 to become ready.

 */

 

static int l4_wait_ready(void)

{

         unsignedint t = L4_TIMEOUT;

 

         while((inb(L4_PORT) & L4_BUSY) && t > 0) t--;

         return-(t <= 0);

}

 

/*

 *l4_cooked_read() reads data from the Lightning 4.

 */

 

static int l4_cooked_read(struct gameport*gameport, int *axes, int *buttons)

{

         structl4 *l4 = gameport->port_data;

         unsignedchar status;

         inti, result = -1;

 

         outb(L4_SELECT_ANALOG,L4_PORT);

         outb(L4_SELECT_DIGITAL+ (l4->port >> 2), L4_PORT);

 

         if(inb(L4_PORT) & L4_BUSY) goto fail;

         outb(l4->port& 3, L4_PORT);

 

         if(l4_wait_ready()) goto fail;

         status= inb(L4_PORT);

 

         for(i = 0; i < 4; i++)

                   if(status & (1 << i)) {

                            if(l4_wait_ready()) goto fail;

                            axes[i]= inb(L4_PORT);

                            if(axes[i] > 252) axes[i] = -1;

                   }

 

         if(status & 0x10) {

                   if(l4_wait_ready()) goto fail;

                   *buttons= inb(L4_PORT) & 0x0f;

         }

 

         result= 0;

 

fail:  outb(L4_SELECT_ANALOG,L4_PORT);

         returnresult;

}

 

static int l4_open(struct gameport*gameport, int mode)

{

         structl4 *l4 = gameport->port_data;

 

       if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)

                   return-1;

         outb(L4_SELECT_ANALOG,L4_PORT);

         return0;

}

 

/*

 *l4_getcal() reads the L4 with calibration values.

 */

 

static int l4_getcal(int port, int *cal)

{

         inti, result = -1;

 

         outb(L4_SELECT_ANALOG,L4_PORT);

         outb(L4_SELECT_DIGITAL+ (port >> 2), L4_PORT);

         if(inb(L4_PORT) & L4_BUSY)

                   gotoout;

 

         outb(L4_CMD_GETCAL,L4_PORT);

         if(l4_wait_ready())

                   gotoout;

 

         if(inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))

                   gotoout;

 

         if(l4_wait_ready())

                   gotoout;

       outb(port & 3, L4_PORT);

 

         for(i = 0; i < 4; i++) {

                   if(l4_wait_ready())

                            gotoout;

                   cal[i]= inb(L4_PORT);

         }

 

         result= 0;

 

out:  outb(L4_SELECT_ANALOG,L4_PORT);

         returnresult;

}

 

/*

 *l4_setcal() programs the L4 with calibration values.

 */

 

static int l4_setcal(int port, int *cal)

{

         inti, result = -1;

 

         outb(L4_SELECT_ANALOG,L4_PORT);

         outb(L4_SELECT_DIGITAL+ (port >> 2), L4_PORT);

         if(inb(L4_PORT) & L4_BUSY)

                   gotoout;

 

         outb(L4_CMD_SETCAL,L4_PORT);

         if(l4_wait_ready())

                   gotoout;

 

         if(inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))

                   gotoout;

 

         if(l4_wait_ready())

                   gotoout;

       outb(port & 3, L4_PORT);

 

         for(i = 0; i < 4; i++) {

                   if(l4_wait_ready())

                            gotoout;

                   outb(cal[i],L4_PORT);

         }

 

         result= 0;

 

out:  outb(L4_SELECT_ANALOG,L4_PORT);

         returnresult;

}

 

/*

 *l4_calibrate() calibrates the L4 for the attached device, so

 *that the device's resistance fits into the L4's 8-bit range.

 */

 

static int l4_calibrate(struct gameport*gameport, int *axes, int *max)

{

         inti, t;

         intcal[4];

         structl4 *l4 = gameport->port_data;

 

         if(l4_getcal(l4->port, cal))

                   return-1;

 

         for(i = 0; i < 4; i++) {

                   t= (max[i] * cal[i]) / 200;

                   t= (t < 1) ? 1 : ((t > 255) ? 255 : t);

                   axes[i]= (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;

                   axes[i]= (axes[i] > 252) ? 252 : axes[i];

                   cal[i]= t;

         }

 

         if(l4_setcal(l4->port, cal))

                   return-1;

 

         return0;

}

 

static int __init l4_create_ports(intcard_no)

{

         structl4 *l4;

         structgameport *port;

         inti, idx;

 

         for(i = 0; i < 4; i++) {

 

                   idx= card_no * 4 + i;

                   l4= &l4_ports[idx];

 

                   if(!(l4->gameport = port = gameport_allocate_port())) {

                            printk(KERN_ERR"lightning: Memory allocation failed\n");

                            while(--i >= 0) {

                                     gameport_free_port(l4->gameport);

                                     l4->gameport= NULL;

                            }

                            return-ENOMEM;

                   }

                   l4->port= idx;

 

                   port->port_data= l4;

                   port->open= l4_open;

                   port->cooked_read= l4_cooked_read;

                   port->calibrate= l4_calibrate;

 

                   gameport_set_name(port,"PDPI Lightning 4");

                   gameport_set_phys(port,"isa%04x/gameport%d", L4_PORT, idx);

 

                   if(idx == 0)

                            port->io= L4_PORT;

         }

 

         return0;

}

 

static int __init l4_add_card(int card_no)

{

         intcal[4] = { 255, 255, 255, 255 };

         inti, rev, result;

         structl4 *l4;

 

         outb(L4_SELECT_ANALOG,L4_PORT);

         outb(L4_SELECT_DIGITAL+ card_no, L4_PORT);

 

         if(inb(L4_PORT) & L4_BUSY)

                   return-1;

         outb(L4_CMD_ID,L4_PORT);

 

         if(l4_wait_ready())

                   return-1;

 

         if(inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)

                   return-1;

 

         if(l4_wait_ready())

                   return-1;

         if(inb(L4_PORT) != L4_ID)

                   return-1;

 

         if(l4_wait_ready())

                   return-1;

         rev= inb(L4_PORT);

 

         if(!rev)

                   return-1;

 

         result= l4_create_ports(card_no);

         if(result)

                   returnresult;

 

         printk(KERN_INFO"gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",

                   card_no? "secondary" : "primary", rev >> 4, rev, L4_PORT);

 

         for(i = 0; i < 4; i++) {

                   l4= &l4_ports[card_no * 4 + i];

 

                   if(rev > 0x28)            /* on 2.9+ thesetcal command works correctly */

                            l4_setcal(l4->port,cal);

                   gameport_register_port(l4->gameport);

         }

 

         return0;

}

 

static int __init l4_init(void)

{

         inti, cards = 0;

 

         if(!request_region(L4_PORT, 1, "lightning"))

                   return-EBUSY;

 

         for(i = 0; i < 2; i++)

                   if(l4_add_card(i) == 0)

                            cards++;

 

         outb(L4_SELECT_ANALOG,L4_PORT);

 

         if(!cards) {

                   release_region(L4_PORT,1);

                   return-ENODEV;

         }

 

         return0;

}

 

static void __exit l4_exit(void)

{

         inti;

         intcal[4] = { 59, 59, 59, 59 };

 

         for(i = 0; i < 8; i++)

                   if(l4_ports[i].gameport) {

                            l4_setcal(l4_ports[i].port,cal);

                            gameport_unregister_port(l4_ports[i].gameport);

                   }

 

         outb(L4_SELECT_ANALOG,L4_PORT);

         release_region(L4_PORT,1);

}

 

module_init(l4_init);

module_exit(l4_exit);


对IO内存资源的访问

1)     request_mem_region()

请求分配指定的IO内存资源。

2)     check_mem_region()

检查指定的IO内存资源是否已被占用。

3)    release_mem_region()

释放指定的IO内存资源。

其中传给函数的start address参数是内存区的物理地址(以上函数参数表已省略)。

驱动开发人员可以将内存映射方式的IO端口和外设内存统一看作是IO内存资源。

ioremap()用来将IO资源的物理地址映射到内核虚地址空间(3GB - 4GB)中,参数addr是指向内核虚地址的指针。

 

 


阅读全文
0 0