当前位置: 首页 > news >正文

手机网站智能管理系统吉林省高等级公路建设局 网站

手机网站智能管理系统,吉林省高等级公路建设局 网站,diywap手机微网站内容管理系统,深圳seo优化推广业务员Linux设备驱动模型之字符设备 前面我们有介绍到Linux的设备树#xff0c;这一节我们来介绍一下字符设备驱动。字符设备是在IO传输过程中以字符为单位进行传输的设备#xff0c;而字符设备驱动则是一段可以驱动字符设备驱动的代码#xff0c;当前Linux中#xff0c;字符设备…Linux设备驱动模型之字符设备 前面我们有介绍到Linux的设备树这一节我们来介绍一下字符设备驱动。字符设备是在IO传输过程中以字符为单位进行传输的设备而字符设备驱动则是一段可以驱动字符设备驱动的代码当前Linux中字符设备驱动是怎样的呢下面一起来探讨学习一下。 基础小知识 字符设备框架 如果让你来设计字符设备框架你会怎么设计呢不同的开发者会有不同的需求但是每个人都需要注册字符设备需要有个地方来保存、管理这些设备驱动信息在代码中如何保存会更灵活更合适呢 注册字符设备 当前Linux内核是通过主设备号与次设备号来定义某一个驱动其中主设备号从0到CHRDEV_MAJOR_MAX(512) - 1共512个主设备号而次设备号则是从0到220 - 1(MINORMASK定义)。所以字符设备驱动第一步就是先向内核注册一个设备号 #define DAO_NAME dao static dev_t dao_devt;// 注册字符设备函数调用 alloc_chrdev_region(dao_devt, 0, MINORMASK 1, DAO_NAME); //注册字符设备号/** 注册字符设备函数声明通过下面我们可以知道* dev 是保存主设备号* baseminor 则是代表可以从该索引开始查找可使用的次设备号* count 代表次设备号可搜索的范围* name 则是该字符设备的名称*/ /*** alloc_chrdev_region() - register a range of char device numbers* dev: output parameter for first assigned number* baseminor: first of the requested range of minor numbers* count: the number of minor numbers required* name: the name of the associated device or driver** Allocates a range of char device numbers. The major number will be* chosen dynamically, and returned (along with the first minor number)* in dev. Returns zero or a negative error code.*/ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);而 alloc_chrdev_region() 是怎么来注册字符设备号的呢下面来看看它的实现。 /** Register a single major with a specified minor range.** If major 0 this function will dynamically allocate an unused major.* If major 0 this function will attempt to reserve the range of minors* with given major.**/ static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor,int minorct, const char *name) {struct char_device_struct *cd, *curr, *prev NULL;int ret;int i;/* 先检查需要申请的主设备号是否在合理范围之内0 ~ CHRDEV_MAJOR_MAX(512) - 1 */if (major CHRDEV_MAJOR_MAX) {pr_err(CHRDEV \%s\ major requested (%u) is greater than the maximum (%u)\n,name, major, CHRDEV_MAJOR_MAX-1);return ERR_PTR(-EINVAL);}/* 再检查次设备号是否在合理范围之内0 ~ MINORMASK */if (minorct MINORMASK 1 - baseminor) {pr_err(CHRDEV \%s\ minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n,name, baseminor, baseminor minorct - 1, 0, MINORMASK);return ERR_PTR(-EINVAL);}/* 申请一块内存该内存将是保存字符设备信息的内核通过结构体(struct char_device_struct)来存储该信息*/cd kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);if (cd NULL)return ERR_PTR(-ENOMEM);mutex_lock(chrdevs_lock);/* 如果调用该函数时传递进来的主设备号为0则代表内核动态分配主设备号* 此时通过 find_dynamic_major() 搜索可用的主设备号*/if (major 0) {ret find_dynamic_major(); /* 动态搜索可用的主设备号 */if (ret 0) {pr_err(CHRDEV \%s\ dynamic allocation region is full\n,name);goto out;}major ret; /* 保存搜索到的主设备号到major */}ret -EBUSY;i major_to_index(major);/* 确认主、次设备号是否可用 */for (curr chrdevs[i]; curr; prev curr, curr curr-next) {if (curr-major major)continue;if (curr-major major)break;if (curr-baseminor curr-minorct baseminor)continue;if (curr-baseminor baseminor minorct)break;goto out;}/* 保存字符设备的主、次设备号、设备名称 */cd-major major;cd-baseminor baseminor;cd- minorct;strlcpy(cd-name, name, sizeof(cd-name));/* 将新注册的字符设备添加到chrdevs */if (!prev) {cd-next curr;chrdevs[i] cd;} else {cd-next prev-next;prev-next cd;}mutex_unlock(chrdevs_lock);/* 返回配置了设备号的char_device_struct */return cd; out:mutex_unlock(chrdevs_lock);kfree(cd);return ERR_PTR(ret); }在alloc_chrdev_region中有搜索可用的主设备号以及将设备信息保存到chrdevs它们究竟是什么呢 上面我们就问如果是你来设计这个字符设备驱动框架你会怎么来设计那么内核是怎么设计的呢 // fs/char_dev.c#define CHRDEV_MAJOR_HASH_SIZE 255static struct char_device_struct {struct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct;char name[64];struct cdev *cdev; /* will die */ } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];内核定义了一个叫chrdevs的指针数组来保存字符设备信息这个指针指向的是struct char_device_struct结构体。chrdevs一共255个成员它们是通过主设备号major来进行排序的。在find_dynamic_major()中搜索可用主设备号先从chrdevs数组的末端开始查找到CHRDEV_MAJOR_DYN_END(234)如果有元素为空的则直接返回索引找到有效的主设备号。简单说就是从 234254从后往前找如果数组元素为空则返回数组索引作为主设备号。如果动态分配(234254)的都找不到了则从314~511开始查找注意到chrdevs只有255个元素所以内核通过major_to_index将主设备号转为cherdev的索引实际上就是一个chrdevs的元素分别被两个字符设备共用比如主设备号为59的设备与主设备号为314的公用一个chrdevs元素chrdevs[59]。此时检查chrdevs的元素是否为空为空则直接返回如果元素中的主设备已经被填充了则查找下一次索引的chrdevs元素。 static int find_dynamic_major(void) {int i;struct char_device_struct *cd;/* 234~254是否为空闲的为空闲则返回 */for (i ARRAY_SIZE(chrdevs)-1; i CHRDEV_MAJOR_DYN_END; i--) {if (chrdevs[i] NULL)return i;}/*234~254已经被占用了从314~511开始查找 */for (i CHRDEV_MAJOR_DYN_EXT_START;i CHRDEV_MAJOR_DYN_EXT_END; i--) {for (cd chrdevs[major_to_index(i)]; cd; cd cd-next)/* 确认chrdevs元素对应的大于255的主设备号已经被占用了* 则从再下一个主设备开始查找该设备号已经被占用*/if (cd-major i) break;if (cd NULL)return i;}return -EBUSY; }上述也就是说每个chrdevs元素可以代表两个主设备号一个是元素的索引i一个是i255如果两个都被用了则只能从下一个元素继续查找了。 总结 内核字符设备通过全局静态指针数组chrdevs来保存字符设备信息数组一共255个元素每个元素可保存两个不同主设备号的字符设备信息所以内核一个可申请512个不同的主设备号字符设备。 当驱动调用alloc_chrdev_region()注册设备号之后终端可以看到以下的信息 rootroot:/# cat /proc/devices Character devices:1 mem4 /dev/vc/04 tty5 /dev/tty5 /dev/console5 /dev/ptmx7 vcs10 misc13 input89 i2c90 mtd 116 alsa 128 ptm 136 pts 153 spi 180 usb 189 usb_device 247 ubi0 248 ttyS 249 hidraw 250 rpmb 251 dao // 这里就是我们注册的字符设备可以看到它的主设备号是251名字叫dao 252 watchdog 253 rtc 254 gpiochipBlock devices:8 sd31 mtdblock65 sd66 sd67 sd68 sd69 sd70 sd71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 sd 134 sd 135 sd 179 mmc 254 ubiblock 259 blkext但是此时我们并没有给这个字符设备增加什么操作下面我们继续看如何添加操作。 字符设备初始化 上面我们向内核申请注册了一个字符设备号但是该设备号我们还没有将其与字符设备建立关系那么又是怎么建立的呢 static dev_t dao_devt; // 上面注册得到的主设备号 static struct cdev dao_cdev; // 字符设备结构体 static const struct file_operations dao_fops { // 字符设备操作集 }cdev_init(dao_cdev, dao_fops); // 初始化字符设备结构体并更新其文件字符操作集 cdev_add(dao_cdev, dao_devt, MINORMASK 1); // 将主设备号devt与字符设备 cdev 建立关系实际上就是更新cdev中的主设备号成员信息。同时在cdev_add中cdev信息保存到指针数组cdev_map中。struct file_operations file_operations 是干什么的呢Linux中任意一个设备都是文件针对文件我们会有各种各样的操作比如打开、读、写、关闭等不同的设备它们上述的操作都会不一样所以设备驱动中需要自定义好设备的操作集方便应用层可正确使用该设备。 经过上面的操作之后我们的字符设备已经完成了基本的初始化在/proc/devices中可以看到以注册的字符设备号而针对该设备操作的file_operations也已经填充此时用户空间已经可以对该设备进行操作。 如果完成上述进行操作时会发现/dev目录下并没有相关的字符节点此时只能通过mknod /dev/dao c 251 0这样的命令来完成创建/dev/dao节点执行cat /dev/dao将会看到依次调用到 file_operations 的 open、read、release函数。 那么我们需要如何操作才会在/dev目录下完成节点的注册呢 创建设备节点 在介绍创建节点之前我们先来了解class。在内核中经常会看到xxx_class内核将设备分为字符设备、块设备、网络设备同时也会分class。有相同特性的设备为同一个classclass可以自己创建设备会属于某一个class。那么上面我们完成字符设备的初始化但我们并完成将其与某个class绑定在一起。所以创建设备节点我们先创建一个class #define DAO_CLASS_NAME dao static struct class *dao_class;/* 创建一个class此时在/sys/class目录下看到一个叫dao的文件夹这个就是我们注册的class */ dao_class class_create(THIS_MODULE, DAO_CLASS_NAME); 接着创建设备 static struct device *dao_dev;dao_dev device_create(dao_class, NULL, dao_devt, NULL, DAO_NAME);函数声明 struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...);通过device_create我们将dao_devt这个字符设备号与dao_class绑定在一起并创建一个设备。通过device_create函数声明我们可以知道创建一个设备时可传入该设备的class、父设备、设备号、设备私有数据以及设备名。 device_create主要完成下面的操作 完成struct device结构体的初始化通知平台其他总线系统有新设备加入创建设备uevent节点将设备与class建立链接增加其他设备节点信息如果bus则加入bus创建设备attr_dev;sys目录下创建设备节点dev目录下创建设备节点触发bus的probe 完成上述操作之后设备完成相应的注册用户空间可正常的操作该设备。 框架架构图 例程 // SPDX-License-Identifier: GPL-2.0 /** dao char device test code** Copyright (c) 2022, dao. All rights reserved.*/#include linux/init.h #include linux/module.h #include linux/device.h #include linux/kernel.h #include linux/slab.h #include linux/vmalloc.h #include linux/cdev.h #include linux/sysfs.h #include linux/fs.h#define DAO_NAME dao #define DAO_CLASS_NAME daostatic dev_t dao_devt; static struct class *dao_class; static struct cdev dao_cdev; static struct device *dao_dev;static int dao_dev_open(struct inode *inode, struct file *file) {pr_info(%s\n, __func__);return 0; }static int dao_dev_release(struct inode *inode, struct file *file) {pr_info(%s\n, __func__);return 0; }static ssize_t dao_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) {pr_info(%s\n, __func__);return 0; }static long dao_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {pr_info(%s\n, __func__);return 0; }static const struct file_operations dao_fops {.owner THIS_MODULE,.open dao_dev_open,.release dao_dev_release,.read dao_dev_read,.unlocked_ioctl dao_dev_ioctl, };static int __init dao_dev_init(void) {int ret 0;ret alloc_chrdev_region(dao_devt, 0, MINORMASK 1, DAO_NAME);if (ret 0) {pr_err(Error: failed to register dao_dev, err: %d\n, ret);return ret;}cdev_init(dao_cdev, dao_fops);cdev_add(dao_cdev, dao_devt, MINORMASK 1);pr_info(%s: major %d\n, __func__, MAJOR(dao_devt));dao_class class_create(THIS_MODULE, DAO_CLASS_NAME);if (IS_ERR(dao_class)) {pr_err(Error: failed to register dao_dev class\n);ret PTR_ERR(dao_class);goto failed1;}dao_dev device_create(dao_class, NULL, dao_devt, NULL, DAO_NAME);if (!dao_dev)goto failed2;return 0;failed2:class_destroy(dao_class); failed1:cdev_del(dao_cdev);unregister_chrdev_region(dao_devt, MINORMASK 1);return ret; }static void __exit dao_dev_exit(void) {device_destroy(dao_class, dao_devt);class_destroy(dao_class);cdev_del(dao_cdev);unregister_chrdev_region(dao_devt, MINORMASK 1); }module_init(dao_dev_init) module_exit(dao_dev_exit)驱动注册 上面介绍了设备的注册但是驱动的注册流程又是怎样的呢下面介绍来介绍一下驱动注册通过driver_register()函数完成而它主要进行下面几个事情 驱动都是挂载在总线上的同时驱动也是使用名字进行区分的所以需要确认该总线上没有同样名字的驱动将该驱动挂载到总线上将驱动添加到klist_drivers这个链表然后和device进行匹配操作创建驱动的属性配置节点 具体的device与driver匹配操作我们下一章节进行介绍。
http://www.dnsts.com.cn/news/27797.html

相关文章:

  • 陕西建设网网站集群济南app开发制作
  • 设计网站都有什么昆明专业网络营销公司
  • 做投标需要知道什么网站自我介绍网页设计代码
  • 使用他人商标做网站搜索词房屋租赁合同
  • 做soho一定要做网站吗加强网站建设与管理的通知
  • 网站页面上的悬浮窗怎么做网页截图快捷键可拉动
  • 什么程序做教育网站好seo和sem分别是什么
  • 博达网站建设流程被自考本科坑了一辈子
  • 哈尔滨巨耀网站建设wordpress在裁剪
  • 免费建站的手机app外贸网站如何做
  • 手机回收站网页设计需要哪些软件
  • 专门做酒店自助餐的网站网络营销渠道策略包括
  • 安装了lnmp怎么做网站wordpress固定链接分类
  • 做网站系统的销售怎么做网站策划主要做什么工作
  • 有哪些网站做明星周边贵州百度竞价网页设计
  • 襄阳论坛网站建设爱站长工具综合查询
  • 东阳网站建设价格国内免费自建网站
  • 广西建设科技在线网站wordpress数字主题
  • 电子商务网站建设与管理的背景设计网名的软件
  • 如何浏览国外网站?wordpress 获取子页面内容
  • 秦皇岛网站制作服务回收类型网站如何做
  • 填手机号码的广告宝安seo优化公司
  • 即墨网站推广海珠建设网站
  • 前端怎么在猪八戒网站接单做网站开发 参考文献
  • 建设网店网站wordpress 文章样式
  • 自己做网站卖机器设备酒店网站素材
  • 美食个人网站设计作品o2o典型代表网站
  • 科普互联网站建设淘宝客网站建设
  • 做不锈钢百度网站哪个比较好备案通
  • 深圳做网站多钱广州最新消息今天