1. 程式人生 > >字元裝置驅動第十一課----裝置樹

字元裝置驅動第十一課----裝置樹

介面

1.出處:

include/linux/of.h

2.資料結構:

struct device_node {
    const char *name;
    const char *type;
    phandle phandle;
    const char *full_name;
    struct fwnode_handle fwnode;

    struct  property *properties;
    struct  property *deadprops;    /* removed properties */
    struct  device_node *parent;
    struct
device_node *child; struct device_node *sibling; struct kobject kobj; unsigned long _flags; void *data; #if defined(CONFIG_SPARC) const char *path_component_name; unsigned int unique_id; struct of_irq_controller *irq_trans; #endif };

3.從裝置樹中查詢節點

/*
* 功能:通過名字找到裝置節點
* 輸入引數: struct device_node *from: 開始節點,比如從/I2C節點開始,搜尋它下面的所有子節點
*          const char *name:         節點名
* 返回值:搜尋到的節點
*/
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
/*
* 功能:通過型別找到裝置節點
* 輸入引數: struct device_node *from: 開始節點,比如從/I2C節點開始,搜尋它下面的所有子節點
*          const char *type:         型別名
* 返回值:搜尋到的節點
*/
struct device_node *of_find_node_by_type(struct device_node *from,const char *type
);
/*
 * 功能:通過compatible屬性查詢節點
 * 引數:struct device_node *from: - 開始節點
 *      const char *type:         - 裝置型別
 *      const char *compat:        - compatible值"廠商,裝置"
 * 返回值: 失敗:NULL
 */
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
/*
 * 功能:從匹配表中查詢裝置ID相匹配的節點
 * 引數:struct device_node *from:             - 開始節點
        const struct of_device_id *matches:   - 表,用於匹配
        const struct of_device_id **match:    - 表中的該項開始,從後面匹配
 * 返回值: 失敗:NULL
 */
struct device_node *of_find_matching_node_and_match(
    struct device_node *from,
    const struct of_device_id *matches,
    const struct of_device_id **match);
/*
 * 功能:通過路徑查詢節點
 * 引數:const char *path: - 路徑
 * 返回值: 失敗:NULL 成功:找到的節點
 */
struct device_node *of_find_node_by_path(const char *path);
/*
* 功能:得到父節點
* 輸入引數:  const struct device_node *node:   當前節點
* 返回值: 失敗:NULL 成功:找到的節點
*/
struct device_node *of_get_parent(const struct device_node *node);
/*
* 功能:得到父節點
* 輸入引數:  const struct device_node *node:   當前節點
* 返回值: 失敗:NULL 成功:找到的節點
*/
struct device_node *of_get_next_parent(struct device_node *node);
/*
* 功能: 獲得子節點
* 輸入引數: const struct device_node *node: 當前節點
*          struct device_node *prev:       前一個子節點
*/
struct device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);
/*
* 功能:通過名字獲取子節點
* 輸入引數: const struct device_node *node:當前節點
*          const char *name:                子節點的名字
*/
struct device_node *of_get_child_by_name(const struct device_node *node,
                    const char *name);

4.從裝置節點中提取資訊

struct property {
    char    *name;      //  屬性名
    int length;         //值長度
    void    *value;     //值的首地址

};
/*
 * 功能:提取屬性
 * 輸入引數:const struct device_node *np:  節點指標
 *          const char *name,:            屬性名
 *          int *lenp:                     長度
 */
struct property *of_find_property(const struct device_node *np,
                     const char *name,
                     int *lenp);
/*
* 功能:通過索引提取屬性
* 輸入引數:const struct device_node *np:   裝置節點
*         const char *propname:           被搜尋的compatible屬性名
*         u32 index:                      索引
* 輸出引數:u32 *out_value:提取到的屬性值
* 返回值: 成功:0  失敗:返回負數,其絕對值是錯誤碼
*/
int of_property_read_u32_index(const struct device_node *np,
                       const char *propname,
                       u32 index, u32 *out_value);
/*
* 功能:提取陣列型別的屬性
* 輸入引數:const struct device_node *np:   裝置節點
*         const char *propname:           被搜尋的compatible屬性名
* 輸出引數:u32 *out_value:提取到的屬性陣列的首地址
*         size_t sz:     提取到的屬性陣列的大小 
* 返回值: 成功:0  失敗:返回負數,其絕對值是錯誤碼
*/
int of_property_read_u8_array(const struct device_node *np,
            const char *propname, u8 *out_values, size_t sz);
/*
* 功能:提取字串型別的屬性
* 輸入引數:const struct device_node *np:   裝置節點
*         const char *propname:           被搜尋的compatible屬性名
* 輸出引數:u32 *out_value:提取到的屬性字串的首地址
* 返回值: 成功:0  失敗:返回負數,其絕對值是錯誤碼
*/
 int of_property_read_string(struct device_node *np,
                   const char *propname,
                   const char **out_string);

5.從裝置樹中提取地址

5.1 地址資料結構

<linux/of_address.h>
 struct resource {
        resource_size_t start;  //開始位置
        resource_size_t end;    //結束位置
        unsigned long flags;    //資源型別:IORESOURCE_MEM(地址類資源)
                                //  IORESOURCE_IRQ(中斷類資源)
    };
 /*
 * 功能:提取資源
 * 輸入引數:struct device_node *dev:   - 當前節點
 *         int index:                 - 資源編號 0開始
 * 輸出引數:struct resource *r:        - 輸出找到結果
*/  
 int of_address_to_resource(struct device_node *dev, int index,
                  struct resource *r);
/*
* 功能:以虛擬地址形式提取
* 輸入引數: struct device_node *node:        裝置節點
*          index:                            索引
* 返回值:成功:虛擬地址     失敗:NULL
*/
void __iomem *of_iomap(struct device_node *node, int index);
/*
* 功能:以虛擬地址形式提取某項屬性中的某個地址
* 輸入引數: struct device_node *node:        裝置節點
*          index:                            該項屬性中的第幾個
*          const char *name:                 該項屬性的名字
* 返回值:成功:虛擬地址     失敗:NULL
*/
void __iomem *of_io_request_and_map(struct device_node *device,
                    int index, const char *name);
/*
* 功能:提取地址
* 輸入引數:   struct device_node *dev: 裝置節點
*             int index:               索引
*             u64 *size:               大小
*             unsigned int *flags*/
const __be32 *of_get_address(struct device_node *dev, int index,
               u64 *size, unsigned int *flags);

從裝置樹中提取中斷

/*
* 功能:提取中斷
* 輸入引數:   struct device_node *dev: 裝置節點
*             int index:               索引
* 輸出引數: struct resource *r:獲得的中斷號
* 返回值成功:0  失敗:負數
*/
int of_irq_to_resource(struct device_node *dev, int index,
                  struct resource *r);
/*
* 功能:提取中斷
* 輸入引數:   struct device_node *dev: 裝置節點
*             int index:               索引
* 返回值:成功:中斷號    失敗:負數
*/
int of_irq_get(struct device_node *dev, int index);
/*
* 功能:通過名字提取中斷
* 輸入引數:   struct device_node *dev: 裝置
*            const char *name:         名字
* 返回值:成功:中斷號    失敗:負數
*/
int of_irq_get_byname(struct device_node *dev, const char *name);
/*
* 功能:通過索引提取中斷,並將物理中斷號對映成系統中的中斷號
* 輸入引數:   struct device_node *dev: 裝置
*            int index:                索引
* 返回值:成功:中斷號    失敗:負數
*/
unsigned int irq_of_parse_and_map(struct device_node *node, int index);

自定義中斷號

/*
 * 功能:從裝置樹獲取gpio編號
 * 輸入引數:@np:              裝置節點
 *         @propname:     屬性域名字
 *         @index:        索引
 * 返回值: gpio編號
 */
int of_get_named_gpio(struct device_node *np,                                      const char *propname, int index)
<include/linux/gpio.h>
/*
 * 功能:將一個gpio編號繫結一些資訊,使其成為核心認可的一個完整的GPIO
 * 輸入引數:@gpio:  GPIO 編號
 *         @flags:  GPIOF_IN還是GPIOF_OUT
 *         @label:  取個名字來標識一下
 */
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
/*
* 功能: 為該gpio資源申請一箇中斷號
* 輸入引數: gpio: GPIO編號
* 返回值:   中斷號
*/
fsirq = gpio_to_irq(gpio);
/*
* 功能: 將使用者申請到的中斷號繫結回撥函式,並向核心註冊
* 輸入引數: fsirq:                 中斷號
*          key_interrupt:         回撥函式名
*          IRQF_TRIGGER_RISING:   中斷觸發方式
*          DEVNAME:               裝置名
*          devid:                  裝置ID(一般可填NULL)
* 返回值:成功:0  失敗:負數
*/
int request_irq(fsirq, key_interrupt, IRQF_TRIGGER_RISING, DEVNAME, NULL)

工程例項

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>

#include <asm/current.h>
#include <linux/sched.h>

#include <linux/uaccess.h>

#include <asm/atomic.h>
#include <linux/mutex.h>

#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>

#include <linux/gpio.h>
#include <linux/interrupt.h>

#include <linux/device.h>
static struct class *cls = NULL;

static int major = 0;
static int minor = 0;
const  int count = 6;

#define DEVNAME "fskey"

static struct cdev *fskeyp = NULL;
static struct device_node *fsnd = NULL;
static int fsirq;
static int gpio;

static atomic_t tv;

/*
 *功能:通過路徑查詢節點
 *引數:
    const char *nodepath - 節點路徑或引用
 *返回值:
    失敗:NULL
    成功:節點指標
 */
static struct device_node *getnodebypath(const char *nodepath)
{
    /*匹配路徑查詢節點*/
    return of_find_node_by_path(nodepath);
}

static irqreturn_t key_interrupt(int irqno, void *devid)
{
    printk("irqno=%d; %s : %d\n", irqno, __func__, __LINE__);
    return IRQ_HANDLED; 
}

//開啟裝置
static int fskey_open(struct inode *inode, struct file *filp)
{
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);

    if(!atomic_dec_and_test(&tv)){//原子操作,防止重複開啟
        atomic_inc(&tv);
        return -EBUSY;
    }


    /* 為一箇中斷號繫結回撥函式並註冊進核心*/
    if (request_irq(fsirq, key_interrupt, IRQF_TRIGGER_RISING, DEVNAME, NULL)){
        printk(KERN_ERR"ERROR: request IRQ[%d] fail.\n", fsirq);
        return -EAGAIN;
    }

    return 0;
}

//關閉裝置
static int fskey_release(struct inode *inode, struct file *filp)
{
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);
    //get major and minor from inode
    printk(KERN_INFO "(major=%d, minor=%d), %s : %s : %d\n",
        imajor(inode), iminor(inode), __FILE__, __func__, __LINE__);

    free_irq(fsirq, NULL);//釋放中斷號
    atomic_inc(&tv);

    return 0;
}

//讀裝置
//ssize_t read(int fd, void *buf, size_t count)
static ssize_t fskey_read(struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    return 0;
}

static struct file_operations fops = {
    .owner  = THIS_MODULE,
    .open   = fskey_open,
    .release= fskey_release,
    .read   = fskey_read,
};

static int __init fskey_init(void)
{
    dev_t devnum;
    int ret, i;
    struct resource r;
    void __iomem *ioaddr = NULL;

    struct device *devp = NULL;

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);

    fsnd = getnodebypath("/key@26");//從裝置樹中找到裝置
    if(NULL == fsnd){
        return -EINVAL;
    }

#if 1
#if 0
    //fsirq = irq_of_parse_and_map(fsnd, 0);
    //if(0 > fsirq){
    //  return fsirq;
    //}
    of_irq_to_resource(fsnd, 0, &r);//獲取資源,中斷資源
    if(IORESOURCE_IRQ == r.flags)//確保獲得的資源是中斷資源而不是IO資源
        printk("irq 0: %d\n", r.start);

    of_irq_to_resource(fsnd, 1, &r);
    if(IORESOURCE_IRQ == r.flags)
        printk("irq 1: %d\n", r.start);

    fsirq = of_irq_get(fsnd, 0);//獲取該裝置節點中的第0箇中斷資源
    printk("fsirq = %d\n", fsirq);
    fsirq = of_irq_get(fsnd, 1);
    printk("fsirq = %d\n", fsirq);
#endif
    of_address_to_resource(fsnd, 0, &r);//獲取該裝置節點中的地址資源
    if(IORESOURCE_MEM == r.flags)
        printk("iomem 0: 0x%x\n", r.start);
    of_address_to_resource(fsnd, 1, &r);
    if(IORESOURCE_MEM == r.flags)
        printk("iomem 1: 0x%x\n", r.start);

    ioaddr = of_iomap(fsnd, 0);//獲取該裝置節點中的地址資源並對映到虛擬地址
    printk("iomem 0: 0x%p\n", ioaddr);
    ioaddr = of_iomap(fsnd, 1);
    printk("iomem 0: 0x%p\n", ioaddr);
#else
    gpio = of_get_named_gpio(fsnd, "key-intr", 0);//獲取該裝置節點中的IO資源
    if (gpio_is_valid(gpio)) {//該gpio可用
        ret = gpio_request_one(gpio, GPIOF_IN, "key-intr");//將一個gpio編號繫結一些資訊,使其成為核心認可的一個完整的GPIO
        if (ret) {
            printk(KERN_ERR"ERROR: gpio_request_one fail.\n");
            return ret;
        }

        fsirq = gpio_to_irq(gpio);  //為該gpio資源申請一箇中斷號
        if(0 > fsirq){
            printk(KERN_ERR"ERROR: gpio_to_irq fail.\n");
            return -ENODEV;
        }
    }else{
        printk(KERN_ERR"ERROR: gpio is invalid.\n");
        return -EINVAL;
    }
#endif

    //1. alloc cdev obj
    fskeyp = cdev_alloc();
    if(NULL == fskeyp){
        return -ENOMEM;
    }

    //2. init cdev obj
    cdev_init(fskeyp, &fops);

    ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME);
    if(ret){
        goto ERR_STEP;
    }
    major = MAJOR(devnum);

    //3. register cdev obj
    ret = cdev_add(fskeyp, devnum, count);
    if(ret){
        goto ERR_STEP1;
    }

    cls = class_create(THIS_MODULE, DEVNAME);
    if(IS_ERR(cls)){
        ret = PTR_ERR(cls);
        goto ERR_STEP1;
    }

    for(i = minor; i < (count+minor); i++){
        devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i);
        if(IS_ERR(devp)){
            ret = PTR_ERR(devp);
            goto ERR_STEP2;
        }
    }

    // init atomic_t
    atomic_set(&tv, 1);//初始化原子變數

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok.\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);
    return 0;

ERR_STEP2:
    for(--i; i >= minor; i--){
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);

ERR_STEP1:
    unregister_chrdev_region(devnum, count);

ERR_STEP:
    cdev_del(fskeyp);

    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - fail.\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);
    return ret;
}

static void __exit fskey_exit(void)
{
    int i;
    //get command and pid
    printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave.\n",
        current->comm, current->pid, __FILE__, __func__, __LINE__);

    for(i=minor; i < (count+minor); i++){
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);

    unregister_chrdev_region(MKDEV(major, minor), count);
    cdev_del(fskeyp);

    gpio_free(gpio);    //釋放申請的gpio
}

module_init(fskey_init);
module_exit(fskey_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Farsight");
MODULE_DESCRIPTION("Demo for kernel module");

相關推薦

字元裝置驅動----裝置

介面 1.出處: include/linux/of.h 2.資料結構: struct device_node { const char *name; const char *type; phandle phandle;

嵌入式Linux驅動筆記()------i2c裝置之mpu6050驅動

你好!這裡是風箏的部落格, 歡迎和我一起交流。 上一節講了i2c框架: 嵌入式Linux驅動筆記(十)——通俗易懂式瞭解i2c框架 這次就來寫一寫真正的i2c裝置驅動: mpu6050是一款6軸運動處理元件,採用i2c通訊介面。 首先是廠家提供的m

、異常類的構建-------------狄泰軟件學院

end ++ pla ini images 數據 pointer oid 變量 一、自定義異常類 1、異常的類型可以是自定義的類類型 2、對於類類型的匹配依舊是之上而下的嚴格匹配 3、賦值兼容性原則在異常匹配中依然適用 所以要 (1)、匹配子類異常的catch放在上部 (2

Ng:機器學習系統的設計(Machine Learning System Design)

未能 計算公式 pos 構建 我們 行動 mic 哪些 指標 11.1 首先要做什麽 11.2 誤差分析 11.3 類偏斜的誤差度量 11.4 查全率和查準率之間的權衡 11.5 機器學習的數據 11.1 首先要做什麽 在接下來的視頻將談到機器

java-web—— 時間類

osc 題目 其中 ima cal pid end 不能 sch 前言: 在我們學習java web之中,實體類的屬性經常會遇到時間類 時間類也是一個很重要的類, 首先我們先了解一下經常使用的時間類 經常使用的時間類也就是這三個類 具體的可以查閱api http://to

集合類Collection和Map

什麽是 基本數據類型 16px 開始 有序 面向對象 style 數據 取出 集合類 面向對象語言對事物的體現都是以對象的形式,所以為了方便對多個對象的操作,就對對象進行存儲,集合就是存儲對象最常用的方式。 數組和集合類都是容器,他們有什麽不同? 數組雖然也可以存儲對象,但

:結合律

tin 結合 .com com img es2017 images get image http://www.52investing.com/jpkecheng/payRoom_31_109.html 第十一課:結合律

語句的嵌套

得到 post 嵌套for 語句 嵌套 color pre pan 我們 說個小故事:話說一個人買到一個治療瘙癢的偏方,在拆開無數層的包裝後,得到的只是一張寫著“撓撓”的小紙條兒。 嵌套,類似於在一個語句中,嵌套另一個語句。舉個栗子-_-!! 我們要計算從1到100之間,

xshell實現linux與windows互文件、用戶與密碼的配置文件、用戶和用戶組的管理

20180403一、Xshell 上實現Linux 與windows互傳1、先用命令 yum install -y lrzsz ;2、向windosw 系統傳文件,sz filename ,向Linux上傳文件,直接輸入rz命令,然在WINDOWS選擇文件即可 二、用戶與密碼的配置文件cat /etc/pa

潭州課堂25班:Ph201805201 繼承,多繼承和魔術方法,屬性和方法 (課堂筆記)

筆記 父類 當前 TE -s __del__ color true ont 繼承: class p : cls_name = ‘p‘ def __init__(self): print(‘正在實例化‘) def __del__(se

斯坦福大學-自然語言處理入門 筆記 最大熵模型與判別模型(2)

一、最大熵模型 1、模型介紹 基本思想:我們希望資料是均勻分佈的,除非我們有其他的限制條件讓給我們相信資料不是均勻分佈的。均勻分佈代表高熵(high entropy)。所以,最大熵模型的基本思想就是我們要找的分佈是滿足我們限制條件下,同時熵最高的分佈。 熵:表示分佈的不

C++筆記 新型的型別轉換---狄泰學院

如果在閱讀過程中發現有錯誤,望評論指正,希望大家一起學習,一起進步。 學習C++編譯環境:Linux 第十一課 新型的型別轉換 1.強制型別轉換 C方式的強制型別轉換 (Type)(Expression) Type (Expression)—老式型別 11-1 粗暴的型別轉

潭州課堂25班:Ph201805201 django 專案 手機號是否存在,簡訊驗證分析 (課堂筆記)

判斷手機號是否註冊功能實現 1.分析 請求方法:GET url定義:/mobiles/(?P<mobile>1[3-9]\d{9})/ 請求引數:url路徑引數 引數 型別 前端是否必須傳 描述 mobile

C語言筆記 enum,sizeof,typedef分析

第十一課 enum,sizeof,typedef分析 列舉型別的使用方法 enum是C語言中的一種自定義型別 enum值是可以根據需要自定義的整型值 第一個定義的enum值預設為0 預設情況下的enum值是在前一個定義值的基礎上加1 enum型別的變數只能取定義時的離散值

【問鏈-EOS公開課】 EOS 智慧合約相互呼叫

EOS中合約之間是可以相互呼叫的,主要通過inline action完成合約之間的呼叫。 譬如在擲骰子游戲中,存在兩個玩家先下注資金,然後比較骰子大小後決定勝負,贏的那一方將獲得所有的下注資金。在eosio原始碼eos/build/contract/dice 智

:UI Elements:Fragments(基於AndroidStudio3.2)

執行Android APP的裝置繁多,螢幕大小更是多種多樣。針對不同螢幕尺寸,通常情況下,開發者都是先針對手機開發一套原始碼,然後拷貝一份,修改佈局以適應大螢幕裝置,或平板,電視等。為了決解這樣的麻煩,Google推出了Fragment。你可以把Fragment當成Activ

MATLAB:線性迴歸方程式與線性系統

目標: 線性方程 線性系統 一、線性方程 Linear Equation: 給定一個線性方程的例子: 把線性方程寫成矩陣的形式。 通常,我們求解線性方程時,A和b是已知的,x是未知的。 Solving Linear Equations: Success

javaweb基礎:完善登入功能

  這節課我們來完善登入功能:   Login.jsp: <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> &n

北大AI公開課--語言智慧的進展by微軟亞洲研究院周明

想做的事情很多,但是時間卻很有限。所以我們更應當在開始任何事情之前,做好足夠的計劃和準備,這樣才能在有限的時間裡做出最恰當的選擇,為自己真正值得做的事爭取出更多時間。之所以有這個感悟是因為最近太多事情要做,但是卻一團糟,所以突然發現,或許應當做一個更為嚴密的計劃! 第十一講

【深度學習框架Caffe學習與應用】

1.車輛檢測實踐:使用Caffe訓練的深度學習模型做目標檢測——以車輛檢測為例 有關檔案都放在以下資料夾中: 對vehicle_detetc.cpp進行編譯: 編譯之前,需要修改一下程式碼中的檔