IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 平台总线驱动编程 -> 正文阅读

[嵌入式]平台总线驱动编程


设备驱动模型和sysfs

Linux设备驱动开发流程

  1. 实现入口函数xxx_init()和卸载函数xxx_exit()
  2. 申请设备号 register_chrdev
  3. 利用udev/mdev机制创建设备文件(节点)class_create,device_create
  4. 硬件部分初始化
    io资源映射ioremap,内核提供gpio库函数
    注册中断
  5. 构建file_operations结构
  6. 实现操作硬件方法xxx_open,xxx_read,xxx_write
    思考:当我们写相似设备的驱动代码时,有大量代码的重复,比如上述步骤中除红色字体部分的步骤。
    措施:我们可以把通用代码(上述黑色字体步骤)写一份,每一个设备写不同的差异化代码(上述红色字体步骤),就能实现部分代码的重用。

我们可以设想一个设备驱动模型

Device
Bus
Driver

Device:代表设备对象(描述地址、中断等信息)
Driver:代表设备驱动对象 (申请设备号,创建节点,实现硬件操作方法……)
Bus:代表总线对象(进行device和driver的匹配,device和driver名字进行匹配)

这三个对象,可以在根文件系统/sys目录下可以看到Buses,Device,Class三个文件夹
Sysfs文件系统拓扑图,一个设备信息可以通过总线去查看,也可以通过类(classes)去查看
在这里插入图片描述
进入sys/目录

book@ubuntu:/$ cd sys/
book@ubuntu:/sys$ ls
block  class  devices   fs          kernel  power
bus    dev    firmware  hypervisor  module

sys文件系统是告诉用户,内核驱动的信息,
进入class目录下

book@ubuntu:/sys/class$ cd input/
book@ubuntu:/sys/class/input$ ls
event0  event2  event4  input1  input4  mice    mouse1
event1  event3  input0  input3  input5  mouse0  mouse2
book@ubuntu:/sys/class/input$ cd event0
book@ubuntu:/sys/class/input/event0$ ls
dev  device  power  subsystem  uevent
book@ubuntu:/sys/class/input/event0$ cat uevent
MAJOR=13
MINOR=64
DEVNAME=input/event0 
book@ubuntu:/sys/class/input/event0$ 
book@ubuntu:/sys/class/input/event0/device$ cat name 
Power Button
book@ubuntu:/sys/class/input/event0/device$ 

通过以上指令可以查看设备驱动的信息(主设备号,次设备号,名字等)。
进入bus目录下

book@ubuntu:/sys$ cd bus/
book@ubuntu:/sys/bus$ cd platform/
book@ubuntu:/sys/bus/platform$ ls
devices  drivers  drivers_autoprobe  drivers_probe  uevent
book@ubuntu:/sys/bus/platform$ cd devices/
book@ubuntu:/sys/bus/platform/devices$ ls
ACPI0003:00  Fixed MDIO bus.0  pcspkr                  PNP0001:00  reg-dummy
alarmtimer   i8042             platform-framebuffer.0  PNP0800:00  serial8250
book@ubuntu:/sys/bus/platform/devices$ 

总线中管理两条链表,devices链表和drivers链表,总线进行匹配,匹配成功后调用driver中的probe方法,取消匹配会调用remove方法。

平台总线模型

在这里插入图片描述

编写bus总线对象

struct bus_type	//总线对象,描述一个总线,管理device和driver,完成匹配动作
struct bus_type {
	const char		* name;			//总线名字
	struct module		* owner;

	struct kset		subsys;
	struct kset		drivers;
	struct kset		devices;
	struct klist		klist_devices;
	struct klist		klist_drivers;

	struct blocking_notifier_head bus_notifier;

	struct bus_attribute	* bus_attrs;
	struct device_attribute	* dev_attrs;
	struct driver_attribute	* drv_attrs;
	struct bus_attribute drivers_autoprobe_attr;
	struct bus_attribute drivers_probe_attr;

	int		(*match)(struct device * dev, struct device_driver * drv);	//匹配方法
	int		(*uevent)(struct device *dev, char **envp,
				  int num_envp, char *buffer, int buffer_size);
	int		(*probe)(struct device * dev);
	int		(*remove)(struct device * dev);
	void		(*shutdown)(struct device * dev);

	int (*suspend)(struct device * dev, pm_message_t state);
	int (*suspend_late)(struct device * dev, pm_message_t state);
	int (*resume_early)(struct device * dev);
	int (*resume)(struct device * dev);

	unsigned int drivers_autoprobe:1;
};

注册和注销总线

int bus_register(struct bus_type * bus);
void bus_unregister(struct bus_type * bus); 

最基本的总线模块框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

struct bus_type mybus = {
	.name = "mybus",
};
EXPORT_SYMBOL(mybus);	//导出该对象,device代码中要用
static int __int mybus_init(void){
	//1. 构建一个总线,在/sys/bus/mybus
	bus_register(&mybus)
	if(ret != 0){
		printk("bus_register error\n");
		return ret;
	}
	return 0;
}

static void __exit mybus_exit(void){
	bus_unregister(&mybus);
}

module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");

执行make指令,insmod指令,可以看到/sys/bus/下出现mybus目录,但目录中的devbices和drivers目录中并没有内容。

创建device对象

device对象,对象中包含描述设备信息(地址,中断号,自定义数据)

struct device {
	struct klist		klist_children;
	struct klist_node	knode_parent;		/* node in sibling list */
	struct klist_node	knode_driver;
	struct klist_node	knode_bus;
	struct device		*parent;

	struct kobject kobj;		//所有对象的父类,继承所有父类的特性
	char	bus_id[BUS_ID_SIZE];	/* position on parent bus */
	struct device_type	*type;	//该对象中有name成员用于匹配
	unsigned		is_registered:1;
	unsigned		uevent_suppress:1;
	struct device_attribute uevent_attr;
	struct device_attribute *devt_attr;

	struct semaphore	sem;	/* semaphore to synchronize calls to
					 * its driver.
					 */

	struct bus_type	* bus;		/* 指向该device对象依附于总线的对象 */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*driver_data;	/* data private to the driver */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it 自定义数据*/
	struct dev_pm_info	power; 

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
	/* arch specific additions */
	struct dev_archdata	archdata;

	spinlock_t		devres_lock;
	struct list_head	devres_head; 

	/* class_device migration path */
	struct list_head	node;
	struct class		*class;
	dev_t			devt;		/* dev_t, creates the sysfs "dev" */
	struct attribute_group	**groups;	/* optional groups */

	void	(*release)(struct device * dev);
};

注册和注销的方法

int device_register(struct device * dev);
void device_unregister(struct device * dev);

最基本的device模块框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

extern struct bus_type mybus;

void mydev_release(struct device *dev){
	printk("-------------%s---------------",__FUNCTION__);
}

struct device mydev = {
	.init_name = "mydev",	//3.14内核版本
	.bus = &mybus;
	.release = mydev_release;
};

static int __init mydev_init(void){
	int ret = device_register(&mydev);
	if(ret < 0){
		printk("device_register error");
		return ret;
	}
	return 0;
}

static void __exit mydev_exit(void){
	device_unregister(&mydev);
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");

driver对象:描述设备驱动的方法(代码逻辑)

struct device_driver {
	const char		* name;	//名字,用于匹配,在/sys/bus/mybus/drivers/名字
	struct bus_type		* bus;	//指向总线

	struct kobject		kobj;
	struct klist		klist_devices;
	struct klist_node	knode_bus;

	struct module		* owner;
	const char 		* mod_name;	/* used for built-in modules */
	struct module_kobject	* mkobj;
	/* 方法 */
	int	(*probe)	(struct device * dev);//如果dev和drv匹配成功后,driver要做的事情
	int	(*remove)	(struct device * dev);//如果dev和drv匹配接触后,driver要做的事
	void	(*shutdown)	(struct device * dev);
	int	(*suspend)	(struct device * dev, pm_message_t state);
	int	(*resume)	(struct device * dev);
};

dev注册和注销

int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

最基本driver框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

extern struct bus_type mybus;

int mydrv_probe(struct device *dev){
	return 0;
}

int mydrv_remove(struct device *dev){ 

}
extern struct bus_type mybus;

struct device_driver mydrv = {
	.name = "mydrv",
	.bus = &mybus,
	.probe = mydrv_probe,
	.remove = mydrv_remove,
};

static int __init mydrv_init(void){
	printk("------------%s-------------\n",__FUNCTION__);
	//将driver注册到总线中
	int ret = driver_register(&mydrv);
	if(ret < 0){
		printk("driver_register error\n");
		return ret;
	}
	return 0;
}

static void __exit mydrv_exit(void){
	printk("------------%s-------------\n",__FUNCTION__);
	driver_unregister(&mydrv);
}
module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");

总线模块中的匹配方法

在总线中要能实现匹配的话,必须要实现bus_type对象中的match接口

int	match(struct device * dev, struct device_driver * drv);

匹配成功后,driver模块会自动调用probe方法

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
/* 
	match方法 
*/
int mybus_match(struct device *dev, struct device_driver *drv){
	//如果匹配成功,match方法一定要返回一个1,失败返回0
	if(!strncmp(drv->name, dev->kobj.name,strlen(drv->name))){
		printk("match ok\n");
		return 1;
	}else{
		printk("match failed\n");
		return 0;
	}
	return 0;
}

struct bus_type mybus = {
	.name = "mybus",
	.match = mybus_match,
};
EXPORT_SYMBOL(mybus);

static int __int mybus_init(void){
	//1. 构建一个总线,在/sys/bus/mybus
	……
	return 0;
}

static void __exit mybus_exit(void){
	bus_unregister(&mybus);
}

module_init(mybus_init);
module_exit(mybus_exit);
MODULE_LICENSE("GPL");

如何结合device对象和driver对象

  1. 在dev对象中的device对象的platform_data接口中放入数据(void *platform_data)
  2. dev和drv匹配成功
  3. drv调用probe方法,并从probe接口中获得dev的数据(platform_data接口中)

在drv对象中实现probe方法
在dev中存入数据

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

extern struct bus_type mybus;

/* 设置一个自定义数据,描述设备的特性 */
struct mydev_desc{
	char *name;
	int irqno;
	unsigned long addr;
};

struct mydev_desc devinfo = {
	.name = "testdev",
	.irqno = 9999,
	.addr = 0x30008000,
};

void mydev_release(struct device *dev){
	printk("-------------%s---------------",__FUNCTION__);
}

struct device mydev = {
	.init_name = "mydev",	//3.14内核版本
	.bus = &mybus;
	.release = mydev_release,
	.platform_data = &devinfo,
};

static int __init mydev_init(void){
	int ret = device_register(&mydev);
	if(ret < 0){
		printk("device_register error");
		return ret;
	}
	return 0;
}

static void __exit mydev_exit(void){
	device_unregister(&mydev);
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");

dev中调用probe方法

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>

extern struct bus_type mybus;
struct mydev_desc *pdesc;	//声明一个对象,用来接收数据
int mydrv_probe(struct device *dev){
	printk("-------------%s-------------\n",__FUNCTION__);
	pdesc =(struct mydev_desc *)dev->platform_data;//接收形参dev中的数据
	printk("name - %s\n", pdesc->name);
	printk("irqno = %d\n", pdesc->irqno);
	unsigned long *paddr = ioremap(pdesc->addr, 0);
	/* platform_data中的数据在dev对象中写,在drv中使用,实现合并 */

	return 0;
}

int mydrv_remove(struct device *dev){
	printk("-------------%s-------------\n",__FUNCTION__);
	return 0;
}

extern struct bus_type mybus;


struct device_driver mydrv = {
	.name = "mydrv",
	.bus = &mybus,
	.probe = mydrv_probe,
	.remove = mydrv_remove,
};

static int __init mydrv_init(void){
	printk("------------%s-------------\n",__FUNCTION__);
	//将driver注册到总线中
	int ret = driver_register(&mydrv);
	if(ret < 0){
		printk("driver_register error\n");
		return ret;
	}
	return 0;
}

static void __exit mydrv_exit(void){
	printk("------------%s-------------\n",__FUNCTION__);
	driver_unregister(&mydrv);
}
module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");

平台总线驱动编写

为什么会有平台总线
平台升级导致地址空间发生变化,但操作方式类似,换句话说逻辑操作一样,地址不一样。所以引入平台总线。一个驱动能驱动多个平台相似的模块,并且修改的代码很少。
平台总线三元素

  1. bus
    platform_bus:不需要自己创建,系统自带。
    总线中有platform_bus_type对象
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,	//匹配方法
	.uevent		= platform_uevent,
	.suspend	= platform_suspend,
	.suspend_late	= platform_suspend_late,
	.resume_early	= platform_resume_early,
	.resume		= platform_resume,
};

bus模块入口函数

int __init platform_bus_init(void)
{
	int error;

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);	//注册bus对象
	if (error)
		device_unregister(&platform_bus);
	return error;
}

匹配方法(match)

  • 优先匹配driver中的id_table,里面包含了支持不同平台的名字
  • 直接匹配driver中名字和device中名字
  1. device对象
    device 中有platform_device对象
struct platform_device {
	const char	* name;	//用于匹配
	u32		id;	//一般直接给-1
	struct device	dev;	//继承父类,在父类上进行了扩展
	u32		num_resources;	//资源的个数
	struct resource	* resource;//资源:包括了一个设备的地址和中断
};
  1. driver对象
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 (*suspend_late)(struct platform_device *, pm_message_t state);
	int (*resume_early)(struct platform_device *);
	int (*resume)(struct platform_device *);
	struct device_driver driver;	/* 继承 */
	/*3.14版本内核中*/
	const struct platform_device_id *id_table;//如果driver支持多个平台,在列表中表明
};

3.14版本中的match方法

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);
	if(of_driver_match_device(dev, drv))
		return 1;
	if(acpi_driver_match_device(dev, drv))
		return 1;
	//如果pdrv中有idtable
	if(pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;
	return (strcmp(pdev->name, drv->name) == 0);
}

2.6版本内核

static int platform_match(struct device * dev, struct device_driver * drv)
{
	struct platform_device *pdev = container_of(dev, struct platform_device, dev);

	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}

drv和dev的注册和注销

int platform_device_register(struct platform_device * pdev)
void platform_device_unregister(struct platform_device * pdev)
int platform_driver_register(struct platform_driver * drv)
void platform_driver_unregister(struct platform_driver * drv)

编写平台总线代码

编写一个能在多个平台下使用的led驱动

  1. 注册一个platform_device,定义资源:地址和中断
  2. 注册一个platform_driver,实现操作设备的代码

在platform_device对象中有个resource成员,resource不需要我们初始化

struct platform_device {
	const char	* name;
	u32		id;
	struct device	dev;
	u32		num_resources;
	struct resource	* resource;	//可以用数组形式表示,一个设备有多个资源
};
struct resource {
	resource_size_t start;	//表示资源地址的起始
	resource_size_t end;	//表示资源地址的结束
	const char *name;	//描述名字,自定义
	unsigned long flags;	//区分当前resource是描述中断(IORESOURCE_IRQ)还是内存(IORESOURCE_MEM) 
	struct resource *parent, *sibling, *child;//父母,兄弟,孩子该对象表示二叉树
};

platform_device_led.c程序

#include <linux/init.h> 
#include <linux/module.h>
#include <linux/platform_device.h>

#include <plat/irqs.h>

#define GPIO_REG_BASE 0x11400000

#define GPF3_CON GPIO_REG_BASE + 0x01E0
#define GPF3_SIZE 24

#define GPX1_CON GPIO_REG_BASE + 0x0C20
#define GPX1_SIZE 24


//一个设备可能有多个资源
struct resource led_res[] = {
	[0] = {	//定义第一个寄存器
		.start = GPF3_CON,
		.end = GPF3_CON + GPF3_SIZE - 1,
		.flags = IORESOURCE_MEM, 
	},
	[1] = {	//定义第二个寄存器
		.start = GPX1_CON,
		.end = GPX1_CON + GPX1_SIZE - 1,
		.flags = IORESOURCE_MEM,
	}
	//有些设备也有中断资源,本驱动中没有关于中断的描述
	//以下仅作为中断的例子
	[2] = {	
		.start = IRQ_EINT(4),	//4号中断
		.end = IRQ_EINT(4),
		.flags = IORESOURCE_IRQ, 
	},
};

struct platform_device led_pdev = {
	.name = "s3c2440_led",	//用于做匹配
	.id = -1,
	.num_resources = ARRAY_SIZE(led_res),//资源数量
	.resource = led_res,
	/* 定义资源struct resource *resource
	 	struct resourc {
	  		resource_size_t start;  //资源起始地址
	  		resource_size_t end;	//资源结束地址
	  		unsigned long flags;	//标志位,标志哪一类资源(中断还是内存)
	  								//IORESOURCE_IRQ(中断)
	  								//IORESOURCE_MEM(内存)
	  		struct resource *parent, *sibing, *child;	
	 	}
	 */
};


static int _init plat_led_dev_init(void){
	//注册一个平台设备
	platform_device_register(&led_pdev);
	return 0;
}

static void _exit plat_led_dev_exit(void){
	platform_device_unregister(&led_pdev);
}

module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit);
MODULE_LICENSE("GPL");

platform_driver,实现操作设备的代码,注册完毕,同时如果和pdev匹配成功,自动调用probe方法:
probe方法:对硬件进行操作
a.注册设备号,并且注册fops——为用户提供一个设备标识,同时提供文件操作io接口
b.创建设备节点
c.初始化硬件
ioremap(地址); //地址要从pdev获取到
readl()/writel();
d.实现各种io接口:xxx_open,xxx_read,……

获取资源的方式:

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);

platform_driver_led.c程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>

#include <asm/uaccess.h>
#include <asm/io.h>
 
//设计一个全局的设备对象
struct led_dev{
	int dev_major;
	struct class *cls;
	struct device *dev;
	struct resource *res;	//获取到的内存
	void *reg_base;	//表示物理地址映射之后的虚拟地址
};

struct led_dev *samsung_led;

ssize_t led_pdrv_write(struct file *file, const char __user *buf, size_t count, loff_t *fpos){
	int val;
	int ret;
	ret = copy_from_user(&val, buf, count);
	if(ret > 0){
		printk("copy_from_user error\n")
		return -EFAULT;
	}
	if(val){	//亮
		writel((readl(samsung_led->reg_base+4) |  (0x3<<4)), samsung_led->reg_base+4);
	}else{
		writel((readl(samsung_led->reg_base+4) & ~(0x3<<4)), samsung_led->reg_base+4);
	}
	return count;
}

int led_pdrv_open(struct inode *, struct file *){
	printk("--------%s--------\n", __FUNCTION__);
	return 0;
}

int led_pdrv_close(struct inode *, struct file *){
	printk("--------%s--------\n", __FUNCTION__);
	return 0;
}


const struct file_operations led_fops = {
	.open = led_pdrv_open,
	.release = led_pdrv_close,
	.write = led_pdrv_write,
};

int led_pdrv_probe(struct platform_device *pdev){//总线传入参数
	printk("------------%s-----------\n", __FUNCTION__);
	/* 分配对象,用于存储驱动信息 */
	samsung_led = kzalloc(sizeof(struct led_dev), GFP_KERNEL);
	if(samsung_led == NULL){
		printk("kzalloc erron\n");
		return -ENOMEM;
	}
	/* 1. 注册设备号 */
	samsung_led->dev_major = register_chrdev(0, "led_drv", &led_fops *);

	/* 2. 创建设备节点 */
	samsung_led->cls = class_create(THIS_MODULE, "led_new_cls");
	samsung_led->dev = device_create(samsung_led->cls, NULL, MKDEV(samsung_led->major, 0), "led0");

	/* 3. 初始化硬件,由probe函数参数提供,在device.c里面定义 */
	//a.获取资源
	//参数1:从哪个资源获取资源
	//参数2:资源类型
	//参数3:获取“同种”资源的第几个从0号开始
	samsung_led->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	//以下等同于platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	int irqno = platform_get_irq(pdev, 0);
	printk("--------irqno-------- = %d\n", irqno);
	//res中的start就是物理地址
	samsung_led->reg_base = ioremap(samsung_led->res->start, samsung_led->res->start - samsung_led->res->end + 1);
	//等同于
	//ioremap(samsung_led->res->start, resource_size(samsung_led->res));

	/* 4. 配置各种寄存器,配置为输出功能*/
	//readl()清空,写值,放入原先地址
	writel((readl(samsung_led->reg_base)&~(0xff<<16)|(0x11<<16)), samsung_led->reg_base);
	
	/* 5. 实现各种io接口:xxx_open(),xxx_read(),…… */
	
	return 0;
}

int led_pdrv_remove(struct platform_device *){
	printk("------------%s-----------", __FUNCTION__);
	iounmap(samsung_led->reg_base);
	device_destroy(samsung_led->cls, MKDEV(samsung_led->dev_major,0));
	class_destroy(samsung_led->cls);
	unregister_chrdev(MKDEV(samsung_led->dev_major, 0), "led_drv");
	kfree(samsung_led);
	return 0;
}

/*
struct platform_device_id {
	char name[PLATFORM_NAME_SIZE];
	kernel_ulong_t driver_data; 
}
*/

struct device_driver driver;
/* 3.14版本,2.6版本中暂无,永name匹配 */
const struct platform_device_id led_id_table[] = {
	{"s3c2440_led", 0x4444},	//后面的值自定义
	{"s5pv210_led", 0x3333},
	{"s3c2410_led", 0x2222},
};	//一定要做匹配

struct platform_driver led_pdrv = {
	 .probe = led_pdrv_probe,
	 .remove = led_pdrv_remove,
	 .driver = {
		.name = "samsung_led_drv",	
		//驱动名字可以用来做匹配,总线匹配方法优先匹配id_table中,没有idtable,用名字
		// 创建/sys/bus/platform/drivers/samsung_led_drv
	 }
	 .id_table = led_id_table,//指针,赋数组名
};

static int _init plat_led_pdrv_init(void){
	//注册一个pdrv
	return platform_driver_register(&led_pdrv);

}

static void _exit plat_led_pdrv_exit(void){
	platform_driver_unregister(&led_pdrv);
}

module_init(plat_led_pdrv_init);
module_exit(plat_led_pdrv_exit);
MODULE_LICENSE("GPL");

应用程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys.types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	int fd;
	int on = 0;
	fd = open("/dev/led0", O_RDWR);
	if(fd < 0){
		perror("open");
		exit(1);
	}
	//led灯闪烁
	while(1){
		on = 0;
		write(fd, &on, 4);
		sleep(1);
		
		on = 0;
		write(fd, &on, 4);
		sleep(1);
	}
	return 0;
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-09 16:26:44  更:2021-10-09 16:27:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:21:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码