驱动程序:
创建一个bus_type, 在这个bus下分别注册driver和device,之后创建设备的属性文件用于poll测试
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/kthread.h>
#include <linux/async.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/devinfo.h>
int sp_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
struct bus_type sp_bus = {
.name = "sp_bus",
.match = sp_bus_match,
};
struct sp_device {
struct device device;
};
struct sp_driver {
struct device_driver driver;
};
void spdev_release(struct device *dev)
{
}
int sp_driver_remove(struct device *dev)
{
return 0;
}
void sp_driver_shutdown(struct device *dev)
{
}
ssize_t poll_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "poll show\n\r");
}
ssize_t poll_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
/** poll或者epoll可用
* poll使用 POLLPRI 事件; epoll使用 EPOLLWAKEUP 事件
* 调用poll("value"); epoll_wait() 之前要调用一次read("value"), 原因不明
*
* 即:往poll节点写数据时,唤醒value上的poll休眠
*/
sysfs_notify(&dev->kobj, NULL, "value");
return count;
}
static DEVICE_ATTR_RW(poll);
ssize_t value_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "5A5A\n\r");
}
static DEVICE_ATTR_RO(value);
int sp_driver_probe(struct device *dev)
{
/* 3. 创建device的属性文件 */
device_create_file(dev, &dev_attr_poll);
device_create_file(dev, &dev_attr_value);
return 0;
}
int sp_register_device(struct sp_device *sp_dev)
{
if (sp_dev->device.init_name == NULL ||
sp_dev->device.release == NULL) {
printk("register sp_device failed!\n");
return -EINVAL;
}
sp_dev->device.bus = &sp_bus;
return device_register(&sp_dev->device);
}
int sp_register_driver(struct sp_driver *sp_drv)
{
if (sp_drv->driver.name == NULL) {
printk("register sp_driver failed!\n");
return -EINVAL;
};
sp_drv->driver.bus = &sp_bus;
sp_drv->driver.owner = THIS_MODULE;
sp_drv->driver.probe = sp_driver_probe;
sp_drv->driver.shutdown = sp_driver_shutdown;
sp_drv->driver.remove = sp_driver_remove;
return driver_register(&sp_drv->driver);
}
static int __init sys_poll_init(void)
{
struct sp_device *spdev;
struct sp_driver *spdrv;
spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
spdrv = kzalloc(sizeof(*spdrv), GFP_KERNEL);
if (spdev == NULL && spdev == NULL) {
printk("alloc for device or driver failed!\n");
return -ENOMEM;
}
/* 1. 注册一个bus_type */
bus_register(&sp_bus);
/* 2. 在这个bus下注册device和driver */
spdev->device.init_name = "spdev0";
spdev->device.release = spdev_release;
sp_register_device(spdev);
spdrv->driver.name = "spdrv";
sp_register_driver(spdrv);
return 0;
}
static void __exit sys_poll_exit(void)
{
//to do ...
}
module_init(sys_poll_init);
module_exit(sys_poll_exit);
MODULE_LICENSE("GPL");
APP
/** 测试用例:
* 测试驱动程序 sys_notify() 函数
* 测试方式:
* ./test.out &
* echo xxx > /sys/bus/sp_bus/devices/spdev0/poll 或者 等待10s
* desc:
* 使用poll在 /sys/bus/sp_bus/devices/spdev0/value 属性文件上进入睡眠,10s超时
* 往 /sys/bus/sp_bus/devices/spdev0/poll 写值时,驱动程序调用 sys_notify() 唤醒poll
* value 返回 5A5A
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <sys/epoll.h>
#define FILE "/sys/bus/sp_bus/devices/spdev0/value"
#define POLL_CNT 6
#define TIME_OUT (10*1000)
int main(int argc, char **argv)
{
int fd, ret, readCnt;
char value[4] = {0};
fd = open(FILE, O_RDONLY);
/** need read dummy once */
read(fd, value, 4);
/** epoll */
int epollFd, pollCnt = POLL_CNT;
struct epoll_event event;
event.events = EPOLLWAKEUP | EPOLLERR;
epollFd = epoll_create(1);
ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &event);
if (ret) {
printf("epoll_ctl failed!\n");
}
while (pollCnt--) {
ret = epoll_wait(epollFd, &event, 1, TIME_OUT);
if (ret == 0) {
printf("epoll_wait time out!\n");
} else if (ret < 0) {
printf("epoll_wait failed!\n");
} else {
lseek(fd, 0, SEEK_SET);
readCnt = read(fd, value, sizeof(value));
printf("now can read! value is %s\n", value);
}
}
close(fd);
return 0;
}