1. 程式人生 > >【BeagleBone Black Rev. C試用體驗】+裝置樹驅動

【BeagleBone Black Rev. C試用體驗】+裝置樹驅動

感覺裝置樹寫驅動是未來的潮流。。。下面我們來講講裝置樹驅動開發。。

裝置樹語法我們這就不說了,去下面連結自己參考吧:
http://www.cnblogs.com/xiaojiang1025/p/6131381.html


一、修改裝置樹:
裝置樹檔案在arch/arm/boot/dts目錄下am335x-boneblack.dts檔案

先貼程式碼:
/* add by Sourcelink */
/ {
        model = "TI AM335x BeagleBoneBlack";
        compatible = "ti,am335x-bone", "ti,am33xx";

        cpus {
                [email protected]
{
                        cpu0-supply = <&dcdc2_reg>;
                };
        };
       
        /* add by Sourcelink */
        source_gpio {
                        compatible = "sourcelink_gpio";
                        pinctrl-names = "sourcelink_gpio";
                        pinctrl-0 = <&sourcelink_pin>;
                        source_gpios = <&gpio2 12 0>;
        };
};

&am33xx_pinmux {
        rstctl_pins: pinmux_rstctl_pins {
                pinctrl-single,pins = <
                        /* eMMC_RSTn */
                        0x50 0x17        /* gpmc_a4.gpio1_20, OUTPUT | MODE7 | PULLUP */
                >;
        };

        /* add by Sourcelink */
        sourcelink_pin: pinmux_source_pins {
                pinctrl-single,pins = <
                        0x30 0x7        /* gpmc_ad12.gpio1_12, OUTPUT | MODE7 */
                >;
        };       
};
修改的地方我都有標註。。。
compatible是用來匹配的。。。
pinctrl-names用來匹配pinctrl設定資訊。。。
source_gpios用來獲取io資訊。。。

我們用的是gpio1_12,設定mode7,且為輸出模式。。。

切記gpio的地址標號是從1開始的。。。gpio1對應的是GPIO0_x的地址,我就是這出了問題一直沒有調出來。。。

引腳的複用都是從800h地址開始:

對應管腳從gpio0_0開始四個位元組開始增加,所以gpio1_12偏移對應:4*12 = 48 = 0x30


二、匯流排驅動
platform匯流排驅動這裡就不介紹了。我也講不明白。感興趣的可以去 瞭解,以前學習的時候都是自己寫個device再寫個driver然後匹配。
現在有裝置樹了devcie省去了,減少了很大的程式碼冗餘。。。。

寫驅動編譯有錯誤不要緊,一條條出解決,查問題。這個驅動剛寫好時,一編譯刷刷的一排error下來,看到額眼睛都花了,但是一個個看過去還是能解決的。無非就是標頭檔案沒包含
語法有問題。。呼叫的變數型別有問題。。

下面貼個簡單的框架:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/pinctrl/consumer.h>



static int gpio_drv_open(struct inode *inode, struct file *file)
{

        return 0;
}

static ssize_t gpio_drv_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
        int ret;
       
        return ret;
}


struct file_operations gpio_drv_fops = {
                .owner = THIS_MODULE,
                .open = gpio_drv_open,
                .write = gpio_drv_write,

};



static const struct of_device_id of_gpio_fortree_match[] = {
        { .compatible = "sourcelink_gpio", },
        {},
};


static int gpio_fortree_probe(struct platform_device *pdev)
{
       
       
        return 0;
}



static int gpio_fortree_remove(struct platform_device *pdev)
{
       
        return 0;
}



static struct platform_driver gpio_fortree_driver = {
        .probe                = gpio_fortree_probe,
        .remove                = gpio_fortree_remove,
        .driver                = {
                .name        = "sourcelink_gpio",
                .owner        = THIS_MODULE,
                .of_match_table = of_match_ptr(of_gpio_fortree_match),
        },
};


static int gpio_fortree_init(void)
{
        int ret;


        return ret;
}



static void gpio_fortree_exit(void)
{

}



module_init(gpio_fortree_init);
module_exit(gpio_fortree_exit);


MODULE_AUTHOR("Sourcelink");
MODULE_DESCRIPTION("device tree driver");
MODULE_LICENSE("GPL");

接下來就是填充程式碼了,先填充probe函式,再填充remove函式,接下來註冊init和exit函式。。
最後把file_operations結構體填充完。。

完整程式碼:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/pinctrl/consumer.h>


static int source_gpio;
static struct cdev  gpio_drv_cdev;   //核心中用cdev描述一個字元裝置
static struct class *gpio_drv_class;
static int minor;
static int gpio_drv_open(struct inode *inode, struct file *file)
{
        minor = iminor(inode);    /* 獲取檔案的次裝置號 */
       
    printk("gpio_drv_open\n");
        printk("minor:%d \n", minor);
        return 0;
}

static ssize_t gpio_drv_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{
        int ret = 0;
        int val = 0;
       
        // 返回錯誤個數,正確返回0
        if (copy_from_user((int *)&val, user_buf, count)) {
                ret = -EFAULT;
        } else {
                *ppos += count;
                ret = count;
        }
       
        printk("val:%d \n", val);
        if (val == 1) {
                switch(minor){
                        case 0:
                    gpio_set_value(source_gpio, 1);
                                printk("gpio_on\n");
            break;
                }
        } else {
                switch(minor){
                        case 0:
                    gpio_set_value(source_gpio, 0);
                                printk("gpio_off\n");
                break;
                }
        }
       
        return ret;
}


struct file_operations gpio_drv_fops = {
                .owner = THIS_MODULE,
                .open = gpio_drv_open,
                .write = gpio_drv_write,

};



static const struct of_device_id of_gpio_fortree_match[] = {
        { .compatible = "sourcelink_gpio", },
        {},
};


static int  major;
static int gpio_fortree_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
    dev_t devid;
    struct pinctrl *pctrl;
    struct pinctrl_state *pstate;

        printk("enter %s\n",__func__);
       
    pctrl = devm_pinctrl_get(dev);
    if(pctrl == NULL)
    {
        printk("devm_pinctrl_get error\n");
    }
    pstate = pinctrl_lookup_state(pctrl, "sourcelink_gpio");
    if(pstate == NULL)
    {
        printk("pinctrl_lookup_state error\n");
    }
    pinctrl_select_state(pctrl, pstate);        /* gpmc_a12.gpio1_12, OUTPUT | MODE7 */

        source_gpio = of_get_named_gpio(dev->of_node, "source_gpios", 0);

        if (source_gpio <= 0) {
                printk("of_get_named_gpio error!!!\n");
                return -EINVAL;
        } else {
                printk("sourcelink_gpio is %d\n", source_gpio);
                if (devm_gpio_request_one(dev, source_gpio, GPIOF_OUT_INIT_LOW, "source_gpio44") != 0) {
                        printk("devm_gpio_request_one error!!!\n");
                }               
        }

       
         if(alloc_chrdev_region(&devid, 0, 1, "sourcelink") < 0)/* (major,0~1) 對應 hello_fops, (major, 2~255)都不對應hello_fops */
    {
        printk("%s ERROR\n",__func__);
        goto error;
    }
         
    major = MAJOR(devid);                     

    cdev_init(&gpio_drv_cdev, &gpio_drv_fops);                                                //繫結檔案操作函式
    cdev_add(&gpio_drv_cdev, devid, 1);                                                                      //註冊到核心

    gpio_drv_class = class_create(THIS_MODULE, "sourcelink");                         //建立sourcelink類,向類中新增裝置,mdev會幫我們建立裝置節點
    device_create(gpio_drv_class, NULL, MKDEV(major, 0), NULL, "source_gpio44");


        return 0;
       
error:
    unregister_chrdev_region(MKDEV(major, 0), 1);
       
        return -EINVAL;
}



static int gpio_fortree_remove(struct platform_device *pdev)
{
        printk("enter %s\n",__func__);

        device_destroy(gpio_drv_class, MKDEV(major, 0));

        class_destroy(gpio_drv_class);
       
        cdev_del(&gpio_drv_cdev);

        unregister_chrdev_region(MKDEV(major, 0), 1);
       
        return 0;
}



static struct platform_driver gpio_fortree_driver = {
        .probe                = gpio_fortree_probe,
        .remove                = gpio_fortree_remove,
        .driver                = {
                .name        = "sourcelink_gpio",
                .owner        = THIS_MODULE,
                .of_match_table = of_match_ptr(of_gpio_fortree_match),
        },
};


static int gpio_fortree_init(void)
{
        int ret;

        printk("enter %s\n",__func__);
        ret = platform_driver_register(&gpio_fortree_driver);
           if (ret)
                   printk(KERN_ERR "gpio_fortree_probe: probe failed: %d\n", ret);

        return ret;
}



static void gpio_fortree_exit(void)
{
        printk("enter %s\n",__func__);
        platform_driver_unregister(&gpio_fortree_driver);
}



module_init(gpio_fortree_init);
module_exit(gpio_fortree_exit);


MODULE_AUTHOR("Sourcelink");
MODULE_DESCRIPTION("device tree driver");
MODULE_LICENSE("GPL");
驅動我寫了很多列印函式。。很方便除錯。。

printk函式的列印資訊是通過串列埠打印出來的。
還要改下列印級別:
echo 5 > /proc/sys/kernel/printk

用原始碼的除錯吧。。

測試程式碼:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>



int main(int argc, char **argv)
{
        int fd;
        int led_state = 0;
        int i;


        fd = open("/dev/source_gpio44", O_RDWR);

        if (fd < 0)
                printf("can't open!\n");
#if 0

        if (argc != 2) {
                printf("Usage: \n");
                printf("%s <on|off>\n", argv[0]);
                return -1;
        }

        if (strcmp(argv[1], "on") == 0)
                led_state = 1;
        else
                led_state = 0;


        if (write (fd, &led_state, 1)) {
                printf("write successful!\n");
        }

#else
        for (i = 0; i < 10; i++) {
                switch (led_state) {
                        case 0:
                                write (fd, &led_state, 1);
                                printf("led off!\n");
                                led_state = 1;
                        break;
                        case 1:
                                write (fd, &led_state, 1);
                                printf("led on!\n");
                                led_state = 0;
                        break;
                }
                sleep(1);
        }

#endif

        close(fd);

        return 0;
}