硬件访问 就是访问寄存器,所以必须要了解寄存器和内存的区别,在LDD中说明,寄存器的操作会出现副作用(side effect 或边际效果),当读寄存器时,可能会改变寄存器的值,并不是一定,读取某个地址时可能导致该地址内容发生变化,比如有的中断状态寄存器,读过后会自动清零,因为寄存器以为你已经把中断状态取走了,已经知道中断发生。
接下来还要内存和I/O概念在x86处理器中存在I/O空间的概念,I/O空间是相对内存空间而言的,前面所描述的地址都是内存地址空间的。
I/O端口和I/O内存
当一个寄存器位于I/O空间,称其为I/O端口,当一个寄存器位于内存地址空间称其为I/O内存。
操作I/O端口,三个步骤,1.申请,2. 访问,3. 释放
1.申请 告诉内核这些寄存器要操作了,内核提供一套函数允许驱动申请它需要的I/O端口,其中核心函数:
struct resource *request_region (unsigned long first , unsigned long n, const char * name)
参数:first:要操作的第一个I/O端口的地址,n:操作I/O端口个数,name:操作I/O端口驱动设备名字。
系统中端口的分配情况记录在 目录 /proc/ioports,接下来访问,访问I/O端口用一系列in,out函数,unsigned inb (unsigned port) void inb (unsigned port) 8位端口读写
unsigned inw (unsigned port) void inw (unsigned port) 16位端口读写
unsigned inl (unsigned port) void inl (unsigned port) 32位端口读写
当用完一组I/O端口,应使用如下函数,返还系统
void release_region (unsigned long start, unsigned long n)
对I/O内存有四步,1. 申请 2. 映射 3. 访问 4. 释放
使用struct resource *request_mem_region (unsigned long start, unsigned long len , char *name)
申请使用I/O内存,起始地址start,长度len,设备名字name。申请在 /proc/iomem中列出。
在申请后仅仅获得物理地址,在访问I/O端口之前,必须进行物理地址到虚拟地址的映射,使用 void* ioremap (unsigned long phys_addr, unsigned long size ) 获得虚拟地址。
phys_addr物理起始地址,size 区间长度,函数会自动找到虚拟地址,然后关联起来。 然后使用ioread,iowrite 访问I/O内存,I/O内存不再使用时应当释放,1.void iounmap(void *addr)
解除映射 2.void release_mem_region (unsigned long start, unsigned long n )解除I/O内存的申请。
混杂设备驱动程序
在Linux系统中把一类设备称为混杂设备,1.属于字符设备,2.有相同主设备号(10),但此设备号不同,称这类设备为混杂设备(miscdevice)。根据此设备号区分不同设备,
Linux内核使用struct miscdevice来描述一个混杂设备。
struct miscdevice {
int minor ; //次设备号
const char *name ; //设备名
const struct file_operation *fops; //文件操作
struct list_head list ;
struct device *parent;
struct device *this_device;
};
注册一个混杂设备 int misc_register(struct miscdevice * misc)仅需提供已初始化的miscdevice结构,这样比自己写字符设备简单一些,不用初始化,分配,add cdev结构。
LED驱动程序设计
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define DEVICE_NAME "leds"
static unsigned long led_table [] = {
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
static unsigned int led_cfg_table [] = {
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
static int sbc2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4) {
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[arg], !cmd); //设置GPIO使灯全亮
return 0;
default:
return -EINVAL;
}
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = sbc2440_leds_ioctl, //通过ioctl控制led
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,//内核动态次设备号
.name = DEVICE_NAME,
.fops = &dev_fops, //字符设备都有file_operations
};
static int __init dev_init(void)
{
int ret;
int i;
for (i = 0; i < 4; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); //将GPIO设置为输出功能
s3c2410_gpio_setpin(led_table[i], 0);
}
ret = misc_register(&misc);
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "led.h"
int main(int argc, char *argv[])
{
int fd;
int cmd;
if (argc <2 )
{
printf("please enter the second para!\n");
return 0;
}
cmd = atoi(argv[1]);
fd = open("/dev/myled",O_RDWR);
if (cmd == 1)
ioctl(fd,LED_ON);
else
ioctl(fd,LED_OFF);
return 0;
}