首页 > 其他分享 >字符设备驱动-12.misc杂项字符设备驱动

字符设备驱动-12.misc杂项字符设备驱动

时间:2023-08-18 17:11:39浏览次数:33  
标签:字符 12 struct misc dev file 驱动 include 设备

1 引入misc device

1.1传统cdev方式

char_drv.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
static int led_major;
struct cdev cdev;
static int led_drv_open(struct inode *inode, struct file *file)
{
    return 0;
}
static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    return 0;
}
static struct file_operations led_drv_fops = {
    .owner  =   THIS_MODULE,
    .open   =   led_drv_open,
    .write	=	led_drv_write,
};
static void led_setup_cdev(void)
{
  int err, devno = MKDEV(led_major, 0);//index 为从设备号
  cdev_init(&cdev, &led_drv_fops);
  cdev.owner = THIS_MODULE;
  cdev.ops = &led_drv_fops;
  err = cdev_add(&cdev, devno, 1);//devno 为第一个设备号,1为数量
  if (err)
    printk(KERN_NOTICE "Error %d adding", err);
}
static int led_drv_init(void)
{
    int result;
    dev_t devno;
    struct class *led_class;
    struct device *dev;
    devno=MKDEV(led_major,0);
    if(led_major)//静态申请设备号
        result=register_chrdev_region(devno,1,"led1_dev");
    else
    {
        result = alloc_chrdev_region(&devno,0,1,"led1_dev");//动态申请设备号
        led_major = MAJOR(devno);
    }
    if(result<0)
    {
        printk (KERN_WARNING "hello: can't get major number %d\n", led_major);
        return result;
    }
    led_setup_cdev();
    led_class = class_create(THIS_MODULE, "led_class");
	dev = device_create(led_class, NULL, devno, NULL, "%s", "led_dev");
	if (IS_ERR(dev)) {
		dev_err(dev, "device create failed error code(%ld)\n", PTR_ERR(dev));
		return PTR_ERR(dev);
	}
    return 0;
}
static void led_drv_exit(void)
{
    device_destroy(led_class, dev);     /* remove the device */
    class_destroy(led_class);           /* remove the device class */
    cdev_del(&cdev);
    unregister_chrdev_region(MKDEV(led_major,0),1);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
总结流程:
A:创建设备号。MKDEV(major_no,0),其值为一个整数。因为linux中使用设备号来关联相应的设备和设备对于的驱动程序。
B:注册设备号。register_chrdev_region(devno,1,"led1_dev")或者alloc_chrdev_region(&devno,0,1,"led1_dev");//动态申请设备号
C:初始化并关联file_operations结构体。  cdev_init(&cdev, &led_drv_fops);
D:添加字符设备到内核。int cdev_add(struct cdev *p, dev_t dev, unsigned count),
E:移除字符设备及设备号。cdev_del(&cdev); unregister_chrdev_region(MKDEV(led_major,0),1);

kdev_t.h
image

上面涉及到的API可以在函数linux/fs/char_dev.c中找到定义。

1.2 misc device方式

使用misc_register,在加载模块时会自动创建设备节点,为主设备号为10的字符设备。使用misc_deregister,在卸载模块时会自动删除设备节点。因此无需调用cdev这一套框架流程,无需调用class_create和device_create操作。misc_register时会自行调用了 class_create(), device_create() 因此 /sys/class/misc 类会被创建, /dev/下的设备节点也会自动创建。
/proc/misc记录了系统中所有加载的misc设备:
image

点击查看代码
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
struct led_dev {
	struct miscdevice miscdev;
	void *data;
}
struct led_dev my_led;
static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	return 0;
}
static int leds_open(struct inode *inode, struct file *filp)
{
	//filp->private_data = &my_led;
	return 0;
}
static int leds_release(struct inode *inode, struct file *filp)
{
	return 0;
}
static ssize_t leds_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	  return 1;
}
static struct file_operations leds_fops =
{
	.owner   = THIS_MODULE,
	.read    = leds_read,
	.ioctl   = leds_ioctl,
	.open    = leds_open,
	.release = leds_release
};
static int __init dev_init(void)
{
	struct miscdevice *miscdev = &my_led.miscdev;

	miscdev->minor = MISC_DYNAMIC_MINOR,
	miscdev->name = "misc_leds",
	miscdev->fops = &leds_fops,
	miscdev->parent = NULL;
	int ret = misc_register(miscdev);
	return ret;
}
static void __exit dev_exit(void)
{
	misc_deregister(&my_led.miscdev);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

2 misc杂项设备解析

源代码位置driver/char/misc.c,主设备号固定为10,所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。

2.1 misc_init

image
misc子系统的初始化是利用subsys_initcall进行子系统初始化,首先创建/proc/misc条目,对应cat /proc/misc可以看到所有misc设备信息,cat /proc/misc于是就会调用misc_seq_ops中的misc_seq_show函数,可以看到刚好为misc设备的次设备号和名字信息。
image
主设备号固定为10,调用class_create创建/sys/class/misc, 调用register_chrdev注册字符设备,添加file_operations。register_chrdev如果传入主设备号,则静态注册,否则动态注册返回主设备号。
image
image
image

2.2 misc设备注册过程

image

MISC_DYNAMIC_MINOR = 255,使用者调用misc_register时一般会次设备号传入MISC_DYNAMIC_MINOR,那么会自动分配次设备号;否则遍历misc_list链表,看这个次设备号以前有没有被用过,如果次设备号已被占有则退出返回-EBUSY。
得到这个次设备号后set_bit(i, misc_minors);设置位图中相应位为1。device_create_with_groups等同于device_create创建设备节点。
最后将list节点添加到misc_list链表中。
cat /sys/class可以看到所有驱动中调用class_creat()函数的模块,cat /sys/class/misc则可以看到所有misc杂项驱动模块。ls /dev/*可以看到对应的设备节点
image
image
image

2.3 misc设备卸载过程

image
从misc_list链表中删除节点list,然后删除设备节点。释放位图相应位清0,以便次设备号留给下一个模块使用。

2.4 misc设备打开过程

image
当用户调用open("/dev/xxx")时,由于misc设备主设备号都为10,那么会统一进入到misc_open,那么会根据次设备号来区分不同的misc设备,首先iminor(inode)取出次设备号,i_rdev是对应具体misc设备的设备号dev_t。
image
然后遍历misc_list链表,找到与minor次设备号相匹配的misc device,找到后将file_operations(简称fops)暂存到new_fops。如果匹配不到,则请求加载这个次设备号对应的模块。request_module表示让linux系统的用户空间调用/sbin/modprobe函数加载名为char-major-%d-%d的模块。
image
匹配成功后file->private_data = c;表示将链表中匹配出的miscdevice作为file->private_data(后面会介绍作用)

/*
* Place the miscdevice in the file's
* private_data so it can be used by the
* file operations, including f_op->open below
*/
file->private_data = c;

最后将暂存的new_fops赋值给file->f_op,调用具体的misc模块的fops:

file->f_op->open(inode, file);

image

3 如何从fops中获取模块设备信息

引入:

struct xxx_dev {
	struct miscdevice miscdev;
	void *data;
};

static long dwa_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct xxx_dev *m = container_of(filp->private_data, struct xxx_dev, miscdev);
	...
	return 0;
}
static int __init xxx_init(void) {
	struct xxx_dev *m;
	...
}

方法1:(对于misc设备)

可以看到如果我们想要重file_oprations获取设备入口,可以通过如下方式:

struct xxx_dev *m = container_of(filp->private_data, struct xxx_dev, miscdev);

前面2.4讲过了匹配成功后file->private_data = c;表示将链表中匹配出的miscdevice作为file->private_data.

方法2:(对于cdev设备)

struct xxx_dev {
	struct cdev cdev;
	void *data;
};
int xxx_open(struct inode *inode, struct file *file)
{
	int ret = 0;
	struct xxx_dev *m;
	m = container_of(inode->i_cdev, struct xxx_dev, cdev);
}

inode的i_cdev指向的即为cdev结构体。调用container_of即可获取设备信息。

方法3:xxx_open中保存设备信息

struct xxx_dev {
	struct cdev cdev;
	void *data;
	struct resource* res;
};
struct xxx_dev *m;
static long keyscan_ioctl(struct file *file, unsigned int cmd,
			  unsigned long arg)
{
	struct xxx_dev *m = file->private_data;
	uint32_t res_size = (uint32_t)resource_size(m->res);
}
int xxx_open(struct inode *inode, struct file *file)
{
	file->private_data = m;
}

file->private_data = m保存设备信息。

标签:字符,12,struct,misc,dev,file,驱动,include,设备
From: https://www.cnblogs.com/fuzidage/p/17638448.html

相关文章

  • burpsuite靶场----SQL注入12----oracle的布尔盲注
    burpsuite靶场----SQL注入12----oracle的布尔盲注靶场地址https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors正式开始1.找到注入点为cookie中的TrackingId2.因为oracle使用||进行连接的所以先判断闭合payload:TrackingId=7zHLwisTii2Zhhp......
  • ASEMI逆变器专用整流桥GBU812参数,GBU812规格
    编辑-ZGBU812参数描述:型号:GBU812最大峰值反向电压(VRRM):1200V平均整流正向电流(IF):8A正向浪涌电流(IFSM):200A工作接点温度和储存温度(TJ,Tstg):-55to+150℃最大热阻(RθJC):2.2℃/W正向电压(VF):1.1V最大瞬时反向电流(IR):5uA热容值i2t:166A2S绝缘耐压Visol:2500V  ......
  • SPI驱动0.96寸OLED单色屏刷新率测试以及代码优化改进,方法适用于SPI驱动其他设备
    目前嵌入式当中OLED常用驱屏方式有两种:SPI或IIC。以速度来讲,SPI速度相较于IIC会快上一些,硬件IIC相较于模拟IIC速度又会快上一些。此外还有模拟SPI的,但该种用法我遇到较少,本文就硬件SPI驱动OLED屏幕做一个简单的刷新率测试。 测试硬件平台:CH32V307VCT6+杜邦线连接0.96寸SPI接口O......
  • Spring高手之路12——BeanDefinitionRegistry与BeanDefinition合并解析
    1.什么是BeanDefinitionRegistry?  BeanDefinitionRegistry 是一个非常重要的接口,存在于 Spring 的 org.springframework.beans.factory.support 包中,它是 Spring 中注册和管理 BeanDefinition 的核心组件。 BeanDefinition。在 Spring 中,一个 Bean 就是一个被 Sp......
  • 华秋硬创联合安创加速器,加速和创新赋能技术驱动型创业者
      01大赛介绍中国硬件创新创客大赛始于2015年,由深圳华秋电子有限公司主办,至今已经成功举办八届,赛事范围覆盖华南、华东、华北三大地区,超10个省市区域。大赛影响了超过45万工程师群体,吸引了35000多名硬创先锋报名参加线上线下培训会,并成功聚集了400多家生态合作伙伴,与500多......
  • GBU812-ASEMI新能源专用整流桥GBU812
    编辑:llGBU812-ASEMI新能源专用整流桥GBU812型号:GBU812品牌:ASEMI封装:GBU-4恢复时间:>50ns正向电流:80A反向耐压:1200V芯片个数:4引脚数量:4类型:整流桥特性:插件桥堆、薄体整流桥浪涌电流:200A正向压降:1.10V封装尺寸:如图工作温度:-55°C~150°CGBU812特性超快速切换,实现高效率。反向泄漏低。高......
  • mysql 如何 在用逗号分隔开的字符串中精确查询出 多个字符串
    例如:在MySQL字段中有值为1,2,3,4,5,6,8,1099,98,91,95,97两条数据而我想查出两个数据1和95如果使用find_in_set查询则无法查询出来  则需要使用mysql内置方法SELECT*FROMpay_configWHERECONCAT(',',product_ids,',')REGEXP',(1|43|15),'concat......
  • GBU812-ASEMI新能源专用整流桥GBU812
    编辑:llGBU812-ASEMI新能源专用整流桥GBU812型号:GBU812品牌:ASEMI封装:GBU-4恢复时间:>50ns正向电流:80A反向耐压:1200V芯片个数:4引脚数量:4类型:整流桥特性:插件桥堆、薄体整流桥浪涌电流:200A正向压降:1.10V封装尺寸:如图工作温度:-55°C~150°CGBU812特性超快速切换,实现高效......
  • ID为12848的进程未运行,解决方法
    1.win+R输入eventvwr,打开事件查看器打开windows事件查看器查看报错具体信息,跟具文件位置判断,认证服务没有启动。2.查看具体报错信息,发现由于项目框架为asp.net3.1,本地并未安装该框架,点击下载安装包。3.不报错,运行成功,......
  • 3 字符串操作
    3字符串操作常用的字符串操作相关的方法:s.split()字符串切割s.substr(start,len)字符串切割,从start开始切,切len个字符s.substring(start,end)字符串切割,从start切割到ends.length字符串长度s.charAt(i)第i索引位置的字符s[i]s.indexOf('xxx')返回xxx......