arm裸机下读写寄存器很容易,各个寄存器和内存的地址是单一地址空间,他们是用相同的指令进行读写操作的.而在linux下就要复杂很多,因为linux支持多个体系架构的CPU。比如arm和x86就不一样,具体的差别我暂时也说不上来,这个涉及到CPU体系的设计。目前我只关心:linux为了支持多个硬件体系,在IO访问上做了自己的接口。可以通过IO内存和IO端口这两种方式进行IO访问。在LED的例子上给出这两种方式的具体实现:
10多年建站经验, 成都网站建设、做网站客户的见证与正确选择。创新互联公司提供完善的营销型网页建站明细报价表。后期开发更加便捷高效,我们致力于追求更美、更快、更规范。
1.利用IO Port的方式:
[cpp] view plain copy
#include linux/module.h
#include linux/moduleparam.h
#include linux/init.h
#include linux/kernel.h /* printk() */
#include linux/slab.h /* kmalloc() */
#include linux/fs.h /* everything... */
#include linux/errno.h /* error codes */
#include linux/types.h /* size_t */
#include linux/proc_fs.h
#include linux/fcntl.h /* O_ACCMODE */
#include linux/seq_file.h
#include linux/cdev.h
#include linux/ioport.h
#include mach/regs-gpio.h
#include asm/system.h /* cli(), *_flags */
#include asm/uaccess.h /* copy_*_user */
#include asm/io.h
#define LED_NUM 4
struct led_dev
{
struct cdev dev;
unsigned port;
unsigned long offset;
};
struct led_dev led[4];
dev_t dev = 0;
static struct resource *led_resource;
int led_open(struct inode *inode, struct file *filp)
{
struct led_dev *led; /* device information */
led = container_of(inode-i_cdev, struct led_dev, dev);
filp-private_data = led; /* for other methods */
return 0; /* success */
}
int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char data;
struct led_dev *led;
u32 value;
printk(KERN_INFO "debug by baikal: led dev write\n");
led = (struct led_dev *)filp-private_data;
copy_from_user(data,buf,count);
if(data == '0')
{
printk(KERN_INFO "debug by baikal: led off\n");
value = inl((unsigned)(S3C2410_GPBDAT));
outl(value | 1led-offset,(unsigned)(S3C2410_GPBDAT));
//value = ioread32(led-base);
//iowrite32( value | 1led-offset, led-base);
}
else
{
printk(KERN_INFO "debug by baikal: led on\n");
value = inl((unsigned)(S3C2410_GPBDAT));
outl(value ~(1led-offset),(unsigned)(S3C2410_GPBDAT));
//value = ioread32(led-base);
//iowrite32( value ~(1led-offset), led-base);
}
}
struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
//.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
};
static int led_init(void)
{
int result, i;
result = alloc_chrdev_region(dev, 0, LED_NUM,"LED");
if (result 0) {
printk(KERN_WARNING "LED: can't get major %d\n", MAJOR(dev));
return result;
}
led_resource = request_region(0x56000014,0x4,"led");
if(led_resource == NULL)
{
printk(KERN_ERR " Unable to register LED I/O addresses\n");
return -1;
}
for(i = 0; i LED_NUM; i++)
{
cdev_init( led[i].dev, led_fops);
//led[i].port = ioport_map(0x56000014,0x4);
//led[i].base = ioremap(0x56000014,0x4);
led[i].offset = i + 5; //leds GPB5\6\7\8
led[i].dev.owner = THIS_MODULE;
led[i].dev.ops = led_fops;
result = cdev_add(led[i].dev,MKDEV(MAJOR(dev),i),1);
if(result 0)
{
printk(KERN_ERR "LED: can't add led%d\n",i);
return result;
}
}
return 0;
}
static void led_exit(void)
{
int i;
release_region(0x56000014,0x4);
for( i = 0; i LED_NUM; i++)
{
//iounmap(led[i].base);
cdev_del(led[i].dev);
}
unregister_chrdev_region(dev, LED_NUM);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("Baikal");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple LED Driver");
2.利用IO Mem的方式:
[cpp] view plain copy
#include linux/module.h
#include linux/moduleparam.h
#include linux/init.h
#include linux/kernel.h /* printk() */
#include linux/slab.h /* kmalloc() */
#include linux/fs.h /* everything... */
#include linux/errno.h /* error codes */
#include linux/types.h /* size_t */
#include linux/proc_fs.h
#include linux/fcntl.h /* O_ACCMODE */
#include linux/seq_file.h
#include linux/cdev.h
#include linux/ioport.h
#include asm/system.h /* cli(), *_flags */
#include asm/uaccess.h /* copy_*_user */
#include asm/io.h
#define LED_NUM 4
struct led_dev
{
struct cdev dev;
void __iomem *base;
unsigned long offset;
};
struct led_dev led[4];
dev_t dev = 0;
int led_open(struct inode *inode, struct file *filp)
{
struct led_dev *led; /* device information */
led = container_of(inode-i_cdev, struct led_dev, dev);
filp-private_data = led; /* for other methods */
return 0; /* success */
}
int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
ssize_t led_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char data;
struct led_dev *led;
u32 value;
printk(KERN_INFO "debug by baikal: led dev write\n");
led = (struct led_dev *)filp-private_data;
copy_from_user(data,buf,count);
if(data == '0')
{
printk(KERN_INFO "debug by baikal: led off\n");
value = ioread32(led-base);
iowrite32( value | 1led-offset, led-base);
}
else
{
printk(KERN_INFO "debug by baikal: led on\n");
value = ioread32(led-base);
iowrite32( value ~(1led-offset), led-base);
}
}
struct file_operations led_fops = {
.owner = THIS_MODULE,
.read = led_read,
.write = led_write,
//.ioctl = led_ioctl,
.open = led_open,
.release = led_release,
};
static int led_init(void)
{
int result, i;
result = alloc_chrdev_region(dev, 0, LED_NUM,"LED");
if (result 0) {
printk(KERN_WARNING "LED: can't get major %d\n", MAJOR(dev));
return result;
}
for(i = 0; i LED_NUM; i++)
{
cdev_init( led[i].dev, led_fops);
request_mem_region(0x56000014,0x4,"led");
led[i].base = ioremap(0x56000014,0x4);
led[i].offset = i + 5; //leds GPB5\6\7\8
led[i].dev.owner = THIS_MODULE;
led[i].dev.ops = led_fops;
result = cdev_add(led[i].dev,MKDEV(MAJOR(dev),i),1);
if(result 0)
{
printk(KERN_ERR "LED: can't add led%d\n",i);
return result;
}
}
return 0;
}
static void led_exit(void)
{
int i;
release_mem_region(0x56000014,0x4);
for( i = 0; i LED_NUM; i++)
{
iounmap(led[i].base);
cdev_del(led[i].dev);
}
unregister_chrdev_region(dev, LED_NUM);
}
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("Baikal");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple LED Driver");
这一问题来自项目中一个实际的需求:
我需要在Linux启动之后,确认我指定的芯片寄存器是否与我在uboot的配置一致。
举个例子:
寄存器地址:0x20000010负责对DDR2的时序配置,该寄存器是在uboot中设置,现在我想在Linux运行后,读出改寄存器的值,再来检查该寄存器是否与uboot的配置一致。
Linux应用程序运行的是虚拟空间,有没有什么机制可以是完成我提到的这一需求。若行,还请附些测试代码。
谢谢!
这个需要用mmap()函数将寄存器物理地址映射为用户空间的虚拟地址,即将寄存器的那段内存映射到用户空间,函数介绍如下:
void*
mmap(void
*
addr,
size_t
len,
int
prot,
int
flags,
int
fd,
off_t
offset);
该函数映射文件描述符
fd
指定文件的
[offset,
offset
+
len]
物理内存区至调用进程的
[addr,
addr
+
len]
的用户空间虚拟内存区,通常用于内存共享或者用户空间程序控制硬件设备,函数的返回值为最后文件映射到用户空间的地址,进程可直接操作该地址。下面是测试代码(仅供参考):
#define
DDR2_REG_BASE
(0x20000000)
#define
MAP_SIZE
4096UL
#define
MAP_MASK
(MAP_SIZE
-
1)
static
unsigned
int
pTestRegBase;
static
int
dev_fd;
dev_fd
=
open("/dev/mem",
O_RDWR
|
O_NDELAY);
if
(dev_fd
/SPAN
0)
{
LOGE("open(/dev/mem)
failed.");
return;
}
pTestRegBase
=
(void
*)mmap(NULL,
MAP_SIZE,
PROT_READ
|
PROT_WRITE,
MAP_SHARED,
dev_fd,DDR2_REG_BASE
~MAP_MASK);
if
(MAP_FAILED
==
pTestRegBase)
{
printf("mmap
failed.
fd(%d),
addr(0x%x),
size(%d)\n",
dev_fd,
DDR2_REG_BASE,
MAP_SIZE);
}
else
{
unsigned
int
reg_value
=
*((volatile
unsigned
int
*)(pTestRegBase
+
10));
printf("reg_value
=
0xx\n",
reg_value);
munmap((void*)pTestRegBase,
MAP_SIZE);
}
pTestRegBase
=
0;
if(dev_fd)
close(dev_fd);
这里将DDR2_REG_BASE开始大小为1个page的物理地址映射到了用户空间,然后就可以用pTestRegBase作为起始地址操作寄存器了。
不是很明白你的意思。寄存器是个硬件的结构,存在CPU中,比如EAX,EBX,ECX,EDX这些通用寄存器。硬件设备也会有寄存器,用来给软件提供控制的方法。比如显卡肯定有个寄存器来启用或者禁用。读写寄存器标准的使用IN,OUT指令(IA架构)。当然也会有把寄存器映射到内存空间,想读写内存一样读写寄存器。用户态程序一般是无法访问寄存器的,除非驱动程序把寄存器映射到用户进程空间
1.列出文件清单命令:ls
ls命令能够列出当前目录下的所有内容。ls 命令的执行方式为:
# ls [-选项] [文件名或者目录名]
进入到Linux命令行中后,我们至少要知道当前所处的位置有哪些内容,这些信息就可以使用ls命令来获得。
在Linux中,ls命令是最常使用的命令之一,因为在命令行下要随时查看目录内容。如果不加任何选项的话,ls命令仅列出当前目录下的文件和目录名,例如,想要查看/etc目录下的内容,可以使用下列命令:
# ls /etc
如果想要列出当前目录下所有文件,则可以使用下列命令:
# ls -a
2、cat命令
功能:在标准输出上显示文件。
语法:cat [-vTEuAte] 文件
例子: cat example.txt
cat -A exam2.txt
cat file1 file2 file2
3、more命令
功能:在终端屏幕按屏显示文本文件。
语法: more [-pcdls] 文件
例子: more example.c
more -dc example.c
more -c -10 example.c
4、less命令
less命令的功能几乎和more命令一样,也是用来按页显示文件,不同之处在于less命令在显示文件时允许用户既
可以向前又可以向后翻阅文件。
5、head命令
功能:显示指定文件的前若干行。缺省设置为显示10行
语法:head [-n] 文件
例子: head example.c
head -3 example.c
6、tail命令
功能:显示指定文件的末尾若干行。缺省设置为显示10行
语法:tail [+ / - num ] [参数] 文件
+num 从第num行以后开始显示。- num 从距文件尾num行处开始显示。
例子: tail example.c
tail -4 example.c
7、grep、fgrep和egrep命令
功能:
这组命令以指定模式搜索文件,并通知用户在什么文件中搜索到与指定的模式匹配的字符串,并打印出所有包含该字符串的文本行,在该文本行的最前面是该行所在的文件名。grep命令一次只能搜索一个指定的模式;egrep命令检索扩展的正则表达式(包括表达式组和可选项);fgrep命令检索固定字符串,它不识别正则表达式,是快速搜索命令。
语法:
grep [-EFbcihlnvxef] [查找模式] [文件名1,文件名2,……]
egrep [选项] [查找模式] [文件名1,文件名2,……]
fgrep [选项] [查找模式] [文件名1,文件名2,……]
例子: grep "text file" example
grep data *
grep goto *.c
设备驱动? 如果是:可以写一个 字符设备驱动实现。 驱动,最简单的是:用 ioremap(),把GPIO的地址映射到 linux内核空间。 然后操作该gpio的寄存器。 之后很简单,和裸板控制gpio的方法一样。只是字符设备方面的实现不一样。
处理概要: 通过制定类型(int,char等)的指针变量,把rw的地址给这个指针。 通过指针操作,取得含有07位的数值,然后通过移位运算即可取得07位的值。 仅供参考。