查看总线:ls /sys/bus/
查看设备号的命令 cat /proc/devices
设备都有主设备号和次设备号,否则255个设备号不够用
– 查看杂项设备号的命令 cat /proc/misc
设备一般都需要先注册(挂载),才能注册驱动
– 现在越来越多的热拔插设备,反过来了。先注册驱动,设备来了再注册
1. 虚拟总线
有一些例如 led、蜂鸣器等等一些设备,都不是从字面上理解的总线设备。针对这个情况,Linux 创立了一种虚拟总线,也叫平台总线或者 platform 总线,这个总线也有对应的设备 platform_device,对应的驱动叫 platform_driver。
2. 注册流程图
参考讯为电子的图:
重点函数: probe:初始化、生成设备节点
3. 设备种类
Linux 将设备分为了三大类:字符设备、块设备、网络设备。
① 字符设备:字符设备是能够像字节流一样被访问的设备。一般说来对硬件设备 IO 的操作可以归结为字符设备。常见的字符设备有 led、蜂鸣器、串口、键盘等等。
② 块设备:块设备室通过内存缓冲区访问,可以随机存取的设备,一般性的理解就是存储介质类的设备。常见的字符设备有 U 盘、TF 卡、eMMC、电脑硬盘、光盘等等
③ 网络设备:可以和其它主机交换数据的设备。常见的以太网设备、WIFI、蓝牙等。
4. 设备注册
使用虚拟平台来注册设备会容易很多。
注册设备使用结构体 platform_device,该结构体在头文件 include/linux/platform_device.h 中。头文件中也有注册设备和卸载设
备的函数。
仿写一个:
打开内核文件下的: arch/arm/mach-exynos/mach-itop4412.c
① 添加结构体:
#ifdef CONFIG_HELLO_CTL
struct platform_device s3c_device_hello_ctl = {
.name = "hello_ctl",
.id = -1,
}
#endif
② 还需要导入到链表里面:
#ifdef CONFIG_HELLO_CTL
&s3c_device_hello_ctl,
#endif
③ 然后确认一下 menuconfig里面HELLO的宏定义
make menuconfig
④ 最后编译
make zImage
然后将zImage烧写到板子里面
fastboot.exe flash kernel zImage
fastboot.exe reboot
⑤ 板子启动后查看设备
ls sys/devices/platform/
看到HELLO有了
小插曲:
zImage烧写进去之后板子反复重启
原因:使用的.config文件不对,用的 config_for_android_scp_elite 不可以,改为 config_for_linux_scp_elite就可以了
5. 驱动注册
驱动注册使用结构体 platform_driver,该结构体在头文件“include/linux/platform_device.h”中
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
驱动注册 platform_driver_register,驱动卸载函数 platform_driver_unregister 也在这个头文件中;
– 这两个函数的参数都只有结构体platform_driver
驱动常见的几种状态,初始化,移除,休眠,复位
– 就像PC一样,有的驱动休眠之后无法使用,有的可以使用;有的系统唤醒之后,驱动需要重新启动才能正常工作,也有直接就可以使用等等
• probe 函数
– platform_match函数匹配之后,驱动调用的初始化函数
• remove 函数
– 移除驱动函数
• suspend 函数
– 悬挂(休眠)驱动函数
• resume 函数
– 休眠后恢复驱动
• device_driver 数据结构的两个参数
– name和注册的设备name要一致
– owner一般赋值THIS_MODULE
仿写一个:
打开内核文件下的: arch/arm/mach-exynos/mach-itop4412.c
① 编写文件 probe_linux_module.c 和 Makefile
文件 probe_linux_module.c
#include <linux/init.h>
#include <linux/module.h>
/*驱动注册的头文件,包含结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
#define DRIVER_NAME "hello_ctl" //要和设备注册的名字一样
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");
static int hello_probe(struct platform_device *pdv)
{
printk(DRIVER_NAME "\tinitialized\n");
return 0;
}
static int hello_remove(struct platform_device *pdv)
{
return 0;
}
static void hello_shutdown(void)
{
}
static int hello_suspend(struct platform_device *pdv)
{
return 0;
}
static int hello_resume(struct platform_device *pdv)
{
return 0;
}
struct platform_driver hello_driver ={
.probe = hello_probe,
.remove = hello_remove,
.shutdown = hello_shutdown,
.suspend = hello_suspend,
.resume = hello_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int hello_init(void)
{
int DriverState;
printk(DRIVER_NAME "HELLO WORLD enter!\n");
DriverState = platform_driver_register(&hello_driver); //注册,注册是有返回值的
printk(DRIVER_NAME "\tinitialized is %d \n",DriverState);
return 0;
}
static void hello_exit(void)
{
printk(DRIVER_NAME "HELLO WORLD exit!\n");
platform_driver_unregister(&hello_driver);
}
module_init(hello_init);
module_exit(hello_exit);
② 拷贝到ubuntu中进行编译make,然后查看已经生成了probe_linux_module.ko
③拷贝到U盘,在板子上加载
mount /dev/sda1 /mnt/usb_disk
成功后,即可使用 u 盘了, 文件就在目录 /mnt/usb_disk 下。
④ 加载 驱动
可以看到加载成功。
6. 生成设备节点
(内核2.6版本以前的基本都废弃了,不用管了,学了不实用)
6.1 杂项设备
杂项设备可以说是对一部分字符设备的封装,还有一部分不好归类的驱动也归到杂项设备。
杂项设备初始化部分源文件 “ drivers/char/ misc.c ” :给字符驱动做一个简单的封装
杂项设备注册头文件
– include/linux/miscdevice.h
① 结构体 miscdevice
struct miscdevice {
int minor; //设备好,一般设置为系统自动分配
const char *name; //生成设备节点的名称
const struct file_operations *fops; //指向一个设备节点文件(所以还需要文件的操作)
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
extern int misc_register(struct miscdevice * misc);
extern int misc_deregister(struct miscdevice *misc);
注册设备节点,本质也是新建一个特殊的文件,包含文件名,打开、关闭、操作等函数;
②结构体包含一个文件结构体,包含文件结构体的头文件是 “include/linux/fs.h ”
文件的结构体file_operations参数很多,根据需求选择。
• 必选的是参数是
– owner一般是THIS_MODULE,
– open打开文件函数
– release关闭文件函数
• 这里在必选之外使用参数(为了介绍接下来的GPIO的操作)
– unlocked_ioctl 对GPIO的操作,应用向底层驱动传值
③ 注册设备+注册驱动
编写文件 devicenode_linux_module.c
调用函数:misc_register 和 misc_deregister
static int hello_probe(struct platform_device *pdv)
{
printk(DRIVER_NAME "\t initialized\n");
misc_register(&hello_dev); //probe的时候调用注册节点
return 0;
}
static int hello_remove(struct platform_device *pdv)
{
printk(DRIVER_NAME "\t moved \n");
misc_deregister(&hello_dev);
return 0;
}
结构体 hello_dev
//杂项设备注册结构体
static struct miscdevice hello_dev = {
.minor = MISC_DYNAMIC_MINOR, //节点号,自动分配宏变量
.name = DEVICE_NAME, //设备名字
.fops = &hello_ops,
};
需要文件指针 hello_ops
//设备文件结构体
static struct file_operation hello_ops = {
.owner = THIS_MOUDLE,
.open = hello_open, //打开时要调用的
.release = hello_release, //关闭时要调用的
.unlocked_ioctl = hello_ioctl,
};
定义好文件操作需要的函数: hello_open 、hello_release、hello_ioctl (名字可以自己取)
④ 拷贝到U盘,在板子上加载
mount /dev/sda1 /mnt/usb_disk
成功后,即可使用 u 盘了, 文件就在目录 /mnt/usb_disk 下。
⑤ 加载 驱动
insmod /mnt/usb_disk/devicenode_linux_module.ko
可以看到加载成功。
⑥ 卸载模块
rmmod devicenode_linux_module.ko
⑦ 查看设备节点
ls /dev/