linux学习3 - 驱动注册流程

 

查看总线: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_releasehello_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/

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值