Linux驅動開發2:字符設備驅動
字符設備驅動開發流程
字符設備是 Linux 驅動中最基本的一類設備驅動,字符設備就是一個一個字節,按照字節流進行讀寫操作的設備,讀寫數據是分先后順序的。比如最常見的點燈、按鍵、 IIC、 SPI, LCD 等等都是字符設備,這些設備的驅動就叫做字符設備驅動。
驅動就是獲取外設、或者傳感器數據,控制外設。數據會提交給應用程序。Linux驅動編譯既要編寫一個驅動,還要我們編寫一個簡單的測試應用程序,APP,Linux下驅動和應用是完全分開的。
字符設備的注冊與注銷
注冊字符設備使用register_chrdev函數
函數原型:static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
;
major: 主設備號, Linux 下每個設備都有一個設備號,設備號分為主設備號和次設備號兩部分
name:設備名字,指向一串字符串。
fops: 結構體 file_operations 類型指針,指向設備的操作函數集合變量。
注銷字符設備使用 unregister_chrdev函數
函數原型:static inline void unregister_chrdev(unsigned int major, const char *name);
major: 要注銷的設備對應的主設備號。
name: 要注銷的設備對應的設備名。
設備號
Linux 中每個設備都有一個設備號,設備號由主設備號和次設備號兩部分組成,主設備號表示某一個具體的驅動,次設備號表示使用這個驅動的各個設備
Linux 提供了一個名為 dev_t 的數據類型表示設備號
dev_t 其實就是 unsigned int 類型,是一個 32 位的數據類型,其中高 12 位為主設備號, 低 20 位為次設備號。
可以通過cat /proc/devices
命令查看當前設備中已被使用的主設備號
字符驅動編寫
字符設備驅動的編寫主要就是驅動對應的open/close/read/write函數的編寫,本質上就是對file_operations結構體的成員變量的實現。
在此給出結構體函數定義
完善實現file_operations結構體的成員變量open/close/read/write
函數的實現后進行編譯
編寫應用程序進行測試
加載驅動后移植應用代碼進行測試
輸入命令手動創建驅動節點
輸入命令測試chrdevbase驅動
最后給出一般流程下的完整字符設備驅動框架
字符驅動開發總流程
一、設備樹定義
1、在設備樹文件中定義節點示例:gpioled {#address-cells = <1>;#size-cells = <1>;compatible = "alientek,gpioled";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpioled>;led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;status = "okay";};pinctrl_gpioled: gpiogrp {fsl,pins = <MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10b0 /* gpioled */ >;};二、驅動代碼開發
1、module_init和module_exit
2、聲明模塊相關信息1-作者:MODULE_AUTHOR(author);2-描述:MODULE_DESCRIPTION(description);3-版本:MODULE_VERSION(version_string);4-設備表:MODULE_DEVICE_TABLE(table_info);5-別名:MODULE_ALIAS(alternate_name);6-開源協議:MODULE_LICENSE("GPL");
3、定義字符設備結構體
4、定義設備操作函數 file_operations
5、實現init函數流程1-注冊字符設備,判斷major是否已被指定Y:register_chrdev_region(dev_t from, unsigned count, const char *name)N:alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) -> MAJOR,MINOR2-添加cdev字符設備cdev_init(struct cdev *cdev, const struct file_operations *fops) -> cdev_add(struct cdev *p, dev_t dev, unsigned count)3-創建class設備類 class_create(owner, const char *name)4-創建device設備device_create(struct class *class, NULL , dev_t devt, NULL, const char *name)5-獲取設備樹節點信息of_find_node_by_path(const char *path)5.1-獲取對應的GPIOof_get_named_gpio(struct device_node *np, const char *propname, int index)5.2-申請IOgpio_request(unsigned gpio, const char *label)5.3-設置GPIO口輸入輸出模式并配置默認輸出模式gpio_direction_output(unsigned gpio, int value)5.4-設置指定GPIO口電平值gpio_set_value(unsigned int gpio, int value)6-獲取設備樹屬性信息of_property_read_string-字符串of_property_count_elems_of_size-獲取數組大小of_property_read_u32_array-從數組中獲取每個元素等7-實現write操作函數-從應用程序用戶空間拷貝數據copy_from_user(void *to, const void __user *from, unsigned long n)//7-實現地址映射// ioremap 或 of_iomap
6、實現exit函數流程1-釋放GPIOgpio_free(unsigned gpio)//2-取消地址映射// iounmap3-刪除device設備device_destroy(struct class *class, dev_t devt)4-刪除class設備類class_destroy(struct class *cls)5-注銷cdev字符設備cdev_del(struct cdev *p)6-釋放設備號unregister_chrdev_region(dev_t from, unsigned count)