1.1 修改设备增加led节点
内核源码设备树文件位置:ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dts
添加如下节点:
red_led {
#address-cells = <1>;
#size-cells = <1>;
compatible = "red_led";
status = "okay";
reg = < 0x20C406C0x04/* CCM_CCGR1 */
0x20E006C0x04/* MUX_PAD_GPIO1_IO04 */
0x209C0000x04/* GPIO1_DR */
0x209C0040x04 >; /* GPIO1_GDIR */
};
编译生成的二进制设备树和设备树文件在同一目录,路径:arch/arm/boot/dts/imx6ull-seeed-npi.dtb
dts文件和dtb文件关系类似C语言文件和生成的二进制可执行程序文件
1.2 编译设备树
make_dtb.sh:
#!/bin/sh
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
1.3 更新开发板Linux系统设备树
1.3.1 将设备文件拷贝到根文件系统
copy_dtb_to_nfs_fs.sh:
#!/bin/sh
cp arch/arm/boot/dts/imx6ull-seeed-npi.dtb /home/ares/nfs_rootfs/
1.3.2 拷贝设备到开发板Linux系统设备树文件目录
copy_dtb.sh:
#!/bin/sh
cp /debian/nfs_rootfs/imx6ull-seeed-npi.dtb /boot/dtbs/4.19.71-imx-r1/
1.3.3 重启系统以生效设备树文件
sudo reboot
2.1 编写字符设备核心
即使使用设备树,字符设备最核心的还是open、write、close等函数。没有这些函数函数设备树也是废物一个。
#define RED_LED_DTS_NODE "/red_led"//red_led设备节点的位置 //red_led表示根节点下的red_led节点
structred_led_dev {
structcdevchrdev;//字符设备结构
dev_t dev_no; //设备号
structclass *led_class;
structdevice_node *dev_node;
unsignedint *virtual_ccgr1;
unsignedint *virtual_gpio1_io4;
unsignedint *virtual_dr;
unsignedint *virtual_gdir;
};
staticstructred_led_devled_dev;
staticintred_led_drv_open(struct inode *node, struct file *file)
{
int val = 0;
printk(KERN_INFO"open red led dev\n");
val = ioread32(led_dev.virtual_ccgr1);
val &= ~(3 << 26);
val |= (3 << 26);
iowrite32(val, led_dev.virtual_ccgr1);
val = 0;
val = ioread32(led_dev.virtual_gpio1_io4);
val &= ~(0xf << 0);
val |= (0x5 << 0);
iowrite32(val, led_dev.virtual_gpio1_io4);
val = 0;
val = ioread32(led_dev.virtual_gdir);
val |= (0x1 << 4);
iowrite32(val, led_dev.virtual_gdir);
return0;
}
staticssize_tred_led_drv_write(struct file *file, constchar __user *buf, size_t size, loff_t *offset)
{
int err;
char status;
int val = 0;
printk("write %s %s line %d\r\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(&status, buf, 1);
if (status)
{
val &= ~(1 << 4);
iowrite32(val, led_dev.virtual_dr);
}
else
{
val |= (1 << 4);
iowrite32(val, led_dev.virtual_dr);
}
return1;
}
//这个结构体至始至终都是字符设备的核心,说有设备树不用学这个就是骗鬼
staticstructfile_operationsred_led_drv = {
.owner = THIS_MODULE,
.open = red_led_drv_open,
.write = red_led_drv_write,
};
2.2 定义设备树匹配列表
//设备树匹配列表
staticstructof_device_iddts_match_table[] = {
{.compatible = "red_led", }, //根据compatible兼容属性进行匹配
};
设备节点和驱动是否能匹配成功就看设备树列表。可以看出设备匹配列表可以有多个,这表示驱动是可以匹配多个设备的。
2.3 定义填充驱动结构体
staticintled_red_driver_probe(struct platform_device *dev)
{
int err;
int ret;
u32 regdata[8];
int i;
structdevice *tmpdev;
led_dev.dev_node = of_find_node_by_path(RED_LED_DTS_NODE); //从设备树中查找led字符设备的设备节点
if (!led_dev.dev_node) {
printk("red led can not found!\r\n");
return -EINVAL;
}
ret = of_property_read_u32_array(led_dev.dev_node,
"reg", //从找到的设备节点中读取寄存器属性的值
regdata, 8);
if (ret < 0) {
printk(KERN_INFO"Failed to get red led reg property\n");
} else {
for (i = 0; i < 8; i++) {
printk("reg%d : %x\n", i, regdata[i]);
}
}
#if 1
led_dev.virtual_ccgr1 = ioremap(regdata[0], regdata[1]); //映射寄存器物理地址为虚拟地址
led_dev.virtual_gpio1_io4 = ioremap(regdata[2], regdata[3]);
led_dev.virtual_dr = ioremap(regdata[4], regdata[5]);
led_dev.virtual_gdir = ioremap(regdata[6], regdata[7]);
#else
led_dev.virtual_ccgr1 = of_iomap(led_dev.dev_node, 0);
led_dev.virtual_gpio1_io4 = of_iomap(led_dev.dev_node, 1);
led_dev.virtual_dr = of_iomap(led_dev.dev_node, 2);
led_dev.gdir = of_iomap(led_dev.dev_node, 3);
#endif
ret = alloc_chrdev_region(&led_dev.dev_no, 0, 1, "red_led"); //申请设备号
if (ret < 0) {
pr_err("Error: failed to register mbochs_dev, err: %d\n", ret);
return ret;
}
cdev_init(&led_dev.chrdev, &red_led_drv); //初始化字符设备
cdev_add(&led_dev.chrdev, led_dev.dev_no, 1); //添加字符设备
led_dev.led_class = class_create(THIS_MODULE, "red_led_class");
err = PTR_ERR(led_dev.led_class);
if (IS_ERR(led_dev.led_class)) {
goto failed1;
}
//创建设备节点
tmpdev = device_create(led_dev.led_class , NULL, led_dev.dev_no, NULL, "red_led");
if (IS_ERR(tmpdev)) {
ret = -EINVAL;
goto failed2;
}
printk(KERN_INFO"%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return0;
failed2:
device_destroy(led_dev.led_class, led_dev.dev_no);
class_destroy(led_dev.led_class);
failed1:
cdev_del(&led_dev.chrdev);
unregister_chrdev_region(led_dev.dev_no, 1);
return ret;
}
intled_red_driver_remove(struct platform_device *dev)
{
iounmap(led_dev.virtual_ccgr1);
iounmap(led_dev.virtual_gpio1_io4);
iounmap(led_dev.virtual_dr);
iounmap(led_dev.virtual_gdir);
printk(KERN_INFO"driver remove %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(led_dev.led_class, led_dev.dev_no);
class_destroy(led_dev.led_class);
unregister_chrdev_region(led_dev.dev_no, 1);
cdev_del(&led_dev.chrdev);
return0;
}
staticstructplatform_driverred_led_platform_driver = {
.probe = led_red_driver_probe,
.remove = led_red_driver_remove,
.driver = {
.name = "fire-rgb-led",
.owner = THIS_MODULE,
.of_match_table = dts_match_table, //加设备树列表加进驱动中
},
};
2.4 设备树出口/入口函数
staticint __init red_led_driver_init(void)
{
printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
int ret = platform_driver_register(&red_led_platform_driver);
return ret;
}
staticvoid __exit red_led_driver_exit(void)
{
printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&red_led_platform_driver);
}
module_init(red_led_driver_init);
module_exit(red_led_driver_exit);
MODULE_AUTHOR("Ares");
MODULE_LICENSE("GPL");