红黑树、一张表、三个接口
int epfd=epoll_create(int size--无意义,>0即可)----------成功:返回根节点对应文件描述符,失败:-1
a.struct epoll_event event;定义事件结构体
b.struct epoll_event events[10];定义存放就绪事件描述符的数组
c.添加准备就绪事件进入epoll,如:
event.events = EPOLLIN; // 读事件
event.data.fd = fd1;
epoll_ctl(epfd, EPOLL_CTL_ADD---控制方法, fd1, &event)
epoll_wait(epfd, events, 10, -1--不关心是否超时);
返回值:
>0:准备好的文件描述符的个数
=0:超时
<0:失败
异步IO方式,linux预留了一个信号SIGIO用于进行信号驱动IO,当硬件数据准备就绪后会发起一个硬件中断,在中断的处理函数中向当前进程发送一个SIGIO信号。进程收到SIGIO信号后执行信号处理函数,在信号处理函数中将数据读走即可。
1)打开设备文件
2)注册信号的信号处理函数--signal(SIGIO,信号处理函数)
3)回调驱动中的fasync方法,完成发送信号之前的准备工作
a.获取文件描述符属性
int flags=fcntl(fd,F_GETFL);
b.在文件描述符表的flags中添加FASYNC
fcntl(fd,F_SETFL,flags|FASYNC);
c.设置fd对应的驱动程序发送SIGIO信号只发送给当前进程
fcntl(fd,F_SETOWN,getpid());
4)注意:不能让主程序结束
1)定义一个异步对象指针
struct fasync_struct *fp;
2)异步操作方法
int mycdev_fasync(int fd, struct file *file, int on) // 异步操作方法
{
// 完成发送信号之前的准备工作
fasync_helper(fd, file, on, &fp);
return 0;
}
需要在操作方法结构体对象中加 .fasync = mycdev_fasync,
3)向进程发送信号
参数: fp:异步对象的二级指针
sig:要发生的信号 SIGIO
band:发送信号时添加的事件标志 POLL_IN表述读数据操作
//发送信号
kill_fasync(&fp,SIGIO,POLL_IN);
1)设备树(DeviceTree/DT/of)是用来保存设备信息的一种树形结构
2)设备树的源码是独立于linux内核源码存在的
3)设备树上的设备信息在内核启动后被内核解析,加载到内核空间
4)设备树上的节点(包含属性和子节点),保存设备的设备信息;设备的信息由多个属性(以链表形式存在,属性是键值对)共同描述.
为了让驱动可以兼容更多硬件,不在驱动中指定设备信息,让驱动中获取设备树上的设备信息,基于这些设备信息完成硬件的控制
设备树linux官方手册:Device Tree Usage - eLinux.org
1)根据设备树节点的名字解析指定的设备树节点信息
struct device_node *dnode;
dnode=of_find_node_by_name(NULL(默认从根节点解析),"mynode");
返回值:成功返回目标节点首地址,失败返回NULL
测试:
printk("name=%s,value=%s\n",dnode->properties->name,(char *)dnode->properties->value);
2)根据设备树节点路径解析设备树节点信息
3)根据节点的厂商信息解析指定的节点
dnode=of_find_compatible_node(NULL(默认从根节点解析),NULL(设备类型),:compatible值);
4)将大端字节序32位的数据转换成主机字节序
__u32 __be32_to_cpup(const __be32 *p)
printk("name=%s,value=%x %x\n",dnode->properties->next->next->name,
__be32_to_cpup((u32 *)dnode->properties->next->next->value),
__be32_to_cpup((u32 *)dnode->properties->next->next->value+1));
返回值:成功返回属性对象指针,失败返回NULL
struct property *pr;
int len;
pr=of_find_property(dnode,"uint",&len);
测试:
printk("name=%s value=%x %x\n",pr->name,__be32_to_cpup((u32 *)pr->value),
__be32_to_cpup((u32 *)pr->value+1));
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/wait.h>
- #include <sys/ioctl.h>
- #include <sys/select.h>
- #include <sys/epoll.h>
- /* According to earlier standards */
- #include <sys/time.h>
-
- int main(int argc, char const *argv[])
- {
- int fd1, fd2, epfd;
- struct epoll_event event; // 用于操作epoll
- struct epoll_event events[10]; // 存放就绪事件描述符的数组
- char buf[128] = {0};
- // 创建epoll句柄
- epfd = epoll_create(1);
- if (epfd < 0)
- {
- printf("epoll_create filed\n");
- exit(-1);
- }
- // 打开设备文件
- fd1 = open("/dev/input/mouse0", O_RDWR);
- if (fd1 < 0)
- {
- printf("打开鼠标设备文件失败\n");
- exit(-1);
- }
- fd2 = open("/dev/mycdev0", O_RDWR);
- if (fd2 < 0)
- {
- printf("打开鼠标设备文件失败\n");
- exit(-1);
- }
- // 添加准备就绪事件进入epoll;
- event.events = EPOLLIN; // 读事件
- event.data.fd = fd1;
- if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &event) < 0)
- {
- printf("epoll_ctl add filed\n");
- }
- event.events = EPOLLIN; // 读事件
- event.data.fd = fd2;
- if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd2, &event) < 0)
- {
- printf("epoll_ctl add filed\n");
- }
-
- // 监听事件是否发生
- while (1)
- {
- // 如果成功,ret接收返回的事件个数,把就绪的事件放在events数组中
- int ret = epoll_wait(epfd, events, 10, -1);
- if (ret < 0)
- {
- printf("epoll_wait filed\n");
- exit(-1);
- }
- int i;
- // 循环遍历数组,做事件的处理
- for (i = 0; i < ret; i++)
- {
- if (events[i].events & EPOLLIN)//判断发生的事件是不是读事件
- {
- read(events[i].data.fd, buf, sizeof(buf));
- printf("buf:%s\n", buf);
- }
- }
- }
- close(fd1);
- close(fd2);
- return 0;
- }
proc1.c
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/wait.h>
- #include <sys/ioctl.h>
- #include <sys/select.h>
- #include <sys/epoll.h>
- /* According to earlier standards */
- #include <sys/time.h>
- int fd; // 存放就绪事件描述符的数组
- char buf[128] = {0};
- // 定义信号处理函数
- void sigio_handler(int sig)
- {
- read(fd, buf, sizeof(buf));
- printf("buf:%s\n", buf);
- }
- int main(int argc, char const *argv[])
- {
-
- // 打开设备文件
- fd = open("/dev/myled0", O_RDWR);
- if (fd < 0)
- {
- printf("打开设备文件失败\n");
- exit(-1);
- }
- // 注册SIGIO信号的信号处理函数
- signal(SIGIO, sigio_handler);
- // 回调驱动中的fasync方法,完成发送信号之前的准备工作
- int flags = fcntl(fd,F_GETFL); // 获取文件描述符属性
- fcntl(fd,F_SETFL,flags|FASYNC); // 在文件描述符表的flags中添加FASYNC,就可以回调fasync方法
- fcntl(fd,F_SETOWN,getpid());//驱动发送信号只发送给当前进程
- while(1)
- {
- printf("aaaaa\n");
- sleep(1);
- }
- close(fd);
-
- return 0;
- }
proc2.c
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <sys/ioctl.h>
-
-
- int main(int argc, char const *argv[])
- {
-
- char buf[128] = "hello world";
- int fd = open("/dev/myled0", O_RDWR);
- if (fd < 0)
- {
- printf("打开设备文件失败\n");
- exit(-1);
- }
- write(fd, buf, sizeof(buf));
-
- close(fd);
-
- return 0;
- }
fasync.c
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/cdev.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/uaccess.h>
- #include <linux/slab.h>
- #include <linux/wait.h>
- #include <linux/poll.h>
- struct cdev *cdev;
- char kbuf[128] = {0};
- unsigned int major = 0;
- unsigned int minor = 0;
- dev_t devno;
- module_param(major, uint, 0664); // 方便在命令行传递major的值
- struct class *cls;
- struct device *dev;
- struct fasync_struct *fp; // 定义一个异步对象指针
-
- // 封装操作方法
- int mycdev_open(struct inode *inode, struct file *file)
- {
- printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
- return 0;
- }
- ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
- {
- int ret;
- // 判断IO方式
- if (file->f_flags & O_NONBLOCK) // 非阻塞
- {
- }
- else // 阻塞
- {
- }
- ret = copy_to_user(ubuf, kbuf, size);
- if (ret)
- {
- printk("copy_to_user err\n");
- return -EIO;
- }
-
- return 0;
- }
- ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
- {
-
- int ret;
- // 从用户拷贝数据,模拟硬件数据
- ret = copy_from_user(kbuf, ubuf, size);
- if (ret)
- {
- printk("copy_from_user err\n");
- return -EIO;
- }
- //发送信号(异步对象二级指针,要发生的信号,发送信号时添加事件的标志位)
- kill_fasync(&fp,SIGIO,POLL_IN);
- return 0;
- }
-
- int mycdev_fasync(int fd, struct file *file, int on) // 异步操作方法
- {
- // 完成发送信号之前的准备工作
- fasync_helper(fd, file, on, &fp);
- return 0;
- }
- int mycdev_close(struct inode *inode, struct file *file)
- {
- printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
- return 0;
- }
- // 定义一个操作方法结构体对象并且初始化
- struct file_operations fops = {
- .open = mycdev_open,
- .read = mycdev_read,
- .write = mycdev_write,
- .fasync = mycdev_fasync,
- .release = mycdev_close,
- };
- static int __init mycdev_init(void)
- {
-
- int ret;
- // 为字符设备驱动对象申请空间
- cdev = cdev_alloc();
- if (cdev == NULL)
- {
- printk("字符设备驱动对象申请空间失败\n");
- ret = -EFAULT;
- goto out1;
- }
- printk("申请对象空间成功\n");
- // 初始化字符设备驱动对象
- cdev_init(cdev, &fops);
- // 申请设备号
- if (major > 0) // 静态指定设备号
- {
- ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");
- if (ret)
- {
- printk("静态申请设备号失败\n");
- goto out2;
- }
- }
- else if (major == 0) // 动态申请设备号
- {
- ret = alloc_chrdev_region(&devno, minor, 3, "myled");
- if (ret)
- {
- printk("动态申请设备号失败\n");
- goto out2;
- }
- major = MAJOR(devno); // 获取主设备号
- minor = MINOR(devno); // 获取次设备号
- }
- printk("申请设备号成功\n");
- // 注册字符设备驱动对象
- ret = cdev_add(cdev, MKDEV(major, minor), 3);
- if (ret)
- {
- printk("注册字符设备驱动对象失败\n");
- goto out3;
- }
- printk("注册字符设备驱动对象成功\n");
- // 向上提交目录信息
- cls = class_create(THIS_MODULE, "myled");
- if (IS_ERR(cls))
- {
- printk("向上提交目录失败\n");
- ret = -PTR_ERR(cls);
- goto out4;
- }
- printk("向上提交目录成功\n");
- // 向上提交设备节点信息
- int i;
- for (i = 0; i < 3; i++)
- {
- dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
- if (IS_ERR(dev))
- {
- printk("向上提交设备节点信息失败\n");
- ret = -PTR_ERR(dev);
- goto out5;
- }
- }
- printk("向上提交设备信息成功\n");
- return 0;
- out5:
- // 释放前一次提交成功的设备信息
- for (--i; i >= 0; i--)
- {
- device_destroy(cls, MKDEV(major, i));
- }
- class_destroy(cls); // 释放目录
- out4:
- cdev_del(cdev);
- out3:
- unregister_chrdev_region(MKDEV(major, minor), 3);
- out2:
- kfree(cdev);
- out1:
- return ret;
- }
- static void __exit mycdev_exit(void)
- {
- // 释放节点信息
- int i;
- for (i = 0; i < 3; i++)
- {
- device_destroy(cls, MKDEV(major, i));
- }
- // 销毁目录
- class_destroy(cls);
- // 注销驱动对象
- cdev_del(cdev);
- // 释放设备号
- unregister_chrdev_region(MKDEV(major, minor), 3);
- // 释放对象空间
- kfree(cdev);
- }
- module_init(mycdev_init);
- module_exit(mycdev_exit);
- MODULE_LICENSE("GPL");