学前理论
驱动分类-常规分类法
驱动分类-常规分类法:字符设备,块设备,网络设备。
驱动分类-总线分类法:USB设备,PCI设备,平台总线设备。
字符设备:字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常支持open,close,read,write系统调用。例如:串口,LED,按键。
块设备:在大部分UNIX系统中,块设备定义为以块(通常为512字节)为最小传输单位的设备,块设备不能按字节处理数据。而Linux则允许块设备传送任意数目的字节。因此,块设备和字符设备的区别仅仅是驱动的与内核的接口不同。常见的块设备包括硬盘,flash,SD卡等。
网络接口设备:网络接口可以是一个硬件设备,如网卡;但也可以是一个纯粹的软件设备,比如回环接口。一个网络接口负责发送和接收数据报文。
驱动学习方法和学习禁忌
驱动学习方法:驱动模型,硬件操作。
驱动学习切忌:驱动学习初期(前半年)请不要过多的去阅读内核代码!
硬件访问技术
硬件访问流程
硬件访问实质:
驱动程序控制设备,主要是通过访问设备内的寄存器来达到控制的目的,因此我们讨论如何访问硬件,就成了如何访问这些寄存器了。
硬件访问流程:
地址映射,读写寄存器。
地址映射
静态映射
静态映射:所谓静态映射,是指Linux系统根据用户事先指定的映射关系,在内核启动时,自动将物理地址映射为虚拟地址。
如何事先指定映射关系?
内核启动时,在什么地方完成自动映射?
将映射植入内核代码:
将映射代码写入内核文件(cpu关联文件)中,
在静态映射中,用户是通过map_desc
结构来指明物理地址与虚拟地址的映射关系。
第一是填充如下结构(以下结构)
struct map_desc {
unsigned long virtual;//映射后的虚拟地址
unsigned long pfn;//物理地址所在的页帧号
unsigned long length;//映射长度
unsigned int type;//映射的设备类型
};
Pfn:利用__phys_to_pfn(paddr)
可以计算出物理地址所在的物理页帧号。
第二是填充好结构后还得告诉Linux内核(将填充好的结构添加到对应数组中的尾部)
(例如arch/arm/mach-s3c2440/mach-smdk2440.c)
(例如arch/arm/mach-s5p64x0/cpu.c)
static struct map_desc smdk2440_iodesc[] __initdata = {
/* ISA IO Space map (memory space selected by A24) */
{
.virtual = (u32)S3C24XX_VA_ISA_WORD,
.pfn = __phys_to_pfn(S3C2410_CS2),
.length = 0x10000,
.type = MT_DEVICE,
}, {
.virtual = (u32)S3C24XX_VA_ISA_WORD + 0x10000,
.pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
.length = SZ_4M,
.type = MT_DEVICE,
}, {
.virtual = (u32)S3C24XX_VA_ISA_BYTE,
.pfn = __phys_to_pfn(S3C2410_CS2),
.length = 0x10000,
.type = MT_DEVICE,
}, {
.virtual = (u32)S3C24XX_VA_ISA_BYTE + 0x10000,
.pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
.length = SZ_4M,
.type = MT_DEVICE,
}
};
第三就交给内核在启动时去初始化地址映射了:
static void __init mini2440_map_io(void)
{
s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));
s3c24xx_init_clocks(12000000);
s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));
}
static void __init smdk2440_map_io(void)
{
s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
s3c24xx_init_clocks(16934400);
s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
}
动态映射
动态映射:ioremap()
动态映射:所谓动态映射,是指在驱动程序中采用ioremap
函数将物理地址映射为虚拟地址。
原型:void * ioremap (physaddr, size);
返回值:映射后的虚拟地址。
寄存器读写
寄存器读写函数集:
在完成地址映射(通常为静态映射)后,就可以读写寄存器了,Linux内核提供了一系列函数,来读写寄存器。
unsigned ioread8(void *addr);
unsigned ioread16(void *addr);
unsigned ioread32(void *addr);
unsigned readb(address);
unsigned readw(address);
unsigned readl(address);
unsigned iowrite8(u8 value, void *addr);
unsigned iowrite16(u16 value, void *addr);
unsigned iowrite32(u32 value, void *addr);
unsigned writeb(unsigned value, address);
unsigned writew(unsigned value, address);
unsigned writel(unsigned value, address);