自动创建/dev设备节点
因为在linux里面有两种设备文件系统,devfs和sysfs
devfs
1) 需要手动创建设备节点mknod fasync_dev c 250 0
2) 或者在fasync_dev_init函数中添加:
#ifdef CONFIG_DEVFS_FS //支持devfs文件系统,在内核里面配置
devfs_mk_cdev(设备号, S_IFCHR | S_IRUGO | S_IWUSR, 设备名称)
#endif
sysfs
sysfs通过class_create和device_create在设备树中创建相应的设备,应用层udev会自动根据设备树的变化生成相应的设备节点。
cdev注册
1、申请设备号
动态 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, unsigned const char *name)
静态 int register_chrdev_region(dev_t from, unsigned count ,const char *name)
都使用注销 void unregister_chrdev_region(dev_t from, unsigned count)
2、申请内存空间
struct cdev *cdev_alloc(void)
3、初始化cdev结构体
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
4、添加cdev
int cdev_add(struct cdev *p, dev_T dev, unsigned count)
5、删除cdev
void cdev_del(struct cdev *p)
pdev
1、找到节点
static inline struct device_node *of_find_node_by_path(const char *path)
pdev的节点为pdev->dev.of_node。
of_property_read_s32(pdev->dev.of_node, const char *propname, s32 *out_value);
2、找到节点中属性
static inline struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
3、读取属性中的32位值,或者字符串
读一个:
static inline int of_property_read_s32(const struct device_node *np,
const char *propname,
s32 *out_value)
static inline int of_property_read_string(const struct device_node *np,
const char *propname,
const char **out_string)
读一串:
of_property_read_string_array(const struct device_node * np,
const char * propname,
const char * * out_strs, size_t sz)
static inline int of_property_read_u32_array(const struct device_node *np,
const char *propname,
u32 *out_values, size_t sz)
设备树替代pdev
pdev通过platform_driver中的id_table和platform_device的name匹配执行probe
const struct platform_device_id *id_table;
platform_match_id(pdrv->id_table, pdev)
if (strcmp(pdev->name, id->name) == 0)
dts通过platform_driver中driver成员的of_match_table和dts中节点的compatible匹配执行probe
static const struct of_device_id of_match_leds[] = {
{ .compatible = ""},
};
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
.of_match_table = of_match_leds, /* 能支持哪些来自于dts的platform_device */
}
};
https://www.cnblogs.com/zhu-g5may/p/10316990.html
1、Linux中引入模块机制有什么好处?
首先,模块是预先注册自己以便服务于将来的某个请求,然后他的初始化函数就立即结束。换句话说,模块初始化函数的任务就是为以后调用函数预先作准备。
好处:
1) 应用程序在退出时,可以不管资源的释放或者其他的清除工作,但是模块的退出函数却必须仔细此撤销初始化函数所作的一切。
2) 该机制有助于缩短模块的开发周期。即:注册和卸载都很灵活方便。
2、Linux设备中字符设备与块设备有什么主要的区别?请分别列举一些实际的设备说出它们是属于哪一类设备。
字符设备:字符设备是个能够像字节流(类似文件)一样被访问的设备,只能一个一个字节读写数据,不能随机读取设备内存的数据。字符设备驱动程序通常至少实现open,close,read和write系统调用。字符终端、串口、鼠标、键盘、摄像头、声卡和显卡等就是典型的字符设备。
块设备:可以从设备的任意位置读取一定长度数据的设备。硬盘、磁盘、U盘、SD卡都属于块设备。
3、查看驱动模块中打印信息应该使用什么命令?如何查看内核中已有的字符设备的信息?如何查看正在使用的有哪些中断号?
1) 查看驱动模块中打印信息的命令:dmesg。
2) 查看字符设备信息可以用lsmod 和modprobe,lsmod可以查看模块的依赖关系,modprobe在加载模块时会加载其他依赖的模块。
3) 显示当前使用的中断号cat /proc/interrupt。
4、insmod 一个驱动模块,会执行模块中的哪个函数?rmmod呢?这两个函数在设计上要注意哪些?遇到过卸载驱动出现异常没?是什么问题引起的?
insmod调用init函数,rmmod调用exit函数。这两个函数在设计时要注意什么?卸载模块时曾出现卸载失败的情形,原因是存在进程正在使用模块,检查代码后发现产生了死锁的问题。
要注意在init函数中申请的资源在exit函数中要释放,包括存储,ioremap,定时器,工作队列等等。也就是一个模块注册进内核,退出内核时要清理所带来的影响,带走一切不留下一点痕迹。
5、驱动中操作物理绝对地址为什么要先ioremap?
因为内核没有办法直接访问物理内存地址,必须先通过ioremap获得对应的虚拟地址。
6、内核函数mmap的实现原理,机制?
mmap函数实现把一个文件映射到一个内存区域,从而我们可以像读写内存一样读写文件,他比单纯调用read/write也要快上许多。在某些时候我们可以把内存的内容拷贝到一个文件中实现内存备份,当然,也可以把文件的内容映射到内存来恢复某些服务。另外,mmap实现共享内存也是其主要应用之一,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。