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实现的功能是: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 应用程序开发与调试
与开发工作内容对应,开发流程主要有一下四个阶段。
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,打印装订信息。
硬件设计的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。
驱动程序介于硬件与应用程序之间,运行在内核空间,直接操作硬件设备,使得应用程序可以脱离具体的硬件。应用程序运行在用户空间。从应用程序的需求反推,可以知道驱动程序需要实现下图中的这些功能。
如图所示,应用程序的需求是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成员内注明了支持的操作种类,可按需裁剪。
驱动程序与应用程序不同,没有主函数。加载驱动模块时,会执行入口函数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可从设备树文件
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函数。其内部函数调用关系如下图所示。
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。
参考本博客“zynq自定义PL IP核linux驱动开发demo”一文
应用程序的主要操作是打印一系列用户交互信息,获取键盘的输入信息,以完成打开计数器使能核关闭使能寄存器的操作。在闪灯期间,通过system call读取驱动程序中断标志intc_flag。应用程序为linux_blinked_app.c,可在官方下载。
应用程序编译成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可执行文件。
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