Linux學習筆記(九)MISC設備驅動

前言

????????misc 的意思是混合、雜項的,因此 MISC 驅動也叫做雜項驅動。也就是當我們板子上的某些外設無法進行分類的時候就可以使用 MISC 驅動。 MISC 驅動其實就是最簡單的字符設備驅動,通常嵌套在 platform 總線驅動中,實現復雜的驅動,接下來就來講下一下MISC的使用,其實總結就是一句話,用MISC 設備驅動來簡化字符設備驅動的編寫,也就是替代我們之前注冊字符設備的那一堆操作


一.MISC設備驅動簡介

? ? ? ? 所有的 MISC 設備驅動的主設備號都為 10,不同的設備使用不同的從設備號。隨著 Linux字符設備驅動的不斷增加,設備號變得越來越緊張,尤其是主設備號, MISC 設備驅動就用于解決此問題。

???????? MISC 設備會自動創建 cdev,不需要像我們以前那樣手動創建,因此采用 MISC 設備驅動可以簡化字符設備驅動的編寫

1.miscdevice 設備結構體:

同樣的,我們要使用MISC,就需要向 Linux 注冊一個 miscdevice 設備, miscdevice是一個結構體,定義在文件 include/linux/miscdevice.h 中,內容如下:

struct miscdevice  {int minor;const char *name;const struct file_operations *fops;struct list_head list;struct device *parent;struct device *this_device;const struct attribute_group **groups;const char *nodename;umode_t mode;
};

? ? 這個結構體需要我們填入的參數有:minor、 name 和 fops 這三個成員變量。

<1>minor:

????????minor 表示子設備號, MISC 設備的主設備號為 10,這個是固定的,需要用戶指定子設備號, Linux 系統已經預定義了一些 MISC 設備的子設備號,這些預定義的子設備號定義在include/linux/miscdevice.h 文件中,如下所示:

#define PSMOUSE_MINOR       1
#define MS_BUSMOUSE_MINOR   2   /* unused */
#define ATIXL_BUSMOUSE_MINOR    3   /* unused */
/*#define AMIGAMOUSE_MINOR  4   FIXME OBSOLETE */
#define ATARIMOUSE_MINOR    5   /* unused */
#define SUN_MOUSE_MINOR     6   /* unused */
#define APOLLO_MOUSE_MINOR  7   /* unused */
#define PC110PAD_MINOR      9   /* unused */
......
#define VHOST_VSOCK_MINOR   241
#define RFKILL_MINOR        242
#define MISC_DYNAMIC_MINOR  255

我們在使用的時候可以從這些預定義的子設備號中挑選一個,當然也可以自己定義,只要這個子設備號沒有被其他設備使用接口。

<2>name :

????????name 就是此 MISC 設備名字,當此設備注冊成功以后就會在/dev 目錄下生成一個名為 name的設備文件。

<3>fops :

fops 是字符設備的操作函數集合, MISC 設備驅動最終是需要使用用戶提供的 fops操作集合。也就是之前我們自己手動注冊字符設備的操作函數:

/* 設備操作函數結構體 */
static const struct file_operations gpioled_fops = {.owner		= THIS_MODULE,.open		= gpioled_open,.read		= gpioled_read,.write      = gpioled_write,.release    = gpioled_release
};

2.miscdevice 注冊函數:

函數原型如下:

int misc_register(struct miscdevice * misc)函數參數和返回值含義如下:
misc:要注冊的 MISC 設備。
返回值: 負數,失敗; 0,成功。

以前我們需要自己調用一堆的函數去創建設備,比如在以前的字符設備驅動中我們會使用如下幾個函數完成設備創建過程:

 /* 傳統的創建設備過程 */
alloc_chrdev_region();    /* 申請設備號 */
cdev_init();              /* 初始化 cdev */
cdev_add();               /* 添加 cdev */
class_create();           /* 創建類 */
device_create();          /* 創建設備 */

?現在我們可以直接使用 misc_register 一個函數來完成上面的傳統的創建設備過程中的這些步驟。如下所示:

   ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}

3.misc_deregister卸載函數:

當我們卸載設備驅動模塊的時候需要調用 misc_deregister 函數來注銷掉 MISC 設備,函數原型如下:

int misc_deregister(struct miscdevice *misc)函數參數和返回值含義如下:
misc:要注銷的 MISC 設備。
返回值: 負數,失敗; 0,成功。

以前注銷設備驅動的時候,我們需要調用一堆的函數去刪除此前創建的 cdev、設備等等內容,如下所示:

/* 傳統的刪除設備的過程 */
cdev_del();                 /* 刪除 cdev */
unregister_chrdev_region(); /* 注銷設備號 */
device_destroy();           /* 刪除設備 */
class_destroy();            /* 刪除類 */

?現在我們只需要一個 misc_deregister 函數即可完成傳統的刪除設備的過程中的這些工作。關于MISC 設備驅動就講解到這里,接下來我們就使用 platform 加 MISC 驅動框架來編寫 led驅動。如下所示:

misc_deregister(&led_miscdev);

二、設備樹的修改:

在設備樹下的根節點添加以下結點:

	gpioled{#address-cells = <1>;#size-cells    = <1>;compatible = "led-gpio";pinctrl-names = "default";pinctrl-0 = <&pinctrl_led>;gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;state = "okay";};

在iomux節點下添加;

		pinctrl_led:ledgrp{fsl,pin=</* 配置 GPIO1_IO03 的 IO 屬性 *bit 16:0 HYS 關閉 *bit [15:14]: 00 默認下拉 *bit [13]: 0 kepper 功能 *bit [12]: 1 pull/keeper 使能 *bit [11]: 0 關閉開路輸出 *bit [7:6]: 10 速度 100Mhz *bit [5:3]: 110 R0/6 驅動能力 *bit [0]: 0 低轉換率 */MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0>;

三、驅動代碼的編寫

1.引入框架

????????框架就使用我們之前的platfrom-led的框架不過在peobe函數里面,我們要把關于那些字符設備注冊初始化的部分刪除了:

static int led_probe(struct platform_device *dev)
{myled_init(&gpioled);return 0;
}

?剩下gpio初始化部分就可以了。

2.創建miscdevice結構體

#define DEVICE_NAME         "miscled"
#define DEVICE_MINOR		145			/* 子設備號 *//* MISC設備結構體 */
static struct miscdevice led_miscdev = {.minor = DEVICE_MINOR,.name = DEVICE_NAME,.fops = &gpioled_fops,
};

3.使用.miscdevice 注冊函數

/*當誰列表的設備和驅動匹配上后執行的peobe函數*/
static int led_probe(struct platform_device *dev)
{int ret = 0;myled_init(&gpioled);ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}

4.misc_deregister卸載函數

static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);misc_deregister(&led_miscdev);printk("gpioled exit!\r\n");return 0;
}

完整代碼:

/**************頭文件區域*********************************************************/
#include <linux/ide.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>#include <linux/of.h> 
#include <linux/of_address.h> 
#include <linux/of_gpio.h> 
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/fcntl.h>
#include <linux/miscdevice.h>#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/io.h>
/**********************************************************************************//************************函數定義-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file);
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr);
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr);
static int gpioled_open(struct inode *inode , struct file *file);
static int led_probe(struct platform_device *dev);
static int led_remove(struct platform_device *dev);
/************************函數定義-end********************************************//************************宏定義-begin***********************************************/
#define DEVICE_NAME         "miscled"
#define DEVICE_MINOR		145			/* 子設備號 */
#define DEVICE_CNT          1
#define BEEP_ON              1
#define BEEP_OFF             0
/************************宏定義-end********************************************//************************結構體定義-begin***********************************************/
/* dtsled設備信息結構體 */
struct dts_dev
{dev_t devid;			/* 設備號 	 */struct cdev cdev;		/* cdev 	*/struct class *class;	/* 類 		*/struct device *device;	/* 設備 	 */struct device_node *nd; /* 設備節點 */int led_gpio; /* led 所使用的 GPIO 編號 */
};
struct dts_dev gpioled; /* led設備 *//* 設備操作函數結構體 */
static const struct file_operations gpioled_fops = {.owner		= THIS_MODULE,.open		= gpioled_open,.read		= gpioled_read,.write      = gpioled_write,.release    = gpioled_release
};/* MISC設備結構體 */
static struct miscdevice led_miscdev = {.minor = DEVICE_MINOR,.name = DEVICE_NAME,.fops = &gpioled_fops,
};/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "led-gpio" },{ /* Sentinel */ }
};static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led",.of_match_table = led_of_match,},.probe  =  led_probe,.remove =  led_remove,
};
/************************結構體定義-end***********************************************//************************file_operations操作函數-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr)
{int ret;unsigned char databuf[1];unsigned char ledstate;struct dts_dev *dev = file->private_data;ret = __copy_from_user(databuf,buf,size);if(ret < 0){printk("kernel write failed!\r\n");return -EFAULT;}ledstate = databuf[0];if(ledstate == BEEP_OFF){   gpio_set_value(dev->led_gpio,1);}else if(ledstate == BEEP_ON){gpio_set_value(dev->led_gpio,0);}return 0;
}
static int gpioled_open(struct inode *inode , struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);file->private_data = &gpioled; /* 設置私有數據 */ return 0;
}
/************************file_operations操作函數-end***********************************************//*****************led初始化函數************************/
static int myled_init(struct dts_dev *dev)
{int ret = 0;/* 1、設置 led 所使用的 GPIO */ dev->nd = of_find_node_by_path("/gpioled");if(dev->nd == NULL){printk("gpioled node cant not found!\r\n");return -EINVAL;}else{printk("gpioled node hase been found!\r\n");}/* 2、 獲取設備樹中的 gpio 屬性,得到 led 所使用的 led 編號 */ dev->led_gpio =  of_get_named_gpio(dev->nd,"gpios",0);if(dev->led_gpio < 0){printk("can't get led-gpio\r\n");return -EINVAL;}printk("led-gpio num = %d\r\n", dev->led_gpio); /* 3、設置 GPIO1_IO03 為輸出,并且輸出高電平,默認關閉 led 燈 */ ret = gpio_request(dev->led_gpio,"led");if(ret < 0){printk("led-gpio request fail\r\n"); return -EINVAL;}ret = gpio_direction_output(dev->led_gpio,1);if(ret < 0) {printk("can't set gpio!\r\n");}return ret;
}/************************platfrom操作函數-begin***********************************************/
/*當誰列表的設備和驅動匹配上后執行的peobe函數*/
static int led_probe(struct platform_device *dev)
{int ret = 0;myled_init(&gpioled);ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}
static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);misc_deregister(&led_miscdev);printk("gpioled exit!\r\n");return 0;
}
/************************platfrom操作函數-endn***********************************************/static int __init gpioled_init(void)
{return platform_driver_register(&led_driver);
}static void __exit gpioled_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("oudafa");

四、編寫測試 APP

這里的測試APP和之前得沒什么區別,不用改:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"/** @description		: main主程序* @param - argc 	: argv數組元素個數* @param - argv 	: 具體參數* @return 			: 0 成功;其他 失敗*/
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打開led驅動 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]);	/* 要執行的操作:打開或關閉 *//* 向/dev/led文件寫入數據 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 關閉文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

五、運行測試

1.編寫makefile

并且使用make命令得到.ko文件和APP文件:

KERN_DIR = /home/odf/linux-imx/linux-imxall:clearmake -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o miscledApp miscledApp.c clean:clearmake -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f miscledAppobj-m += miscled.o 

2.加載模塊

將編譯出來 .ko文件 和 app文件 這兩個文件拷貝到 rootfs/lib/modules/4.1.15 目錄中目錄中,重啟開發板,進入到目錄 rootfs/lib/modules/4.1.15 目錄中中,輸入如下命令加載 驅動模塊:

insmod miscled.ko 

所有的 misc 設備都屬于同一個類, /sys/class/misc 目錄下就是 misc 這個類的所有設備,每個設備對應一個子目錄。

????????驅動與設備匹配成功以后就會生成/dev/miscled這個設備驅動文件,輸入如下命令查看這個文件的主次設備號:

ls -l /sys/class/misc 

結果如下所示:?

從上面可以看出, /dev/misc_beep 這個設備的主設備號為 10,次設備號為 144,和我們驅動程序里面設置的一致。

3.測試代碼

輸入如下命令打開 led:

./misc_beep_app /dev/miscled  1

輸入如下命令關閉?led:

./misc_beep_app /dev/miscled  0

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/207389.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207389.shtml
英文地址,請注明出處:http://en.pswp.cn/news/207389.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mysql 索引概念回顧

一、什么是索引 在關系數據庫中&#xff0c;索引是一種單獨的、物理的對數據庫表中一列或多列的值進行排序的一種存儲結構&#xff0c;它是某個表中一列或若干列值的集合和相應的指向表中物理標識這些值的數據頁的邏輯指針清單。索引的作用相當于圖書的目錄&#xff0c;可以根據…

《算法競賽進階指南》------圖論篇

文章目錄 0x01 Telephone Lines POJ - 36620x02 P1073 [NOIP2009 提高組] 最優貿易0x03 道路和航線 BZOJ22000x04 Sorting It All Out POJ - 1094 topo0x05 Sightseeing trip POJ - 1734 最小環問題0x06 Cow Relays POJ - 3613 S到E經過k條邊的最短路0x07 走廊潑水節 &#xff…

為什么Java程序員需要掌握多線程?揭秘并發編程的奧秘

為什么Java程序員需要掌握多線程&#xff1f;揭秘并發編程的奧秘 個人簡介前言多線程對于Java的意義&#x1f4cc;1.提高程序性能&#xff1a;&#x1f4cc;2 提高用戶體驗&#xff1a;&#x1f4cc;3支持并發處理&#xff1a;&#x1f4cc;4 資源共享和同步&#xff1a;&#…

OpenSSL 編程示例

參考&#xff1a;深入探索 OpenSSL&#xff1a;概念、原理、開發步驟、使用方法、使用場景及代碼示例 地址&#xff1a;https://oneisall.blog.csdn.net/article/details/131489812?spm1001.2014.3001.5502 目錄 1. OpenSSL 概念2. OpenSSL 原理3. OpenSSL 開發步驟4. OpenSSL…

C# 語法筆記

1.ref、out&#xff1a;參數傳遞的兩種方式 ref&#xff1a;引用傳遞 using System; namespace CalculatorApplication {class NumberManipulator{public void swap(ref int x, ref int y){int temp;temp x; /* 保存 x 的值 */x y; /* 把 y 賦值給 x */y temp; /* 把 t…

Python中的range()函數詳解:掌握迭代的利器

更多資料獲取 &#x1f4da; 個人網站&#xff1a;ipengtao.com Python中的range()函數是一個強大的工具&#xff0c;用于生成一系列的數字&#xff0c;常用于循環操作。雖然看似簡單&#xff0c;但其靈活性和功能卻不容小覷。在本文中&#xff0c;將深入研究range()函數&…

9. 從零用Rust編寫正反向代理, HTTP2改造篇之HPACK示例, 了解http2頭信息如何處理

wmproxy wmproxy是由Rust編寫&#xff0c;已實現http/https代理&#xff0c;socks5代理&#xff0c; 反向代理&#xff0c;靜態文件服務器&#xff0c;內網穿透&#xff0c;配置熱更新等&#xff0c; 后續將實現websocket代理等&#xff0c;同時會將實現過程分享出來&#xff…

vue3,元素可拖拽,自定義指令,鼠標以及手指事件的寫法不一樣

使用很簡單&#xff0c;直接 <div v-drag><div class"header"></div><div class"content"></div> </div>// 自定義指令 —— 拖動div const vDrag {// 在綁定元素的父組件// 及他自己的所有子節點都掛載完成后調用m…

docker容器_自定義上傳jenkins鏡像(Dockerfile實現)

1.創建jenkins目錄&#xff0c;并上傳相應的包 mkdir /jenkins/ 2.創建一個Dockerfile文件 FROM daocloud.io/library/centos:7#把當前目錄下的jenkins.war包傳到內部容器的/ 下 ADD ./jenkins.war /#把當前目錄下的jdk傳到內部容器的/opt/,并解壓 ADD ./jdk-11.0.19_linu…

程序解釋與編譯

?1.程序的解釋執行方式 程序語言強寫的計策機指令序列稱為“源程序”,計算機并不能直接執行用高級語言編寫的源程序&#xff0c;源程序必須通過“翻譯程序”翻譯成機器指令的形式&#xff0c;計算機才能項別和執行。源程序的翻譯有兩種方式&#xff1a;解釋執行和編譯執行。不…

網絡編程基礎api

1. IP 協議 1.1 IP 分片 &#xff08;1&#xff09;IP 分片和重組主要依靠 IP 頭部三個字段&#xff1a;數據報標識、標志和片偏移 以太網幀的 MTU 是 1500 字節&#xff1b; 一個每個分片都有自己的 IP 頭部&#xff0c;它們都具有相同的標識值&#xff0c;有不同的片偏移…

css 十字分割線(含四等分布局)

核心技術 偽類選擇器含義li:nth-child(2)第2個 lili:nth-child(n)所有的lili:nth-child(2n)所有的第偶數個 lili:nth-child(2n1)所有的第奇數個 lili:nth-child(-n5)前5個 lili:nth-last-child(-n5)最后5個 lili:nth-child(7n)選中7的倍數 border-right: 3px solid white;borde…

EasyExcel-最簡單的讀寫excel工具類

前言&#xff1a; easyExcel 的官網文檔給的示例非常全&#xff0c;可以參考https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read 在此我貼出自己的工具類&#xff0c;可以直接用 導包 <dependency><groupId>com.alibaba</groupId><…

機器學習第15天:GBDT模型

??主頁 Nowl &#x1f525;專欄《機器學習實戰》 《機器學習》 &#x1f4d1;君子坐而論道&#xff0c;少年起而行之 ?? 文章目錄 GBDT模型介紹 Boosting 殘差 GBDT的缺點 python代碼實現 代碼 模型參數解釋 結語 GBDT模型介紹 GBDT&#xff08;Gradient Boos…

vivado $clog2函數

對于.v文件在vivado中是不支持&#xff0c;但是可以修改為.sv或更改文件屬性使用sytemverilog來支持。 /*** Math function: $clog2 as specified in Verilog-2005** clog2 0 for value 0* ceil(log2(value)) for value > 1** This implementatio…

php+mysql期末作業小項目

目錄 1、登錄界面 2、注冊界面 3、主界面 4、學生表界面 5 、查詢學生界面?編輯 6、修改學生信息界面?編輯 7、刪除學生信息界面 8、添加學生信息界面 9、后臺數據庫?編輯 一個簡單的php?mysql項目學生信息管理系統&#xff0c;用于廣大學子完成期末作業的參考&…

測試架構工程師需要具備哪些能力 ?

前言 相比于我們常見的研發架構師&#xff0c;測試架構師是近幾年才出現的一個崗位&#xff0c;當然崗位title其實沒有特殊的含義&#xff0c;在我看來測試架構師其實更像對某一類人的抽象稱呼和對其復合能力的期待及認可。 在聊這篇文章的主題之前&#xff0c;先來看這樣一個…

算法訓練營Day4(鏈表)

語言 采用的Java語言&#xff0c;一些分析也是用于Java&#xff0c;請注意。 24. 兩兩交換鏈表中的節點 24. 兩兩交換鏈表中的節點 - 力扣&#xff08;LeetCode&#xff09; 解題 這道題就是考驗鏈表的基礎操作&#xff0c;但是有個語言方面的知識需要去掌握&#xff0c;就是|…

TCP通信

第二十一章 網絡通信 本章節主要講解的是TCP和UDP兩種通信方式它們都有著自己的優點和缺點 這兩種通訊方式不通的地方就是TCP是一對一通信 UDP是一對多的通信方式 接下來會一一講解 TCP通信 TCP通信方式呢 主要的通訊方式是一對一的通訊方式&#xff0c;也有著優點和缺點…

如何在Android平板上遠程連接Ubuntu服務器使用code-server代碼開發

目錄 1.ubuntu本地安裝code-server 2. 安裝cpolar內網穿透 3. 創建隧道映射本地端口 4. 安卓平板測試訪問 5.固定域名公網地址 6.結語 1.ubuntu本地安裝code-server 準備一臺虛擬機,Ubuntu或者centos都可以&#xff0c;這里以VMwhere ubuntu系統為例 下載code server服務…