Linux学习笔记(20)——基于platform的设备驱动

  1. 设备树文件仍与第18节的保持不变
  2. 编写led设备程序
/* 
 * 文件名   : leddevice.c
 * 作者     : glen  
 * 描述     : leddevice驱动文件
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*
 * 寄存器地址定义 
 */
#define CCM_CCGR1_BASE          (0x020C406C)
#define SW_MUX_GPIO1_IO03_BASE  (0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE  (0x020E02F4)
#define GPIO1_DR_BASE           (0x0209C000)
#define GPIO1_GDIR_BASE         (0x0209C004)
#define REGISTER_LENGTH         4

/**
 * \brief   释放platform设备模块时此函数会执行
 * \param   dev 要释放的设备
 * \retval  无
 */
static void led_release(struct device *dev)
{
    printk("led device release!\r\n");
}

/*
 * 设备资源信息, 也就是LED0所使用的所有寄存器
 */
static struct resource led_resources[] = {
    [0] = {
        .start  = CCM_CCGR1_BASE,
        .end    = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [1] = {
        .start  = SW_MUX_GPIO1_IO03_BASE,
        .end    = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [2] = {
        .start  = SW_PAD_GPIO1_IO03_BASE,
        .end    = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [3] = {
        .start  = GPIO1_DR_BASE,
        .end    = (GPIO1_DR_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },

    [4] = {
        .start  = GPIO1_GDIR_BASE,
        .end    = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1),
        .flags  = IORESOURCE_MEM,
    },
};

/*
 * platform设备结构体
 */
static struct platform_device leddevice = {
    .name   = "glen-led",
    .id     = -1,
    .dev    = {
        .release = &led_release,
    },

    .num_resources = ARRAY_SIZE(led_resources),
    .resource = led_resources,
};

/**
 * \brief   设备模块加载
 * \param   无
 * \retval  无
 */
static int __init leddevice_init(void)
{
    return platform_device_register(&leddevice);
}

/**
 * \brief   设备模块注销
 * \param   无
 * \retval  无
 */
static void __exit leddevice_exit(void)
{
    platform_device_unregister(&leddevice);
}

/* 设备注册入口, 展开后
 * static initcall_t \
 *        __initcall_leddevice_init6 \
 *        __used \
 *        __attribute__((__section__(".initcall6.init"))) \
 *        = leddevice_init;
 */
module_init(leddevice_init);

/* 设备注册出口, 展开后
 * static exitcall_t \
 *        __exitcall_leddevice_exit \
 *        __exit_call \
 *        = leddevice_exit;
 */
module_exit(leddevice_exit);

/* 模块的许可证声明, 展开后
 * static const char __UNIQUE_ID_license__COUNTER__[] \
 *   __used __attribute__((section(".modinfo"), unused, aligned(1))) \
 *  = "license=GPL";
 */
MODULE_LICENSE("GPL");

/* 模块的作者声明, 展开后
 * static const char __UNIQUE_ID_author__COUNTER__[]					  \
 * __used __attribute__((section(".modinfo"), unused, aligned(1)))	  \
 * = "author=glen_cao"
 */
MODULE_AUTHOR("glen");
  1. 编写led驱动程序
/* 
 * 文件名   : leddriver.c
 * 作者     : glen  
 * 描述     : leddriver驱动文件
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define LEDDEV_CNT  1           /* 设备号长度 */
#define LEDDEV_NAME "platled"   /* 设备名字 */
#define LEDOFF       "OFF" 
#define LEDON        "ON"

/*
 * 寄存器名
 */
static void __iomem *imx6u_ccm_ccgr1;
static void __iomem *sw_mux_gpio1_io03;
static void __iomem *sw_pad_gpio1_io03;
static void __iomem *gpio1_dr;
static void __iomem *gpio1_gdir;

/* leddev设备结构体 */
struct leddev_dev {
    dev_t   devid;              /* 设备号 */
    struct cdev cdev;           /* cdev */
    struct class *class;        /* 类 */
    struct device *device;      /* 设备 */
    int major;                  /* 主设备号 */
};

struct leddev_dev leddev;       /* led设备 */

/**
 * \brief   LED打开/关闭
 * \param   sta
 * \retval  无
 */
void led0_switch(char *stat)
{
    u32 val = 0;

    if (strcmp(stat, LEDON) == 0) {
        val = readl(gpio1_dr);
        val &= ~(1 << 3);
        writel(val, gpio1_dr);
    } else if (strcmp(stat, LEDOFF) == 0) {
        val = readl(gpio1_dr);
        val |= (1 << 3);
        writel(val, gpio1_dr);
    }

}

/**
 * \brief   打开设备
 * \param   inode   传递给驱动的inode
 *          filp    设备文件
 * \retval  0 成功; 其它 失败
 */
static int led_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &leddev;       /* 设置私有数据 */
    return 0;
}

/**
 * \brief   向设备写数据
 * \param   filp    设备文件
 *          buf     要写给设备写入的数据
 *          cnt     要写入的数据长度
 *          offt    相对于文件首地址的偏移
 * \retval  写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int ret;
    char data_buf[8];

    ret = copy_from_user(data_buf, buf, ((cnt < sizeof(data_buf) ? cnt : sizeof(data_buf))));
    if (ret < 0) {
        return -EFAULT;
    }

    led0_switch(data_buf);
    return 0;
}

/*
 * 设备操作函数
 */
static struct file_operations led_fops = {
    .owner  = THIS_MODULE,
    .open   = led_open,
    .write  = led_write,
};

/**
 * \brief   platform驱动的probe函数,当驱动与设备匹配以后此函数就会执行
 * \param   dev platform设备
 * \retval  0,成功    负值,失败
 */
static int led_probe(struct platform_device *dev)
{
    int i = 0;
    u32 res_sz[5];
    u32 val = 0;
    struct resource *reg_led[5];

    printk("led driver and device has matched!\r\n");

    /* 获取资源 */
    for (i = 0; i < 5; i++) {
        reg_led[i] = platform_get_resource(dev, IORESOURCE_MEM, i);

        if (!reg_led[i]) {
            dev_err(&dev->dev, "No Memory resource for always on\n");
            return -ENXIO;
        }

        res_sz[i] = resource_size(reg_led[i]);
    }

    /* 初始化LED */
    /* 寄存器地址映射 */
    imx6u_ccm_ccgr1 = ioremap(reg_led[0]->start, res_sz[0]);
    sw_mux_gpio1_io03 = ioremap(reg_led[1]->start, res_sz[1]);
    sw_pad_gpio1_io03 = ioremap(reg_led[2]->start, res_sz[2]);
    gpio1_dr = ioremap(reg_led[3]->start, res_sz[3]);
    gpio1_gdir = ioremap(reg_led[4]->start, res_sz[4]);

    val = readl(imx6u_ccm_ccgr1);
    val |= (3 << 26);               /* 设置时钟 */
    writel(val, imx6u_ccm_ccgr1);

    /* 设置gpio1_io03复用功能, 将其复用为gpio1_io03 */
    writel(5, sw_mux_gpio1_io03);
    writel(0x10B0, sw_pad_gpio1_io03);

    /* 设置gpio1_io03为输出功能 */
    val = readl(gpio1_gdir);        
    val |= (1 << 3);                /* 设置为输出 */
    writel(val, gpio1_gdir);

    /* 关闭LED */
    val = readl(gpio1_dr);
    val |= (1 << 3);
    writel(val, gpio1_dr);

    /* 注册字符设备驱动 */
    /* 创建设备号 */
    if (leddev.major) {
        leddev.devid = MKDEV(leddev.major, 0);
        register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
    } else {
        alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
        leddev.major = MAJOR(leddev.devid);
    }

    /* 初始化cdev */
    leddev.cdev.owner = THIS_MODULE;
    cdev_init(&leddev.cdev, &led_fops);

    /* 添加一个cdev */
    cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);

    /* 创建类 */
    leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
    if (IS_ERR(leddev.class)) {
        return PTR_ERR(leddev.class);
    }

    /* 创建设备 */
    leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
    if (IS_ERR(leddev.device)) {
        return PTR_ERR(leddev.device);
    }

    return 0;
}

/**
 * \brief   移除platform驱动的时候此函数会执行
 * \param   dev platform设备
 * \retval  0,成功; 其他负值,失败
 */
static int led_remove(struct platform_device *dev)
{
    iounmap(imx6u_ccm_ccgr1);
    iounmap(sw_mux_gpio1_io03);
    iounmap(sw_pad_gpio1_io03);
    iounmap(gpio1_dr);
    iounmap(gpio1_gdir);

    cdev_del(&leddev.cdev);
    unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
    device_destroy(leddev.class, leddev.devid);
    class_destroy(leddev.class);

    return 0;
}

/* platform驱动结构体 */
static struct platform_driver led_driver = {
    .driver = {
        .name = "glen-led",
    },
    .probe = led_probe,
    .remove = led_remove,
};

/**
 * \brief   驱动模块加载函数
 * \param   无
 * \retval  无
 */
static int __init leddriver_init(void)
{
    return platform_driver_register(&led_driver);
}

/**
 * \brief   驱动模块缷载函数
 * \param   无
 * \return  无
 */
static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&led_driver);
}

/* 设备注册入口, 展开后
 * static initcall_t \
 *        __initcall_leddriver_init6 \
 *        __used \
 *        __attribute__((__section__(".initcall6.init"))) \
 *        = leddriver_init;
 */
module_init(leddriver_init);

/* 设备注册出口, 展开后
 * static exitcall_t \
 *        __exitcall_leddriver_exit \
 *        __exit_call \
 *        = leddriver_exit;
 */
module_exit(leddriver_exit);

/* 模块的许可证声明, 展开后
 * static const char __UNIQUE_ID_license__COUNTER__[] \
 *   __used __attribute__((section(".modinfo"), unused, aligned(1))) \
 *  = "license=GPL";
 */
MODULE_LICENSE("GPL");

/* 模块的作者声明, 展开后
 * static const char __UNIQUE_ID_author__COUNTER__[]					  \
 * __used __attribute__((section(".modinfo"), unused, aligned(1)))	  \
 * = "author=glen_cao"
 */
MODULE_AUTHOR("glen");
  1. 编写测试程序并拷贝到NFS文件系统目录下
/*
 * 文件名   :  platform_test.c
 * 作者     :  glen
 * 描述     :  platform测试程序
 */
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
#include "signal.h"

/**
 * @brief   : main函数
 * @par     : argc  argv数组元素的个数
 *            argv  参数数组
 * @retval  : 0 成功    其它 失败
 */
int main(int argc, char *argv[])
{
    static int fd = 0;  /* 文件描述符 */
    int ret = 0;
    char *filename;

    if (argc != 3) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);       /* 阻塞访问 */
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    ret = write(fd, argv[2], sizeof(argv[2]));
    if (ret < 0) {
        printf("LED control failure!\r\n");
        close(fd);
        return -1;
    }

    /* 关闭文件 */
    ret = close(fd);
    if (ret < 0) {
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}

拷贝到NFS文件系统目录下

glen@ubuntu:~/linux/imx6ull/linux/driver/15_platform$ sudo cp leddevice.ko leddriver.ko platform_test ~/linux/nfs/rootfs/lib/modules/4.1.15 -rf
  1. 测试程序
    5.1 加载设备程序模块和驱动程序模块
/lib/modules/4.1.15 # insmod leddevice.ko
/lib/modules/4.1.15 # insmod leddriver.ko
led driver and device has matched!

5.2 查看platform平台的设备和驱动信息
查看platform平台的设备信息

/lib/modules/4.1.15 # ls /sys/bus/platform/devices/
20a8000.gpio                          2290000.iomuxc-snvs
20ac000.gpio                          2294000.snvs-gpr
20b0000.snvs                          900000.sram
20b4000.ethernet                      904000.sram
20bc000.wdog                          905000.sram
20c4000.ccm                           Vivante GCCore
20c8000.anatop                        a01000.interrupt-controller
20c8000.anatop:regulator-3p0@120      alarmtimer
20c8000.anatop:regulator-vddcore@140  backlight
20c8000.anatop:regulator-vddsoc@140   beep
20c9000.usbphy                        ci_hdrc.0
20ca000.usbphy                        ci_hdrc.1
20cc000.snvs                          glen-led

查看platform平台的驱动信息

/lib/modules/4.1.15 # ls /sys/bus/platform/drivers/
CCI-400            imx-gpc            imx6sll-pinctrl    of-flash
CCI-400 PMU        imx-gpcv2          imx6sx-pinctrl     pata_imx
ahci               imx-i2c            imx6ul-pinctrl     platform-lcd
ahci-imx           imx-ipuv3          imx6ul-tsc         pwm-backlight
alarmtimer         imx-keypad         imx7d-pinctrl      pxp-v4l2
anatop_regulator   imx-mmdc           imx_busfreq        reg-dummy
arm-pmu            imx-mu             imx_mc13783        reg-fixed-voltage
chipidea-usb2      imx-pre            imx_thermal        sdhci-esdhc-imx
ci_hdrc            imx-prg            imx_usb            smc911x
coda               imx-pwm            ldo2p5-dummy       smc91x
da9052-regulator   imx-pxp            leds-gpio          smsc911x
eukrea_tlv320      imx-rpmsg          mc13783-regulator  snd-soc-dummy
fec                imx-sdma           mc13892-regulator  snvs_pwrkey
fsl-lpuart         imx-sema4          msm_hsusb          snvs_rtc
fsl-quadspi        imx-sgtl5000       mx3-camera         soc-audio
fsl-spdif-dai      imx-spdif          mx3_sdc_fb         soc-camera-pdrv
fsl-ssi-dai        imx-ssi            mxc-ehci           spi_imx
generic-bl         imx-uart           mxc_lcdif          sram
glen-led           imx-weim           mxc_mlb150         stmpe-ts
gpio-keys          imx-wm8962         mxc_nand           syscon
gpio-mxc           imx2-wdt           mxc_rtc            usb_phy_generic
gpio-rc-recv       imx50-pinctrl      mxc_sdc_fb         usbmisc_imx
gpio-reset         imx53-pinctrl      mxc_vdoa           vf610-pinctrl
gpio-vf610         imx6dl-pinctrl     mxc_vpu            zevio_usb
gpmi-nand          imx6q-cpufreq      mxs-dma
imx-audmux         imx6q-pinctrl      mxs_phy
imx-ddrc           imx6sl-pinctrl     mxsfb

5.3 测试

/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF
/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF
/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF
/lib/modules/4.1.15 # ./platform_test /dev/platled ON
/lib/modules/4.1.15 # ./platform_test /dev/platled OFF

你可能感兴趣的:(Linux)