上一个笔记里面,简单的记录了一下字符设备的注册。后面自己通过配置rk3288的gpio,可以实现app控制灯的亮灭,这里一次性的记录一下字符设备点灯,基于platform总线的代码改造。
一.platform总线
1.什么是platform总线?有什么用?直接写字符设备驱动不一样吗?有什么分别吗?
这也是我刚刚听到platform总线的疑惑,也想了半天
1)platform是虚拟出来的总线,实际并不是像I2C SPI一样真实存在的物理总线。
2)主要用于分离设备资源和驱动,更便于驱动的移植,硬件资源的调整
3)如果板子上只有一个led资源,那么我们可以通过将硬件资源写死在驱动里面,一样可以跑,那么试想一下,如果有10个led呢,每一个led占用的IO口不一样,那么对应的mem地址就不一样,那么要驱动起来这些led,就需要led1.c,led2.c led3.c.....去写10份驱动,那通过platform总线,首先platform device里面资源的定义很直观,容易修改,那么我完全可以 将led的驱动写成通用的驱动,通过获取device里面定义的资源来进行通用的操作,所以10个led,我可以定义10个platform device,platform driver,只有一份就可以了。
二.platform 结构体与注册
1.platform_device
#include <linux/platform_device.h>
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
1).name
platform bus device与driver进行匹配的名字,要与platform_driver里面的名字对应,不然匹配不上
2).id
设备实例号,或者用“-1”表示只有一个实例
3).dev
这里面我们会用到它的成员.release,没有这个release函数会报错
4)struct resource *resource
这里不多说了,后面看代码吧
struct resource {
resource_size_t start; //资源的起始
resource_size_t end; //资源的结束
const char *name; //资源的名字
unsigned long flags; //资源的类型
struct resource *parent, *sibling, *child;
};
这里的.flags有以下类型..等...没有都赋值过来,就是看一下
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */
#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */
#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_REG 0x00000300 /* Register offsets */
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000
5).num_resources
资源的数量,我们一般给它赋值为ARRAY_SIZE(resource)
代码实例
struct resource led_resource = {
{
.start = 0xFF760000+0x198,
.end = 0xFF760000+0x198+3,
.name = "GPIO8_CLKGATE_CON",
.flags = IORESOURCE_MEM
}
};
struct platform_device led_device = {
.name = "alon_charDevPlt_test",
.id = -1,
.resource = led_resource,
.num_resources = ARRAY_SIZE(led_resource),
.dev = {
.release = led_release
}
};
2.platform_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 (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
1).probe
在platform driver与platform device匹配后,会马上执行probe,所以可以将上一份代码里面类的注册,设备号申请,设备注册的代码放在这里,同时结合硬件资源,这里会对硬件进行初始化等。
2).driver
这里我们只用到了它的两个成员.name .owner
struct device_driver {
const char *name;
struct module *owner;
...
};
这里的.name与platform device里面的.name要匹配.owner = THIS_MODULE
代码示例
struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.owner = THIS_MODULE,
.name = "alon_charDevPlt_test"
}
};
3.platform注册
platform device注册/反注册
platform_device_register(drv)
platform_device_unregister(drv)
platform driver注册/反注册
platform_driver_register(drv)
platform_driver_unregister(drv)
platform总线的注册,也是通过模块加载的方式去注册,只需要在init/exit里面调用对应的platform注册/反注册函数就可以了
代码示例
platformDevice.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
struct resource led_resource[] = {
[0]={
.start = 0xFF760000+0x198, //GPIO8 CLK GATE 寄存器地址
.end = 0xFF760000+0x198+3,
.name = "GPIO8_CLKGATE_CON",
.flags = IORESOURCE_MEM
},
[1]={
.start = 0xFF7F0000+0x000, // GPIO8 数据寄存器地址
.end = 0xFF7F0000+0x000+3,
.name = "GPIO8_PORT_DR",
.flags = IORESOURCE_MEM
},
[2]={
.start = 0xFF770000+0x080, //GPIO8 复用寄存器地址
.end = 0xFF770000+0x080+3,
.name = "GRF_GPIO8A_IOMUX",
.flags = IORESOURCE_MEM
},
[3]={
.start = 0xFF7F0000+0x004, //GPIO8 input output控制寄存器地址
.end = 0xFF7F0000+0x004+3,
.name = "GPIO_PORT_DDR",
.flags = IORESOURCE_MEM
}
};
void led_release(struct device *dev){ //platform_device的成员.dev的成员,没有编译会有警告,必须有,可以什么都不做
printk("led release \n");
}
struct platform_device led_device = { //platform_device结构体
.name = "alon_charDevPlt_test", //driver名字
.id = -1,
.resource = led_resource, //硬件资源
.num_resources = ARRAY_SIZE(led_resource), //资源大小
.dev = {
.release = led_release
}
};
static int platformDeviceTest_init(void) //模块init入口
{
platform_device_register(&led_device); //注册platform_driver
return 0;
}
static void platformDeviceTest_exit(void){ //模块exit出口
platform_device_unregister(&led_device); //反注册platform_driver
}
module_init(platformDeviceTest_init);
module_exit(platformDeviceTest_exit);
MODULE_LICENSE("GPL");
platform_drvier.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/io.h>
int inputPara=0;
int dev_major;
int dev_minor=0;
char *chardev_device_name="aloncharDevice"; //设备驱动名字
struct class *class;
char *chardev_class_name="aloncharClass"; //类名字
char *charDev_node_name="aloncharTest"; //设备节点名字
module_param(inputPara, int, S_IRUSR); //insmod的时候传入的参数,参数名字inputPara,类型int,S_IRUSR是权限
/*
static volatile unsigned int *GPIO8_CLKGATE_CON; //这里是裸驱动的时候定义的地址指针,改造成platform 总线后就给改成下面的了
static volatile unsigned int *GPIO_PORT_DR;
static volatile unsigned int *GPIO_PORT_DDR;
static volatile unsigned int *GRF_GPIO8A_IOMUX;
*/
static volatile unsigned int *led_reg_vir_addr[4]; //指针数组,存储GPIO8 4个控制寄存器地址
enum ledctlreg{ //寄存器地址的顺序,对应platform_device里面的资源的顺序,这里是方便获取资源定义的,顺序要和resource对应上,不然控制的时候就乱了
GPIO8_CLKGATE_CON = 0,
GPIO_PORT_DR,
GRF_GPIO8A_IOMUX,
GPIO_PORT_DDR
};
int led_probe(struct platform_device *pdev){ //device和driver匹配上之后会立刻执行probe
struct resource *mem_tmp; //获取的资源指针
int i;
for(i = 0;i<4 ;i++) //因为platform的resource有4个,这里获取4个
{
mem_tmp = platform_get_resource(pdev, IORESOURCE_MEM, i);//获取IORESOURCE_MEM类型的第i个资源
if(mem_tmp == NULL)
{
printk(KERN_ERR "get resource error! i = %d\n",i);
return -EBUSY;
}
led_reg_vir_addr[i] = ioremap(mem_tmp->start, 4); //获取资源对应的地址后,申请虚拟地址
}
dev_major = register_chrdev(0,chardev_device_name,&f_op); //注册设备号,f_op结构体
if(dev_major < 0)
{
printk("can not regist char device!\n");
return dev_major;
}
printk(KERN_ERR "dev_major = %d,chardev device name is %s\n",dev_major,chardev_device_name);
class = class_create(THIS_MODULE, chardev_class_name); //注册类
if(IS_ERR(class))
{
printk("can not create charDev class!");
unregister_chrdev(dev_major,chardev_device_name);
return PTR_ERR(class);
}
printk(KERN_ERR "chardev class name is %s\n",chardev_class_name);
device_create(class, NULL, MKDEV(dev_major,dev_minor), NULL, charDev_node_name); //注册设备节点
printk(KERN_ERR "chardev node create ok!\n");
return 0;
}
int charDev_open (struct inode *inode, struct file *file){
*led_reg_vir_addr[GPIO8_CLKGATE_CON] = (1<<(16+8))|(0<<8); //31:16 write mask :8 gpio8 clk gate,0:enable
*led_reg_vir_addr[GRF_GPIO8A_IOMUX] = (1<<(16+4))|(~(1<<4));//31:16 write mask :4 0:fuc gpio
*led_reg_vir_addr[GPIO_PORT_DDR] |= (1<<2); //gpio8_A2 set output
printk("open charTest node OK!\n");
return 0;
}
ssize_t charDev_read (struct file *file, char __user *buf, size_t size, loff_t *ppos){
return 0;
}
ssize_t charDev_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos){
int cmdbuf;
int ret;
ret = copy_from_user(&cmdbuf, buf, 1);
if(ret < 0)
{
printk("access data from user failed!\n");
}
if(cmdbuf == 1)
{
*led_reg_vir_addr[GPIO_PORT_DR] &= ~(1<<2); // 0点亮
}
else
{
*led_reg_vir_addr[GPIO_PORT_DR] |= (1<<2); //1 熄灭
}
return 0;
}
static const struct file_operations f_op = { //file_operations结构体
.open = charDev_open,
.read = charDev_read,
.write = charDev_write,
.owner = THIS_MODULE,
};
int led_remove(struct platform_device *pdev){
return 0;
}
struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.owner = THIS_MODULE,
.name = "alon_charDevPlt_test"
}
};
static int charDev_init(void) //init函数,MODULE_INIT的时候会进行初始化
{
int ret = 0;
ret = platform_driver_register(&led_driver); //在这里注册platform_driver
if(ret < 0){
printk(KERN_ERR "platform driver register failed \n");
return ret;
}
printk(KERN_ERR "insmod platform driver\n");
if(inputPara) //这里是测试参数是否传入,执行insmod devDrv.ko inputPara=整数(等号前后不能有空格)。
{
printk(KERN_ERR "input Param is %d\n",inputPara);
}
return 0;
}
static void charDev_exit(void)
{
int i;
for(i = 0;i<4;i++)
{
iounmap(led_reg_vir_addr[i]);
}
platform_driver_unregister(&led_driver);
printk(KERN_ERR "remove platform driver\n");
device_destroy(class, MKDEV(dev_major,dev_minor));
printk(KERN_ERR "remove chardev node!\n");
class_destroy(class);
printk(KERN_ERR "remove chardev class!\n");
unregister_chrdev(dev_major,chardev_device_name);
printk(KERN_ERR "remove chardev device!\n");
}
module_init(charDev_init);
module_exit(charDev_exit);
MODULE_LICENSE("GPL");
app代码
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(int argc, const char ** argv)
{
int fd;
char val;
val = atoi(argv[1]);
fd = open("/dev/charTest",O_RDWR);
if(fd < 0)
{
printf("APP can not open node!\n");
return 0;
}
printf("open /dev/charTest ok!\n");
write(fd, &val, 1);
close(fd);
return 0;
}