驱动程序中的硬件操作(2)

硬件访问 就是访问寄存器,所以必须要了解寄存器和内存的区别,在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 区间长度,函数会自动找到虚拟地址,然后关联起来。 然后使用ioreadiowrite 访问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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值