邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛
分类: LINUX
2017-03-02 10:50:37
可以看到,将一个普通字符驱动改写为平台驱动,驱动各方法方法的实现以及 fops 定
2. led_drv 模块点击(此处)折叠或打开
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/platform_device.h>
- #define GPIO_LED_PIN_NUM 55 /* gpio 1_23 */
- /* 定义 LED 资源 */
- static struct resource led_resources[] = {
- [0] = {
- .start = GPIO_LED_PIN_NUM,
- .end = GPIO_LED_PIN_NUM,
- .flags = IORESOURCE_IO,
- },
- };
- static void led_platform_release(struct device *dev)
- {
- return;
- }
- /* 定义平台设备 */
- static struct platform_device led_platform_device = {
- .name = "led", /* platform_driver 中, .name 必须与该名字相同 */
- .id = -1,
- .num_resources = ARRAY_SIZE(led_resources),
- .resource = led_resources,
- .dev = {
- /* Device 'led' does not have a release() function, it is broken and must be fixed. */
- .release = led_platform_release,
- .platform_data = NULL,
- },
- };
- static int __init led_platform_init(void)
- {
- int ret;
- ret = platform_device_register(&led_platform_device);
- if (ret < 0) {
- platform_device_put(&led_platform_device);
- return ret;
- }
- return 0;
- }
- static void __exit led_platform_exit(void)
- {
- platform_device_unregister(&led_platform_device);
- }
- module_init(led_platform_init);
- module_exit(led_platform_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("xxxxx");
led_drv 模块由 led_drv.c 和 led_drv.h 两个文件组成:
程序清单 2.42 led_drv.c 参考代码
程序清单 2.19 LED 驱动头文件点击(此处)折叠或打开
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/version.h>
- #include <linux/fs.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/platform_device.h>
- #include <asm/gpio.h>
- #include "led_drv.h"
- static int major;
- static int minor;
- struct cdev *led; /* cdev 数据结构 */
- static dev_t devno; /* 设备编号 */
- static struct class *led_class;
- static int led_io; /* 用于保存 GPIO 编号 */
- #define DEVICE_NAME "led"
- static int led_open(struct inode *inode, struct file *file )
- {
- try_module_get(THIS_MODULE);
- gpio_direction_output(led_io, 1);
- return 0;
- }
- static int led_release(struct inode *inode, struct file *file )
- {
- module_put(THIS_MODULE);
- gpio_direction_output(led_io, 1);
- return 0;
- }
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
- int led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- #else
- static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
- #endif
- {
- if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) {
- return -ENOTTY;
- }
- if (_IOC_NR(cmd) > LED_IOCTL_MAXNR) {
- return -ENOTTY;
- }
- switch(cmd) {
- case LED_ON:
- gpio_set_value(led_io, 0);
- break;
- case LED_OFF:
- gpio_set_value(led_io, 1);
- break;
- default:
- gpio_set_value(led_io, 0);
- break;
- }
- return 0;
- }
- struct file_operations led_fops = {
- .owner = THIS_MODULE,
- .open = led_open,
- .release = led_release,
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
- .unlocked_ioctl = led_ioctl
- #else
- .ioctl = led_ioctl
- #endif
- };
- static int __devinit led_probe(struct platform_device *pdev)
- {
- int ret;
- struct resource *res_io;
- res_io = platform_get_resource(pdev, IORESOURCE_IO, 0); /* 从设备资源获取 IO 引脚 */
- led_io = res_io->start;
- ret = alloc_chrdev_region(&devno, minor, 1, DEVICE_NAME); /* 从系统获取主设备号 */
- major = MAJOR(devno);
- if (ret < 0) {
- printk(KERN_ERR "cannot get major %d \n", major);
- return -1;
- }
- led = cdev_alloc(); /* 分配 led 结构 */
- if (led != NULL) {
- cdev_init(led, &led_fops); /* 初始化 led 结构 */
- led->owner = THIS_MODULE;
- if (cdev_add(led, devno, 1) != 0) { /* 增加 led 到系统中 */
- printk(KERN_ERR "add cdev error!\n");
- goto error;
- }
- } else {
- printk(KERN_ERR "cdev_alloc error!\n");
- return -1;
- }
- led_class = class_create(THIS_MODULE, DEVICE_NAME"_class");
- if (IS_ERR(led_class)) {
- printk(KERN_INFO "create class error\n");
- return -1;
- }
- device_create(led_class, NULL, devno, NULL, DEVICE_NAME);
- return 0;
- error:
- unregister_chrdev_region(devno, 1); /* 释放已经获得的设备号 */
- return ret;
- }
- static int __devexit led_remove(struct platform_device *dev)
- {
- cdev_del(led); /* 移除字符设备 */
- unregister_chrdev_region(devno, 1); /* 释放设备号 */
- device_destroy(led_class, devno);
- class_destroy(led_class);
- return 0;
- }
- /* 定义和初始化平台驱动 */
- static struct platform_driver led_platform_driver = {
- .probe = led_probe,
- .remove = __devexit_p(led_remove),
- .driver = {
- .name = "led", /* 该名称必须与 platform_device 的.name 相同 */
- .owner = THIS_MODULE,
- },
- };
- static int __init led_init(void)
- {
- return(platform_driver_register(&led_platform_driver));
- }
- static void __exit led_exit(void)
- {
- platform_driver_unregister(&led_platform_driver);
- }
- module_init(led_init);
- module_exit(led_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("xxxxxxx")
3. 测试程序点击(此处)折叠或打开
- #ifndef _LED_DRV_H
- #define _LED_DRV_H
- #define LED_IOC_MAGIC 'L'
- #define LED_ON _IO(LED_IOC_MAGIC, 0)
- #define LED_OFF _IO(LED_IOC_MAGIC, 1)
- #define LED_IOCTL_MAXNR 2
- #endif /*_LED_DRV_H*/
驱动编写完成后,还需编写一个测试程序,用来测试驱动的正确性。 程序清单 2.20 是
一个简单的测试程序。打开设备后,通过 ioctl 方法, 控制 LED 闪烁 3 次。
注意, 如果驱动定义了 ioctl 命令,则应用程序必须有这些命令的定义,通常做法是包
含驱动头文件。
程序清单 2.20 LED 测试程序 led_test.c
点击(此处)折叠或打开
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <errno.h>
- #include <fcntl.h>
- #include "../led_drv.h"
- #define DEV_NAME "/dev/led"
- int main(int argc, char *argv[])
- {
- int i;
- int fd = 0;
- fd = open (DEV_NAME, O_RDONLY);
- if (fd < 0) {
- perror("Open "DEV_NAME" Failed!\n");
- exit(1);
- }
- for (i=0; i<3; i++) {
- ioctl(fd, LED_ON);
- sleep(1);
- ioctl(fd, LED_OFF);
- sleep(1);
- }
- close(fd);
- return 0;
- }
4. 测试
将两份驱动分别编译得到 led_platform.ko 和 led_drv.ko 两个驱动模块,按顺序依次插入,
编译测试程序得到led_test,然后运行:
[root@ M283 mnt]# insmod led_platform.ko
[root@ M283 mnt]# insmod led_drv.ko
[root@ M283 mnt]# ./led_test