目錄
一、使用設備樹
1.1 修改設備樹流程
二、手動創建平臺設備?
三、總結(附驅動程序)
前情提要:???????【IMX6ULL驅動開發學習】07.驅動程序分離的思想之平臺總線設備驅動模型和設備樹_阿龍還在寫代碼的博客-CSDN博客
手動注冊平臺設備和設備樹的目的都是為了構造platform_device結構體
一、使用設備樹
之前的驅動編寫方式,引腳信息在驅動程序里寫死了,移植性差。而使用設備樹,這樣的程序編寫方式便于在換了板子或引腳之后,只要修改設備樹的節點信息即可,不需要看復雜的驅動程序,前提是有完備的驅動程序。
在驅動程序的入口函數里注冊了gpio_platform_driver,
module_init(gpio_drv_init);
static int __init gpio_drv_init(void)
{/* 注冊platform_driver */return platform_driver_register(&gpio_platform_driver);
}
gpio_platform_driver結構體中probe函數被調用,需要在設備樹中找到與之匹配的設備節點信息,即設備節點信息也要含有這個語句.compatible = "100ask,gpiodemo"
static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "100ask,gpiodemo", },{ /* sentinel */ }
};static struct platform_driver gpio_platform_driver = {.driver = {.name = "100ask_gpio_plat_drv",.of_match_table = gpio_dt_ids,},.probe = gpio_drv_probe,.remove = gpio_drv_remove,
};
1.1 修改設備樹流程
在內核目錄下找到設備樹文件,以我的路徑為例,這里給出絕對路徑
- 修改設備樹:vi /home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dts
- 增加節點信息如下:
motor {compatible = "100ask,gpiodemo";gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>, <&gpio4 20 GPIO_ACTIVE_HIGH>,<&gpio4 21 GPIO_ACTIVE_HIGH>,<&gpio4 22 GPIO_ACTIVE_HIGH>;
};
- 一定要在/home/book/100ask_imx6ull-sdk/Linux-4.9.88目錄下編譯設備樹:make dtbs
- 復制到單板上,如下:
PC:
cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/開發板:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/100ask_imx6ull-14x14.dtb /boot
reboot
- 查看設備樹節點信息:ls /sys/firmware/devicetree/base/?
- ?查看馬達節點有沒有被轉換成平臺設備(platform_device):ls /sys/bus/platform/devices/
?
?有這個motor文件表示有了對應的平臺設備。
- 進入motor文件查看,因為還沒裝驅動,所以沒有顯示驅動程序
?
- 裝載驅動后,查看motor文件:?說明驅動和設備都匹配上了
?
- 最后就編寫測試程序進行測試即可:?
./button_test /dev/motor ...
二、手動創建平臺設備?
- 編寫dev.c文件
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.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/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>static struct resource my_drv_resource[] = {{.start = 115,.end = 115,.flags = IORESOURCE_IRQ,},{.start = 116,.end = 116,.flags = IORESOURCE_IRQ,},{.start = 117,.end = 117,.flags = IORESOURCE_IRQ,},{.start = 118,.end = 118,.flags = IORESOURCE_IRQ,},
};static struct platform_device gpio_platform_device = {.name = "100ask_gpio_plat_drv",.id = 0,.num_resources = ARRAY_SIZE(my_drv_resource),// =4 宏來計算數組大小.resource = my_drv_resource,
};static int __init gpio_dev_init(void)
{/* 注冊platform_driver */return platform_device_register(&gpio_platform_device);
}static void __exit gpio_dev_exit(void)
{/* 反注冊platform_driver */platform_device_unregister(&gpio_platform_device);
}/* 7. 其他完善:提供設備信息,自動創建設備節點 */module_init(gpio_dev_init);
module_exit(gpio_dev_exit);MODULE_LICENSE("GPL");
?其中,由這個名字"100ask_gpio_plat_drv"找到與之匹配的平臺驅動(platform_driver)
static struct platform_device gpio_platform_device = {.name = "100ask_gpio_plat_drv",.id = 0,.num_resources = ARRAY_SIZE(my_drv_resource),// =4 宏來計算數組大小.resource = my_drv_resource,
};
- ?修改Makefile文件
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # 板子所用內核源碼的目錄all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o button_test button_test.c
clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.order button_test# 參考內核源碼drivers/char/ipmi/Makefile
# 要想把a.c, b.c編譯成ab.ko, 可以這樣指定:
# ab-y := a.o b.o
# obj-m += ab.oobj-m += gpio_drv.o
obj-m += gpio_dev.o
- ?編譯:make
- 設備樹節點信息取消:因為內核里有了設備樹節點信息,需要改回去或者添加disabled狀態
motor {compatible = "100ask,gpiodemo";gpios = <&gpio4 19 GPIO_ACTIVE_HIGH>, <&gpio4 20 GPIO_ACTIVE_HIGH>,<&gpio4 21 GPIO_ACTIVE_HIGH>,<&gpio4 22 GPIO_ACTIVE_HIGH>;status = "disabled";
};
- 編譯:make dtbs
- 更新設備樹:
PC:
cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/開發板:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
cp /mnt/100ask_imx6ull-14x14.dtb /boot
reboot
- 查看設備樹節點信息:ls /sys/firmware/devicetree/base/
????????狀態為禁止狀態,設備樹節點信息被我們禁用了?
?
- 查看馬達節點有沒有被轉換成平臺設備(platform_device):ls /sys/bus/platform/devices/,這里肯定也是沒有的
?
- ?裝載驅動程序、查看是否匹配上、查看設備節點
?這個平臺驅動支持名為"100ask_gpio_plat_drv"的設備,設備節點也出來了
?
三、總結(附驅動程序)
這個驅動程序,在入口函數里注冊了平臺驅動,這個平臺驅動可以支持來自設備樹的平臺設備.compatible = "100ask,gpiodemo",和來自自己手動創建的平臺設備.name = "100ask_gpio_plat_drv"。匹配規則有4種,這里用的是最簡單的。當平臺driver匹配到平臺device時,驅動程序里的probe函數就被調用,probe函數里可以從設備樹里得到引腳信息,也可以從手動注冊的平臺device里得到所謂的資源。
static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "100ask,gpiodemo", },{ /* sentinel */ }
};static struct platform_driver gpio_platform_driver = {.driver = {.name = "100ask_gpio_plat_drv",.of_match_table = gpio_dt_ids,//設備樹信息},.probe = gpio_drv_probe,.remove = gpio_drv_remove,
};
?驅動程序:
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.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/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>struct gpio_desc{int gpio;int irq;char name[128];int key;struct timer_list key_timer;
} ;static struct gpio_desc *gpios;
static int count;/* 主設備號 */
static int major = 0;
static struct class *gpio_class;/* 馬達引腳設置數字 */
static int g_motor_pin_ctrl[8]= {0x2,0x3,0x1,0x9,0x8,0xc,0x4,0x6};
static int g_motor_index = 0;void set_pins_for_motor(int index)
{int i;for (i = 0; i < 4; i++){gpio_set_value(gpios[i].gpio, g_motor_pin_ctrl[index] & (1<<i) ? 1 : 0);}
}void disable_motor(void)
{int i;for (i = 0; i < 4; i++){gpio_set_value(gpios[i].gpio, 0);}
}/* int buf[2];* buf[0] = 步進的次數, > 0 : 逆時針步進; < 0 : 順時針步進* buf[1] = mdelay的時間*/
static ssize_t motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{int ker_buf[2];int err;int step;if (size != 8)return -EINVAL;err = copy_from_user(ker_buf, buf, size);if (ker_buf[0] > 0){/* 逆時針旋轉 */for (step = 0; step < ker_buf[0]; step++)
{set_pins_for_motor(g_motor_index);mdelay(ker_buf[1]);g_motor_index--;if (g_motor_index == -1)g_motor_index = 7;}}else{/* 順時針旋轉 */ker_buf[0] = 0 - ker_buf[0];for (step = 0; step < ker_buf[0]; step++)
{set_pins_for_motor(g_motor_index);mdelay(ker_buf[1]);g_motor_index++;if (g_motor_index == 8)g_motor_index = 0;}
}/* 改進:旋轉到位后讓馬達不再消耗電源 */disable_motor();return 8;
}/* 定義自己的file_operations結構體 */
static struct file_operations gpio_key_drv = {.owner = THIS_MODULE,.write = motor_write,
};/* 在入口函數 */
static int gpio_drv_probe(struct platform_device *pdev)
{int err = 0;int i;struct device_node *np = pdev->dev.of_node;struct resource *res;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);/* 從platfrom_device獲得引腳信息 * 1. pdev來自c文件* 2. pdev來自設備樹*/if (np){/* pdev來自設備樹 : 示例reg_usb_ltemodule: regulator@1 {compatible = "100ask,gpiodemo";gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>, <&gpio5 3 GPIO_ACTIVE_HIGH>;};*/count = of_gpio_count(np);if (!count)return -EINVAL;gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);for (i = 0; i < count; i++){gpios[i].gpio = of_get_gpio(np, i);sprintf(gpios[i].name, "%s_pin_%d", np->name, i);}}else{/* pdev來自c文件 static struct resource omap16xx_gpio3_resources[] = {{.start = 115,.end = 115,.flags = IORESOURCE_IRQ,},{.start = 118,.end = 118,.flags = IORESOURCE_IRQ,}, }; */count = 0;while (1){res = platform_get_resource(pdev, IORESOURCE_IRQ, count);if (res){count++;}else{break;}}if (!count)return -EINVAL;gpios = kmalloc(count * sizeof(struct gpio_desc), GFP_KERNEL);for (i = 0; i < count; i++){res = platform_get_resource(pdev, IORESOURCE_IRQ, i);gpios[i].gpio = res->start;sprintf(gpios[i].name, "%s_pin_%d", pdev->name, i);}}for (i = 0; i < count; i++){err = gpio_request(gpios[i].gpio, gpios[i].name);gpio_direction_output(gpios[i].gpio, 0);}/* 注冊file_operations */major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv); /* /dev/gpio_desc */gpio_class = class_create(THIS_MODULE, "100ask_gpio_key_class");if (IS_ERR(gpio_class)) {printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, "100ask_gpio_key");return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "motor"); /* /dev/motor */return err;
}/* 有入口函數就應該有出口函數:卸載驅動程序時,就會去調用這個出口函數*/
static int gpio_drv_remove(struct platform_device *pdev)
{int i;printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, "100ask_gpio_key");for (i = 0; i < count; i++){gpio_free(gpios[i].gpio);}return 0;
}static const struct of_device_id gpio_dt_ids[] = {{ .compatible = "100ask,gpiodemo", },{ /* sentinel */ }
};static struct platform_driver gpio_platform_driver = {.driver = {.name = "100ask_gpio_plat_drv",.of_match_table = gpio_dt_ids,//設備樹信息},.probe = gpio_drv_probe,.remove = gpio_drv_remove,
};static int __init gpio_drv_init(void)
{/* 注冊platform_driver */return platform_driver_register(&gpio_platform_driver);
}static void __exit gpio_drv_exit(void)
{/* 反注冊platform_driver */platform_driver_unregister(&gpio_platform_driver);
}/* 7. 其他完善:提供設備信息,自動創建設備節點 */module_init(gpio_drv_init);
module_exit(gpio_drv_exit);MODULE_LICENSE("GPL");
?