ZYNQ Linux 逻辑端(PL)中断demo

一、关于本demo

1.本demo中,zynq运行linux系统,包含一个自定义的PL端IP外设。

2.开发板从sd卡启动。

3.主要参考文献为xilinx ug1165 zynq embedded design tutorial,

https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_2/ug1165-zynq-embedded-design-tutorial.pdf

相关参考文献为ug1144 petalinux tools reference guide 以及ug1157 petalinux tools command line guide

二、demo详解

1.应用目标与工作内容

        本demo实现的功能是:PL端封装一自定义IP (即RTL模块,封装成IP核)。该IP为一8bit计数器,内含使能寄存器,接收linux 应用程序的使能控制信号。计数器的最高位连接到PL的led上,计数器被linux程序使能后,led开始闪烁。同时,将6bit引出,封装为一个中断信号,连接到arm cpu,cpu接收到该PL端中断后,在驱动程序层面以及应用程序层面,打印中断信息。这样,就实现了linux应用程序使能PL自定义IP,并接收PL IP中断的交互。

        因此,本demo的目标是打通PL IP与驱动、驱动与应用程序之间的交互机制。

        本demo的主要内容有一下4个部分:

1)带PL IP核的硬件设计

2)带PL设备树的linux系统镜像制作

3)PL设备的驱动开发与调试

4)Linux 应用程序开发与调试

2.整体开发流程

        与开发工作内容对应,开发流程主要有一下四个阶段。

1.硬件设计

        此阶段与裸板应用的硬件设计类似,在vivado内设计、封装自定义IP核,在block design内梨花IP核与zynq cpu等。完成逻辑设计。生成bit流文件以及输出hdf硬件交付文件,进入软件开发阶段。

2.修改ug1165的驱动文件blink.c,假如中断处理功能(打印信息)。

3.linux系统镜像制作

        利用1阶段生成的hdf文件,以及2阶段的blink.c,进入ubuntu系统下的petalinux开发环境。参考本博客“zynq自定义PL IP核linux驱动开发demo”,制作出与逻辑硬件设计匹配的待驱动程序的linux系统镜像。

4.修改ug1165的应用程序c文件linux_blink_application.c,打印装订信息。

3.硬件设计

        硬件设计的blockdesign例化自定义的计数器IP核以及zynq CPU即可。其中,自定义IP 引出计数器的最高位到板子的led管脚,同时封装为中断信号,一秒钟向CPU发送一次中断,连接到CPU的中断输入。其余部分令BD自动连接即可。自定义IP封装的过程,可参看本博客“zynq 自定义逻辑IP核的封装”一文。

        zynqCPU配置为zc702的preset,并在interrupts一栏中,勾选PL-PS interrupt Ports里的IRQ_F2P,使能PL到PS的中断。

        preset 已将led分配到管脚,完成BD后编译生成bit文件并export hardware。

4.驱动程序修改与解读

4.1驱动程序整体功能分析

        驱动程序介于硬件与应用程序之间,运行在内核空间,直接操作硬件设备,使得应用程序可以脱离具体的硬件。应用程序运行在用户空间。从应用程序的需求反推,可以知道驱动程序需要实现下图中的这些功能。

ZYNQ Linux 逻辑端(PL)中断demo_第1张图片

如图所示,应用程序的需求是1.写设备寄存器(PL IP 计数使能寄存器)2.接收设备中断。用户空间的应用程序无法直接操作内核空间的内存,必须借由system call(系统调用)函数来实现对内核空间设备文件的操作。常用的system call有read ,write,ioctl等。

system call可以在用户空间直接调用,在内核驱动里,有相应的驱动函数来实现system call的操作。这些驱动函数需要开发者来设计。

       本demo中,需要设计device_read函数来读取驱动程序内的中断标志,需要设计device_ioctl来操作IO设备(即PL IP),写PL IP的使能寄存器。

为此,需要做以下操作:

1.申请中断号并向系统注册中断。

2.申请IO端口,分配IO内存,并将内存映射到虚拟地址上去。

这两步操作主要由blink_driver结构体的probe成员完成,该成员是blink_probe()函数。

在执行probe函数之前,需匹配驱动与设备的name,id_table等字段,确保驱动与设备时匹配的。该工作主要由blink_driver.driver->bus->match成员完成。

再反推,要匹配设备与驱动,必先注册设备与驱动。该操作在驱动的入口函数即完成。设备注册函数的file_operation成员内注明了支持的操作种类,可按需裁剪。

4.2驱动程序解读

驱动程序与应用程序不同,没有主函数。加载驱动模块时,会执行入口函数module_init();卸载驱动模块时,会执行module_exit();函数。入口函数接出口函数会调用一系列函数,完成4.1节图的大部分功能。另外的一些函数,如device_read()等函数,则是设计好放在驱动程序里,等待应用程序中的system call函数的调用。下文从入口函数开始,按照调用关系,详细解读驱动代码blink.c该代码在ug1165文末中有下载链接。

原始驱动程序见参考文献,下面详解有所改动。代码详解:

module_init(blink_init);以及module_exit(blink_exit);为驱动函数的入口与出口函数。入口函数调用了blink_init()函数。出口函数调用blink_exit函数。

blink_init()函数首先打印一些问候信息,表明驱动进入了初始化函数。其中的printk函数为printf函数的内核空间版本,用法与printf类似。该函数可注明打印信息的紧急等级(分八级,0-7,数字越小越紧急),等级低于某个等级时,信息不会打印在串口上,但是在开发板系统的var/log/dmsg核var/log/messegges路径下看到。

chrdev_register()第一个参数表示申请的住设备号,该值为0时,系统动态分配住设备号。推荐使用动态分配,可避免设备号冲突。第二个参数为设备名称,该名称为开发板系统/dev设备节点名称。第三个参数为file_operation结构体,该结构体内的成员即为本设备支持的操作。本demo的Fops结构体代码如下:

struct file_operations Fops =

{

        .owner = THIS_MODULE,

        .read = device_read,

        .write = device_write,

       .unlocked_ioctl - device_ioctl,

        .open -device_open,

        .release =device_release

};

可见,本demo注册的设备声明了read,write,ioctl,open,release等几个常用的成员,可以支持语音程序内对应的system call。

mmio -ioremap(BLINK_CTRL_REG,0x100);

将自定义PL IP的外设基址(总线地址)映射到内存空间内。映射地址赋值给mmio,后面对mmio的操作即可被映射到实际的总线地址上,从而改变IP核被寄存器的值。ioremap函数的第一个参数为PL IP核设备的实际基址,该基址与BlockDesign中的基址是一致的。第二个参数为映射地址的长度,其余的程序里可设置为0x100,小于硬件设计的IP核的地址空间64K。

rc=platform_driver)register(&blink_driver)注册驱动、platform+driver_register函数会调用一系列函数,完成设备与驱动的match,进一步调用blink_probe函数,完成IO与中断的初始化操作。参数blink_driver为结构体,代码如下:

static struct platform_driver blink_driver ={

.driver ={

        .name = DRIVER_NAME,

       .owner = THIS_MODULE,

        .of_match_table = blink_of_match

},

.probe = blink_probe,

.remove = blink_remove

};

blink_driver结构体中的driver成员的上子成员信息会被用于设备与驱动的匹配,尤其是of_match_table 成员,其应被赋值为blink_of_match,这与xilinx原始文件不同。blink_of_match成员如下:
 

static struct  of_devide_id blink_of_match[]={

{.compatible = "xlnx,blink-ip-1.0",},

{},

};

compatible = "xlnx,blink-ip-1.0可从设备树文件 /components/plnx_workspace/device-tree-generation/pl.dtsi中查询到.pl.dtsi是根据硬件设计hdf文件编译设备树后生成的。编译设备树会在后续章节的编译系统镜像时一起完成。如果想在第一次就将此处修改正确(此时还未制作系统镜像,还没有pl.dtsi文件),可先根据hdf文件单独编译设备树,可以参考ug1157以及ug1144

pl.dtsi代码如下:

amba_pl:amba_pl{

#address-cells = <1>;

#size-cells =<1>;

compatible = "simple-bus";

ranges;

blink_ip_0: blink_ip@43c00000{

        compatible = "xlnx,blink-ip-1.0";

        interrupt-parent = <&intc>;

        interrups = <0 29 1>;

        reg = <0x43c00000 0x10000>;

        xlnx,s00-axi-addr-width = <0x4>;

        xlnx,s00-axi-data-width = <0x20>;

    };

};

pl.dtsi文件可以看到设备的基址,以及映射到内核空间的中断号29,该中断号与逻辑硬件设计的中断号相差32,(硬件中断号是61。其他的中断号也符号这个关系)

interrups的第一个参数为中断类型,0表示共享中断,第二个参数为映射中断号,第三个参数为触发类型,0表示无触发,1表示上升沿触发,2表示下降沿触发,4表示高电平触发,8表示低电平触发。

回到blink_driver函数,该函数只赋值了三个成员,其他成员在platform_driver_register调用的子函数里被设置为默认值,包括match成员。

.probe = blink_probe,将blink_probe赋值给probe成员,probe成员会被platform_driver_register的子函数调用,最终完成IO以及中断的初始化工作。

.remove = blink_remove;blink_remove函数在卸载驱动时,会被platform_driver_register的子函数调用,用于释放中断,释放IO内存空间等。

再看platform_driver_register函数。其内部函数调用关系如下图所示。

ZYNQ Linux 逻辑端(PL)中断demo_第2张图片

platform_driver_register函数调用的都是linux kernel源码 内的系统函数。该函数先调用driver_match_device函数,匹配设备与驱动,然后调用really_probe函数,进一步调用blink_probe函数,blink_probe为blink.c中开发者设计的函数,用于IO与中断的初始化。

blink_probe函数详解:

r_mem=...获取IO资源,但是并未获取实质的物理内存。r_mem时一个指向resource结构体的指针,resource结构体包含的首个成员是start,用于表示所申请到的资源的首地址(根据第二个参数申请资源类型的不同,start有不同的含义,例如,当申请中断资源时,start表示申请的首个中断号)。执行完platform_get_resource函数后,返回一个申请到的resource结构体,内含start,end,name,flags,desc以及嵌套的resource等成员。我们需要关注start以及end成员,因为涉及到内存操作地址。

lp=...为lp结构体(即blink_local结构体)分配物理内存空间。blink_local结构体内含irq,mem_start,mem_end,*base_addr等成员,可用来保存IO设备的中断,内存起止,基址等信息。kmalloc函数的第一个参数为申请内存的大小,第二个参数为申请内存的种类。不同种类的内存具有不同的操作属性。例程中的GFP_KERNEL是普通类型。

dev_set_drvdata(dev,lp);将分配好内存的lp结构体信息保存到设备的driver_data成员。(此时lp内的信息还未更新,是默认值)

lp->..两行将lp的起止地址信息赋值给lp对应的成员。

if (!request_mem_region)...申请IO内存。request_mem_region向内核申请物理内存,第一个参数为起始地址,第二个参数为申请的内存长度,第三个参数为设备名称。

lp->base_addr...将IO物理内存地址映射到内核虚拟地址。

r_irq...获取中断资源,与获取IO资源区别在于第二个参数

lp->irq = r_irq->start;将获取的中断主要的首中断号赋值给lp结构体的irq成员。lp起中转作用。

rc=request_irq...申请中断。request_irq第一个参数为申请中断资源时分配的中断号,第二个参数为中断处理函数,发生中断后将调用该函数处理中断。本demo的中断处理函数如下:

static irqreturn_t blink_irq(int irq,void *lp){

 printk(blink interrupt\n);

intc_flag++;

printk("kintc is %x\n0",intc_flag);

return IRQ_HANDLED;

}

主要是打印信息,表明进入了中断处理函数。

第三个参数为中断触发类型,与pl.dtsi一致。第四个参数为驱动名称。第五个参数将被传给第二个参数的中断处理函数使用。

intc_flag记录中断发生的次数,是全局变量。将被device_read函数引用,以便通过read system call上传到用户空间的应用程序中去。

总体而言,blink_probe函数实现了IO端口和中断申请等初始化操作;将IO内存映射到虚拟地址,以便device_ioctl函数操作寄存器;指定指定处理函数,实现中断信号的打印和上传。

两个驱动函数device_read和device_ioctl解读:

copy_to_user函数将中断处理函数的中断计数变量intc_flag上传到用户空间。第一个参数为用于缓存的变量,在应用程序内需要利用该参数以获取数据。第二个参数是需要上传的数据,第三个参数是上传数据的长度。

ioctl_num参数是应用程序ioctl system call传下来的命令码。不同的命令码对应device_ioctl内不同的操作。假如传下来的命令码是IOCTL_ON_LED,则打开PL计数器使能寄存器,PL模块开始计数,led开始闪烁。

set_blink_ctrl函数会打印一条信息,并通过虚拟地址将PL模块的首个寄存器写为1。

5.系统镜像制作

参考本博客“zynq自定义PL IP核linux驱动开发demo”一文

6.应用程序修改以及编译

6.1应用程序解读

应用程序的主要操作是打印一系列用户交互信息,获取键盘的输入信息,以完成打开计数器使能核关闭使能寄存器的操作。在闪灯期间,通过system call读取驱动程序中断标志intc_flag。应用程序为linux_blinked_app.c,可在官方下载。

6.2应用程序编译

应用程序编译成elf文件,可在petalinux环境中编译,参考本博客“zynq自定义PL IP核linux驱动开发demo”一文。也可以直接使用交叉编译器产生。步骤如下:

1.将linux_blinked_aoo.c以及blink.h放在同一文件夹下。

2.需在linux主机安装vivado的软件开发环境SDK,若环境变量设置正确,输入arm-linux后按tab键,会自动补全出arm-linux-gnueabihf-交叉编译器。若未设置好,也可手动source SDK安装路径下的settings64.sh脚本,即可添加交叉编译器。脚本路径为xxx/Xilinx/SDK/2016.4/settins64.sh

3.在linux_blinked_aoo,c所在的文件夹下,输入arm-linux-gnueabihf-gcc linux_blinked_app.c -o blink即可编译出blink可执行文件。

7.执行应用程序

1.将5节产生的BOOT.BIN和image.ub以及6节产生的blink可执行文件拷贝到系统SD卡,重启开发板。

2.在串口命令行内,cd /lib/modules/4.9.0-xilinx-v201x.x/extra/ 该路径下有驱动模块的目标文件blink.ko

3.加载驱动模块 modprobe blink.ko

4.生成设备节点 mknod /dev/blink_Dev  c  244  0

5.进入sd卡, cd /mnt  假设SD卡是挂载在.mnt路径下

6.执行blink。./blink

即可看到一样程序被执行,输入1,打开ledled

开发板led开始闪烁,并且串口持续打印kintc中断次数。

输入0,led停止闪烁,串口停止打印中断次数

卸载驱动:rmmod blink

串口会打印Goodbye module world.

 

参考源码:

驱动blink.c无改动

/*  blink.c - The simplest kernel module.
 */
#include 
#include 
#include 

#include 

#include 
#include 
#include 

#include 
#include 
#include 

#include  /* for get_user and put_user */
#include 
#include "blink.h"
#define SUCCESS 0
#define DEVICE_NAME "/dev/blink_Dev"


#define BLINK_CTRL_REG 0x43C00000
static unsigned long *mmio;
static int major_num;

/*
* Is the device open right now? Used to prevent
* concurent access into the same device
*/
static int Device_Open = 0;


static void set_blink_ctrl(void)
{
	printk("KERNEL PRINT : set_blink_ctrl \n\r");
	*(unsigned int *)mmio = 0x1;
}



static void reset_blink_ctrl(void)
{

	printk("KERNEL PRINT : reset_blink_ctrl \n\r");
	*(unsigned int *)mmio = 0x0;
}
/*
* This is called whenever a process attempts to open the device file
*/
static int device_open(struct inode *inode, struct file *file)
{
	#ifdef DEBUG
		printk(KERN_INFO "device_open(%p)\n", file);
	#endif
	/*
	* We don't want to talk to two processes at the same time
	*/
	if (Device_Open)
		return -EBUSY;
	Device_Open++;
	/*
	* Initialize the message
	*/
//	Message_Ptr = Message;
	try_module_get(THIS_MODULE);
	return SUCCESS;
}
static int device_release(struct inode *inode, struct file *file)
{
	#ifdef DEBUG
		printk(KERN_INFO "device_release(%p,%p)\n", inode, file);
	#endif
	/*
	* We're now ready for our next caller
	*/
	Device_Open--;
	module_put(THIS_MODULE);
	return SUCCESS;
}
/*
* This function is called whenever a process which has already opened the
* device file attempts to read from it.
*/
static ssize_t device_read(	struct file *file, /* see include/linux/fs.h */
							char __user * buffer, /* buffer to be filled with data */
							size_t length, /* length of the buffer */
							loff_t * offset)
{
	return SUCCESS;
}
/*
* This function is called when somebody tries to
* write into our device file.
*/
static ssize_t device_write(struct file *file,
							const char __user * buffer, 
							size_t length, 
							loff_t * offset)
{
	return SUCCESS;
}
/*
* This function is called whenever a process tries to do an ioctl on our
* device file. We get two extra parameters (additional to the inode and file
* structures, which all device functions get): the number of the ioctl called
* and the parameter given to the ioctl function.
*
* If the ioctl is write or read/write (meaning output is returned to the
* calling process), the ioctl call returns the output of this function.
*
*/
long device_ioctl(			struct file *file, /* ditto */
					unsigned int ioctl_num, /* number and param for ioctl */
					unsigned long ioctl_param)
{
//	int i;
	char *temp;
//	char ch;
	/*
	* Switch according to the ioctl called
	*/
	switch (ioctl_num) 
	{
	case IOCTL_ON_LED:
		
		temp = (char *)ioctl_param;
		set_blink_ctrl();
	break;
	case IOCTL_STOP_LED:
		temp = (char *)ioctl_param;
		reset_blink_ctrl();
	break;
	
	}
	return SUCCESS;
}
/* Module Declarations */
/*
* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions.
*/
struct file_operations Fops = {
								.owner = THIS_MODULE,       
								.read = device_read,
								.write = device_write,
								.unlocked_ioctl = device_ioctl,
								.open = device_open,
								.release = device_release, /*close */								
							};

/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
    ("Xilinx Inc.");
MODULE_DESCRIPTION
    ("blink - loadable module template generated by petalinux-create -t modules");

#define DRIVER_NAME "blink"

/* Simple example of how to receive command line parameters to your module.
   Delete if you don't need them */
unsigned myint = 0xdeadbeef;
char *mystr = "default";

module_param(myint, int, S_IRUGO);
module_param(mystr, charp, S_IRUGO);

struct blink_local {
	int irq;
	unsigned long mem_start;
	unsigned long mem_end;
	void __iomem *base_addr;
};

static irqreturn_t blink_irq(int irq, void *lp)
{
	printk("blink interrupt\n");
	return IRQ_HANDLED;
}

static int blink_probe(struct platform_device *pdev)
{
        int rc = 0;
	struct resource *r_irq; /* Interrupt resources */
	struct resource *r_mem; /* IO mem resources */
	struct device *dev = &pdev->dev;
	struct blink_local *lp = NULL;
     

	dev_info(dev, "Device Tree Probing\n");

	/* Get iospace for the device */
	r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r_mem) {
		dev_err(dev, "invalid address\n");
		return -ENODEV;
	}
	
	lp = (struct blink_local *) kmalloc(sizeof(struct blink_local), GFP_KERNEL);
	if (!lp) {
		dev_err(dev, "Cound not allocate blink device\n");
		return -ENOMEM;
	}
	
	dev_set_drvdata(dev, lp);
	
	lp->mem_start = r_mem->start;
	lp->mem_end = r_mem->end;

	if (!request_mem_region(lp->mem_start,
				lp->mem_end - lp->mem_start + 1,
				DRIVER_NAME)) {
		dev_err(dev, "Couldn't lock memory region at %p\n",
			(void *)lp->mem_start);
		rc = -EBUSY;
		goto error1;
	}

	lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
	if (!lp->base_addr) {
		dev_err(dev, "blink: Could not allocate iomem\n");
		rc = -EIO;
		goto error2;
	}

	/* Get IRQ for the device */
	r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (!r_irq) {
		dev_info(dev, "no IRQ found\n");
		dev_info(dev, "blink at 0x%08x mapped to 0x%08x\n",
			(unsigned int __force)lp->mem_start,
			(unsigned int __force)lp->base_addr);
		return 0;
	}
	lp->irq = r_irq->start;
	
	rc = request_irq(lp->irq, &blink_irq, 0, DRIVER_NAME, lp);
	if (rc) {
		dev_err(dev, "testmodule: Could not allocate interrupt %d.\n",
			lp->irq);
		goto error3;
	}

	dev_info(dev,"blink at 0x%08x mapped to 0x%08x, irq=%d\n",
		(unsigned int __force)lp->mem_start,
		(unsigned int __force)lp->base_addr,
		lp->irq);
	return 0;
error3:
	free_irq(lp->irq, lp);
error2:
	release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
error1:
	kfree(lp);
	dev_set_drvdata(dev, NULL);


	return rc;
}

static int blink_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct blink_local *lp = dev_get_drvdata(dev);
	free_irq(lp->irq, lp);
	release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
	kfree(lp);
	dev_set_drvdata(dev, NULL);
	return 0;
}

#ifdef CONFIG_OF
static struct of_device_id blink_of_match[] = {
	{ .compatible = "vendor,blink", },
	{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, blink_of_match);
#else
# define blink_of_match
#endif


static struct platform_driver blink_driver = {
	.driver = {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
		.of_match_table	= blink_of_match,
	},
	.probe		= blink_probe,
	.remove		= blink_remove,
};

static int __init blink_init(void)
{
        int rc = 0;
	printk("<1>Hello module world.\n");
	printk("<1>Module parameters were (0x%08x) and \"%s\"\n", myint,mystr);

	/*
	* Register the character device (atleast try)
	*/
	major_num = register_chrdev(0,DEVICE_NAME, &Fops);

	/*
	* Negative values signify an error
	*/
	if (major_num < 0) 
	{
		printk(KERN_ALERT "%s failed with \n","Sorry, registering the character device ");
	}
	
	mmio = ioremap(BLINK_CTRL_REG,0x100);
        
        printk("%s: Registers mapped to mmio = 0x%x  \n",__FUNCTION__,mmio);
	
        printk(KERN_INFO "%s the major device number is %d.\n","Registeration is a success", major_num);
	printk(KERN_INFO "If you want to talk to the device driver,\n");
	printk(KERN_INFO "create a device file by following command. \n \n");
	printk(KERN_INFO "mknod %s c %d 0\n\n", DEVICE_NAME, major_num);
	printk(KERN_INFO "The device file name is important, because\n");
	printk(KERN_INFO "the ioctl program assumes that's the file you'll use\n");

	rc =  platform_driver_register(&blink_driver);

        return rc;
}


static void __exit blink_exit(void)
{
        unregister_chrdev(major_num,DEVICE_NAME);
	platform_driver_unregister(&blink_driver);
	printk(KERN_ALERT "Goodbye module world.\n");
}

module_init(blink_init);
module_exit(blink_exit);

驱动头文件

/*
* blink.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because
* they need to be known both to the kernel module
* (in chardev.c) and the process calling ioctl (ioctl.c)
*/
#ifndef BLINK_H
#define BLINK_H
#include 
/*
* The major device number. We can't rely on dynamic
* registration any more, because ioctls need to know
* it.
*/
#define MAGIC_NUM 100
/*
* TURN ON LED ioctl
*/
#define IOCTL_ON_LED _IOR(MAGIC_NUM, 0, char *)
/*
* _IOR means that we're creating an ioctl command
* number for passing information from a user process
* to the kernel module.
*
* The first arguments, MAGIC_NUM, is the major device
* number we're using.
*
* The second argument is the number of the command
* (there could be several with different meanings).
*
* The third argument is the type we want to get from
* the process to the kernel.
*/
/*
* STOP LED BLINK ioctl
*/
#define IOCTL_STOP_LED _IOR(MAGIC_NUM, 1, char *)

#define DEBUG

#define DEVICE_FILE_NAME "/dev/blink_Dev"
#endif

应用程序linux_blinked_app.c,有改动

/*
* ioctl.c - the process to use ioctl's to control the kernel module
*
* Until now we could have used cat for input and output. But now
* we need to do ioctl's, which require writing our own process.
*/
/*
* device specifics, such as ioctl numbers and the
* major device file.
*/
#include "blink.h"
#include 
#include 
#include  /* open */
#include  /* exit */
#include  /* ioctl */
/*
* Functions for the ioctl calls
*/
void ioctl_ON_LED(int file_desc)
{
	int ret_val;
	ret_val = ioctl(file_desc, IOCTL_ON_LED, NULL);
	if (ret_val < 0)
	{
		printf("ioctl_ON_LED failed:%d\n", ret_val);
		exit(-1);
	}
}
void ioctl_OFF_LED(int file_desc)
{
	int ret_val;
	ret_val = ioctl(file_desc, IOCTL_STOP_LED,NULL);
	if (ret_val < 0)
	{
		printf("ioctl_OFF_LED failed:%d\n", ret_val);
		exit(-1);
	}
}
/*
* Main - Call the ioctl functions
*/
int main()
{
	int Choice;
	int exitflag=1;
	int file_desc;
    unsigned char intc_flag;//add by shimmy_lee


	printf("################################ \n\r");
	printf("      Blink LED Application  \n\r");
	printf("################################ \n\r");
	file_desc = open(DEVICE_FILE_NAME, O_RDWR | O_SYNC);
	if (file_desc < 0) 
	{
		printf("Can't open device file: %s\n", DEVICE_FILE_NAME);
		exit(-1);
	}
	while (exitflag)
	{
		printf("************************************************ \n\r");
		printf("Press '1' to Start Blink LED TEST \n\r");
		printf("Press '0' to Stop Blink LED TEST \n\r");
		printf("************************************************ \n\r");
		scanf("%d",&Choice);
		printf("Choice  :: %d \n\r",Choice);

        read(file_desc,&intc_flag,1);//add by shimmy_lee
		if(Choice == 1)
		{
            printf("uintc is %x\n",intc_flag);

			ioctl_ON_LED(file_desc);
			exitflag	= 1;
		}
		else if (0 == Choice )
		{
			ioctl_OFF_LED(file_desc);
			exitflag	= 1;
		}
		else
		{
			exitflag	= 0;

		}
	}

	close(file_desc);
	printf("################################ \n\r");
	printf("      Bye Blink LED Application  \n\r");
	printf("################################ \n\r");
	return 0;
}

应用程序头文件

/*
* blink.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because
* they need to be known both to the kernel module
* (in chardev.c) and the process calling ioctl (ioctl.c)
*/
#ifndef BLINK_H
#define BLINK_H
#include 
/*
* The major device number. We can't rely on dynamic
* registration any more, because ioctls need to know
* it.
*/
#define MAGIC_NUM 100
/*
* TURN ON LED ioctl
*/
#define IOCTL_ON_LED _IOR(MAGIC_NUM, 0, char *)
/*
* _IOR means that we're creating an ioctl command
* number for passing information from a user process
* to the kernel module.
*
* The first arguments, MAGIC_NUM, is the major device
* number we're using.
*
* The second argument is the number of the command
* (there could be several with different meanings).
*
* The third argument is the type we want to get from
* the process to the kernel.
*/
/*
* STOP LED BLINK ioctl
*/
#define IOCTL_STOP_LED _IOR(MAGIC_NUM, 1, char *)

#define DEBUG

#define DEVICE_FILE_NAME "/dev/blink_Dev"
#endif

 

你可能感兴趣的:(ZYNQ学习笔记)