赞
踩
写Linux驱动比写应用程序要难很多,Linux驱动学习是一项浩大的工程,想理清千头万绪,最好的方法就是抓住几个切入点,深入下去,扩展开去。比如用户空间和内核空间的信息交换。用户空间和内核空间的通信是通过文件系统完成的,像procfs, devfs,sysfs等,另一个切入点比如内存管理,像函数malloc, sizeof, free, 甚至memcpy等等。
为我们自己的IP写驱动,基本思路是这样的,把我们的IP看作一个字符设备,当然,我们不一定把它注册为一个chardev, 可以是miscdev, 也可以是platform_device, platform是从Linux 2.6 引入的,很多设备都可以注册为一个platform_device, 想USB,uart,或者AXI 的挂载设备,其基本的思想就是只要知道设备的地址,我就可以和设备通信,只要知道中断,就可以控制设备,至于系统和设备是怎么样链接的,是USB还是uart, 可以忽略,我的理解是,因为他们已经在各自的驱动中考虑了,我们的工作是建筑在前人工作的基础上的。
我把设备注册为一个platform_device, 然后使用procfs和设备进行通信,对于procfs, 如果每次传递的数据超过1页的话,就要用到Seq_file, 我们的IP比较简单,这一点可以不用考虑。首先创建proc_entity, 我们使用函数create_proc_entry, 使用这个函数创建的proc_entity 不能连接自定义的fileoperation, 而只能使用默认的,也就是proc_entity结构体的2个成员,write_proc 和 read_proc, 实际上我们只需要实现write_proc就可以了。新建一个文档myled.c, 我的代码如下,
#include
#include
#include /* Needed for copy_from_user */
#include /* Needed for IO Read/Write Functions */
#include
#include /* Needed for Proc File System Functions */
#include /* Needed for Sequence File Operations */
#include /* Needed for Platform Driver Functions */
/* Define Driver Name */
#define DRIVER_NAME "myled"
unsigned long *base_addr; /* Vitual Base Address */
struct resource *res; /* Device Resource Structure */
unsigned long remap_size; /* Device Memory Size */
struct proc_dir_entry *myled_proc_entry;
static struct platform_device myled = {
.name = DRIVER_NAME,
.id = -1,
};
static int myproc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
return 0;
}
static ssize_t myproc_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
unsigned long count2 = count;
u32 myled_value;
char mychar[32];
if(count2 >= sizeof(mychar))
count2 = sizeof(mychar) - 1;
if(copy_from_user(mychar,buffer,count2))
return -EFAULT;
mychar[count2]='\0';
myled_value = simple_strtoul(mychar,NULL,0);
wmb();
iowrite32(myled_value,base_addr);
return count;
};
static int __devinit myproc_probe(struct platform_device *pdev)
{
myled_proc_entry = create_proc_entry(DRIVER_NAME,0,NULL);
if(myled_proc_entry == NULL) {
printk("Couldn't create proc entry\n");
}
base_addr = ioremap(0x7E400000, 1);
if(base_addr == NULL) {
printk("Couldn't create proc entry\n");
}
myled_proc_entry->write_proc = myproc_write;
myled_proc_entry->read_proc = myproc_read;
return 0;
};
static struct platform_driver myled_driver = {
.probe = myproc_probe,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
static int __init myled_init(void)
{
platform_device_register(&myled);
platform_driver_register(&myled_driver);
// myled_proc_entry->write_proc = myproc_write;
return 0;
};
static void __exit myled_exit(void)
{
remove_proc_entry(DRIVER_NAME,NULL);
iounmap(base_addr);
release_mem_region(0x7E400000, 1);
platform_driver_unregister(&myled_driver);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_AUTHOR("Fahui, Gong");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRIVER_NAME ": myled driver");
MODULE_ALIAS(DRIVER_NAME);
我的代码可能看起不太完美,但点亮LED足够了。驱动完成以后可以编辑一个简单的Makefile, 在myled.c相同的目录下命令 gedit Makefile, 代码如下,
obj-m := myled.o
all:
make -C ../linux-digilent-3.6-digilent-13.01/ M=$(PWD) modules
clean:
make -C ../linux-digilent-3.6-digilent-13.01/ M=$(PWD) clean
Makefile的作用是告诉编译器,怎么样去编译,这儿我们在Makefile中没有声明编译器,所以要编译的话,要在myled.c 和 Makefile的目录下执行命令make ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- 编译完成后在相同目录下得到文件myled.ko,把这个文件拷到 ZedBoard SD卡的 ZED_BOOT分区上。执行如图命令,可见驱动myled加载正确。
编辑makefile的时候要注意一个窍门,all 和 clean 下边的2行一定要对齐,否则会出错。可以先把光标定位到行头,然后摁一下TAB键。
加载驱动功后,就可以编个应用程序试试了, Digilent提供了一个LED闪烁的程序,我们可以测试一下,新建一个C文件,输入代码如下,
#include
#include
#include
void main(void)
{
FILE* fp;
while(1) {
fp = fopen("/proc/myled", "w");
if(fp == NULL) {
printf("Cannot open /proc/myled for write\n");
return -1;
}
fputs("0x0F\n", fp);
fclose(fp);
sleep(1);
fp = fopen("/proc/myled", "w");
if(fp == NULL) {
printf("Cannot open /proc/myled for write\n");
return -1;
}
fputs("0x00\n", fp);
fclose(fp);
sleep(1);
}
return 0;
}
然后,在相同的目录下新建Makefile, 输入代码,
CC = arm-xilinx-linux-gnueabi-gcc
CFLAGS = -g
all : led_blink
led_blink : led_blink.o
${CC} ${CFLAGS} –o $^ $@
clean :
rm –rfv *.o
rm –rfv led_blink
.PHONY : clean
在这个Makefile 中我们声明了交叉编译器,所以编译的时候只需在应用程序和makefile相同目录下执行make. 就可以得到和应用程序相同文件名的可执行文件,把他拷到SD卡的ZED_BOOT分区。在终端中输入如下图的命令。
运行结果,发不了视频,只能截图了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。