-
App应用程序调用poll函数检测 /dev/dtsled 设备文件的可写事件
无可写事件,进程一直休眠(poll)
有可写事件,写入字符 ‘1’,点亮led -
file_operations->poll:
监视write_data全局变量值
值为0,无事件发生
值为1,返回可写事件 -
指定唤醒App进程的等待队列头
poll_wait(唤醒操作) -
file_operations->write:判断write_data全局变量值
- 值为0,点亮rgb红灯
- 值为1,唤醒poll函数引起休眠的App进程
-
后台执行App应用程序,往设备文件写入字符’0’
./App 0&
设备文件没有可写事件产生,poll函数使当前App进程休眠
-
借助‘echo’应用程序,往设备文件写入字符’1’
sudo sh -c "echo '1'>/dev/rgb_led"
唤醒之前休眠的App应用程序
- 设备文件产生可写事件
- App应用程序写入字符’0’
- rgb红灯被点亮
#include2、测试app 源文件#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DTSLED_NAME "dtsled" #define LED_OFF 0 #define LED_ON 1 // 定义一组全局变量,用于存放内存映射得到的虚拟地址 static void __iomem *IMX6U_CCM_CCGR1; static void __iomem *SW_MUX_GPIO1_IO03; static void __iomem *SW_PAD_GPIO1_IO03; static void __iomem *GPIO1_DR; static void __iomem *GPIO1_GDIR; // 定义一个等待队列头 wait_queue_head_t wait_queue; static unsigned int write_data; extern struct dtsled_dev dtsled; static void led_switch(u8 state) { u32 val = 0; if(state == LED_ON) { val = readl(GPIO1_DR); val &= (~(1<<3)); writel(val, GPIO1_DR); } else if(state == LED_OFF) { val = readl(GPIO1_DR); val |= (1<<3); writel(val, GPIO1_DR); } } static int dtsled_open(struct inode *inode, struct file *filp) { int ret; if(filp->f_flags & O_NONBLOCK) { ; } else { ; } // struct file 结构体定义在 include/linux/fs.h 中 // struct file 有一个成员(空指针):void *private_data filp->private_data = &dtsled; return 0; } static int dtsled_release(struct inode *inode, struct file *filp) { //struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data; return 0; } static unsigned int dtsled_poll(struct file *filp, struct poll_table_struct *wait) { unsigned int mask = 0; // 详见下,注意第二个参数就是等待队列头 // zhiding xiaoxiduilie tou poll_wait(filp, &wait_queue, wait); // 判断驱动程序里面发生了哪些条件,这些条件对应哪些文件的事件,设置好事件后,return回去 if(write_data) { mask |= POLLOUT; } return mask; } static ssize_t dtsled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int ret; u8 databuf[32] = {0}; //struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data; printk("%s(%d):buf=(%s), count=%dn", __FILE__, __LINE__, buf, count); ret = copy_from_user(databuf, buf, count); printk("%s(%d):databuf=(%s)n", __FILE__, __LINE__, databuf); if(ret < 0) { printk("%s(%d):Kernel write failed.rn", __FILE__, __LINE__); return -1; } // s是输入字符串,base可以是10(10进制)或16(16进制),或者是0自动识别,res存放转换后的整形值 // kstrtox.c ret = kstrtoint(databuf, 16, &write_data); printk("%s(%d):write_data=%d", __FILE__, __LINE__, write_data); if(write_data) { wake_up(&wait_queue); printk("%s(%d):have woken up.n", __FILE__, __LINE__); return count; } else { led_switch(LED_OFF); } return 0; } // 操作集结构体,是 struct cdev 的成员 static const struct file_operations dtsled_fops = { .owner = THIS_MODULE, .write = dtsled_write, .open = dtsled_open, .release = dtsled_release, //unsigned int (*poll) (struct file *, struct poll_table_struct *); .poll = dtsled_poll, }; // 自定义结构体类型来描述 LED struct dtsled_dev { dev_t devid; struct cdev cdev; // 用于注册字符设备,操作集结构体是其成员变量 struct file_operations dtsled_fops; // 操作集结构体 struct class *class; // 用于自动创建设备节点 struct device *device; // 用于自动创建设备节点 u32 major; u32 minor; struct device_node *nd; // 设备树结点,用来表示 LED 设备节点 }; // 自定结构体来描述 LED struct dtsled_dev dtsled; // 驱动入口函数 static int __init dtsled_init(void) { int ret = 0; int val = 0; const char *str; u32 regdata[10]; u8 i; // 1、使用新方法来获取设备号 dtsled.major = 0; if(dtsled.major) { dtsled.devid = MKDEV(dtsled.major, 0); ret = register_chrdev_region(dtsled.devid, 1, DTSLED_NAME); } else { ret = alloc_chrdev_region(&dtsled.devid, 0, 1, DTSLED_NAME); dtsled.major = MAJOR(dtsled.devid); dtsled.minor = MINOR(dtsled.devid); } if(ret < 0) { printk("%d:Register char dev error.rn", __LINE__); goto fail_devid; } // 2、添加(注册)字符设备 dtsled.cdev.owner = THIS_MODULE; // zhu yi liang ge qu di zhi fu ! cdev_init(&dtsled.cdev, &dtsled_fops); ret = cdev_add(&dtsled.cdev, dtsled.devid, 1); if(ret < 0) { goto fail_cdev; } // 3、自动创建设备节点,需要一个 struct class,一个 struct device dtsled.class = class_create(THIS_MODULE, DTSLED_NAME); if(IS_ERR(dtsled.class)) { ret = PTR_ERR(dtsled.class); goto fail_class; } // 第一个 NULL 表示副设备, 第二个 NULL 表示 drvdata dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME); if(IS_ERR(dtsled.device)) { ret = PTR_ERR(dtsled.device); goto fail_device; } // 4、设备树相关,读取属性值 dtsled.nd = of_find_node_by_path("/alpha_led"); if(dtsled.nd == NULL) { goto fail_findnd; } // 获取状态属性,注意是指针的指针 ret = of_property_read_string(dtsled.nd, "status", &str); if(ret) { printk("%d:failrn", __LINE__); goto fail_rs; } else { printk("%d:status = "%s"rn", __LINE__, str); } //huoqu shuxing ret = of_property_read_string(dtsled.nd, "compatible", &str); if(ret) { printk("%d:failrn", __LINE__); goto fail_rs; } else { printk("%d:compatible = "%s"rn", __LINE__, str); } // 获取 reg 属性, 也就是获取 u32 类型属性值的方法 ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10); if(ret < 0) { printk("%d:failrn", __LINE__); goto fail_rs; } else { printk("< reg > = < "); for(i=0; i<10; i++) { printk("%#X ", regdata[i]); } printk(">rn"); } // 5、初始化 LED IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0); SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1); SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2); GPIO1_DR = of_iomap(dtsled.nd, 3); GPIO1_GDIR = of_iomap(dtsled.nd, 4); // 初始化 LED 相关寄存器 val = readl(IMX6U_CCM_CCGR1); val &= (~(3<<26)); val |= (3<<26); writel(val, IMX6U_CCM_CCGR1); writel(0X5, SW_MUX_GPIO1_IO03);//io mux writel(0x10B0, SW_PAD_GPIO1_IO03); // set electrical properity val = readl(GPIO1_GDIR); // set bit3, out val |= (1<<3); writel(val, GPIO1_GDIR); val = readl(GPIO1_DR); val &= (~(1<<3));//turn on led writel(val, GPIO1_DR); // 6、初始化等待队列头 init_waitqueue_head(&wait_queue); return 0; fail_rs: fail_findnd: device_destroy(dtsled.class, dtsled.devid); fail_device: class_destroy(dtsled.class); fail_class: cdev_del(&dtsled.cdev); fail_cdev: unregister_chrdev_region(dtsled.devid, 1); fail_devid: return ret; } // 出口函数 static void __exit dtsled_exit(void) { unsigned int val = 0; val = readl(GPIO1_DR); val |= (1<<3); // turn off led writel(val, GPIO1_DR); // 取消地址映射 iounmap(IMX6U_CCM_CCGR1); iounmap(SW_MUX_GPIO1_IO03); iounmap(SW_PAD_GPIO1_IO03); iounmap(GPIO1_DR); iounmap(GPIO1_GDIR); // 删除字符设备 cdev_del(&dtsled.cdev); // 释放设备号 unregister_chrdev_region(dtsled.devid, 1); // 删除设备 device_destroy(dtsled.class, dtsled.devid); // 删除类 class_destroy(dtsled.class); } // 注册出口函数和入口函数 module_init(dtsled_init); module_exit(dtsled_exit); MODULE_LICENSE("GPL");
#include3、实验现象#include #include #include #include int main(int argc, char *argv[]) { int error; int fd; // jiegouti suoyoude chengyuan junwei0 struct pollfd fds = {0}; if(argc != 3) { printf("%s(%d):command error!n", __FILE__, __LINE__); return -1; } fd = open(argv[1], O_RDWR|O_NONBLOCK); if(fd < 0) { printf("%s(%d):fail to open file : %s !!!n", __FILE__, __LINE__, argv[1]); return -1; } fds.fd = fd; fds.events = POLLOUT; // -1 biaoshi keyi yizhixiumian error = poll(&fds, 1, -1); if(error < 0) { printf("%s(%d):poll error.n", __FILE__, __LINE__); } else { if(fds.revents & POLLOUT) { error = write(fd,argv[2],strlen(argv[2])); if(error < 0) { printf("%s(%d):write to file error! n", __FILE__, __LINE__); } else { printf("%s(%d):write to file success! n", __FILE__, __LINE__); } } } error = close(fd); if(error < 0) { printf("%s(%d):close file error! n", __FILE__, __LINE__); } return 0; }
/lib/modules/4.1.15 # modprobe test.ko 228:status = "okay" 239:compatible = "alientek, alphaled" < reg > = < 0x20C406C 0X4 0x20E0068 0X4 0x20E02F4 0X4 0x209C000 0X4 0x209C004 0X4 > /lib/modules/4.1.15 # /lib/modules/4.1.15 # /lib/modules/4.1.15 # /lib/modules/4.1.15 # /lib/modules/4.1.15 # ./app /dev/dtsled 0 & /lib/modules/4.1.15 # echo 1 > /dev/dtsled /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(103):buf=(1 count=2 /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(105):databuf=(1 ) /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(114):write_data=1 /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(103):buf=(0), count=1 /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(118):have woken up. /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(105):databuf=(0) /lib/modules/4.1.15 # /home/jl/linux/imx6ull/linux_driver/z_exercise/test.c(114):write_data=0app.c(46):write to file success! [1]+ Done ./app /dev/dtsled 0 /lib/modules/4.1.15 #
第一次调用app,进程阻塞
echo 1 唤醒app进程
app进程成功唤醒后熄灯



