结合之前对Linux内核的platform总线 ,以及对字符设备的cdev接口的分析,本文将编写基于platform总线与cdev接口的LED设备的实例代码并对其进行分析。

platform总线分析,详见Linux platform驱动模型。

字符设备的cdev接口分析,详见Linux字符设备驱动(一):cdev接口。

硬件接口:

  CPU:s5pv210;

  LED的GPIO:GPIO_J0_3 ~ GPIO_J0_5;

  LED的工作方式:低电平亮,高电平灭。

1. led_device.c

本文将设备信息写成一个模块的形式,需要时加载该模块即可。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>//定义并初始化LED设备的相关资源
static struct resource led_resource =
{.start = 0xE0200240,.end = 0xE0200240 + 8 - 1,.flags = IORESOURCE_MEM,
};//定义并初始化LED设备信息
static struct platform_device led_dev =
{.name = "led",             //设备名称.id = -1,                  //设备数量,-1表示只有一个设备.num_resources = 1,        //资源数量.resource = &led_resource, //资源指针.dev = {.release = led_release,},
};//注册LED设备
static int __init led_device_init(void)
{return platform_device_register(&led_dev);
}//注销LED设备
static void __exit led_device_exit(void)
{platform_device_unregister(&led_dev);
}module_init(led_device_init);
module_exit(led_device_exit);MODULE_AUTHOR("Lin");
MODULE_DESCRIPTION("led device for x210");
MODULE_LICENSE("GPL");

2. led_driver.c

led_driver_init():模块加载函数
  platform_driver_register()将驱动对象模块注册到平台总线
  led_probe()探测函数,提取相应的信息
    platform_get_resource()获取设备资源
    request_mem_region()、ioremap()虚拟内存映射
    readl()、write()初始化硬件设备
    cdev_alloc()申请cdev内存
    cdev_init()初始化cdev对象,将cdev与fops结构体绑定
    alloc_chrdev_region()申请设备号
    cdev_add()注册LED设备对象,将cdev添加至系统字符设备链表中
    class_create()创建设备类
    device_create()创建设备文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/leds.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/ioctl.h>#define LED_IOC_MAGIC  'l'                  //ioctl幻数
#define LED_IOC_MAXNR    2                  //ioctl最大命令序数
#define    LED_ON    _IO(LED_IOC_MAGIC, 0)   //ioctl自定义命令
#define    LED_OFF    _IO(LED_IOC_MAGIC, 1)#define DEVNAME "led"        //设备名称static int led_major = 0;    //主设备号
static int led_minor = 0;    //次设备号
const  int led_count = 1;    //次设备数量//GPIO寄存器变量定义
typedef struct GPJ0REG
{volatile unsigned int gpj0con;volatile unsigned int gpj0dat;
}gpj0_reg_t;static gpj0_reg_t *pGPIOREG = NULL;static dev_t led_devnum;                 //设备号
static struct cdev *led_cdev = NULL;     //设备对象static struct class *led_cls = NULL;     //设备类
static struct device *led_dev = NULL;    //设备//LED设备的ioctl函数实现
static int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{int reg_value = 0;//检测命令的有效性if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) return -EINVAL;if (_IOC_NR(cmd) > LED_IOC_MAXNR) return -EINVAL;//根据命令,执行相应的硬件操作switch(cmd) {case LED_ON:reg_value = readl(&pGPIOREG->gpj0dat); reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5));writel(&pGPIOREG->gpj0dat, reg_value);break;case LED_OFF:reg_value = readl(&pGPIOREG->gpj0dat); reg_value |= (1 << 3) | (1 << 4) | (1 << 5);writel(&pGPIOREG->gpj0dat, reg_value);break;default:  return -EINVAL;}return 0;
}static int led_open(struct inode *inode, struct file *filp)
{return 0;
}static int led_release(struct inode *inode, struct file *filp)
{return 0;
}//LED设备的write函数实现
static ssize_t led_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
{char kbuf[20] = {0};int reg_value = 0;memset(kbuf, 0, sizeof(kbuf));if (copy_from_user(kbuf, user_buf, count)){return -EFAULT;}if (kbuf[0] == '0'){reg_value = readl(&(pGPIOREG->gpj0dat)); reg_value |= (1 << 3) | (1 << 4) | (1 << 5);writel(reg_value, &(pGPIOREG->gpj0dat));}else{reg_value = readl(&(pGPIOREG->gpj0dat));  reg_value &= ~((1 << 3) | (1 << 4) | (1 << 5));writel(reg_value, &(pGPIOREG->gpj0dat));}return 1;
}//定义并初始化LED设备的操作集
static const struct file_operations led_ops =
{.owner      = THIS_MODULE,.open       = led_open,.write      = led_write,.ioctl      = led_ioctl,.release    = led_release,
};//LED设备的probe函数实现
static int led_probe(struct platform_device *pdev)
{struct resource *res_led = NULL;int ret = -1;int reg_value = 0;int i = 0;/****************************申请资源*******************************///获取资源res_led = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res_led) {return -ENOMEM;}//动态内存映射if (!request_mem_region(res_led->start, resource_size(res_led), "GPIOJ0")){return -EBUSY;}pGPIOREG = ioremap(res_led->start, resource_size(res_led));if (pGPIOREG ==  NULL) {ret = -ENOENT;goto ERR_STEP;}/****************************初始化资源*******************************///设置GPIO为输出模式reg_value = readl(&(pGPIOREG->gpj0con)); reg_value |= (1 << (3*4)) | (1 << (4*4)) | (1 << (5*4));writel(reg_value, &(pGPIOREG->gpj0con));/***************************创建接口(cdev)***************************///申请cdev内存led_cdev = cdev_alloc();if (led_cdev == NULL){ret = -ENOMEM;goto ERR_STEP1;}//初始化led_cdev(将led_cdev于led_ops关联)cdev_init(led_cdev, &led_ops);//申请设备号ret = alloc_chrdev_region(&led_devnum, led_minor, led_count, DEVNAME);if (ret < 0){goto ERR_STEP1;}//注册LED设备对象(将cdev添加至系统字符设备链表中)ret = cdev_add(led_cdev, led_devnum, led_count);if (ret < 0){goto ERR_STEP2;}//创建设备类led_cls = class_create(THIS_MODULE, DEVNAME);if (IS_ERR(led_cls)) {ret = PTR_ERR(led_cls);goto ERR_STEP3;}//在设备类下创建设备文件led_major = MAJOR(led_devnum);for(i = led_minor; i < (led_count + led_minor); i++){led_dev = device_create(led_cls, NULL, MKDEV(led_major, i), NULL, "%s%d", DEVNAME, i);if(IS_ERR(led_dev)){ret = PTR_ERR(led_dev);goto ERR_STEP4;}}return 0;/*******************************倒映式错误处理*******************************/
ERR_STEP4:for(--i; i >= led_minor; i--){device_destroy(led_cls, MKDEV(led_major, i));}class_destroy(led_cls);ERR_STEP3:cdev_del(led_cdev);ERR_STEP2:unregister_chrdev_region(led_devnum, led_count);ERR_STEP1:iounmap(pGPIOREG);    ERR_STEP:release_mem_region(res_led->start, resource_size(res_led));return ret;
}int led_remove(struct platform_device *pdev)
{int i = 0;//删除设备文件for(i = led_minor; i < (led_count + led_minor); i++){device_destroy(led_cls, MKDEV(led_major, i));}//删除设备类
    class_destroy(led_cls);//删除设备对象
    cdev_del(led_cdev);//注销设备号
    unregister_chrdev_region(led_devnum, led_count);//释放内存
    iounmap(pGPIOREG);return 0;
}//定义并初始化LED驱动信息
static struct platform_driver led_drv =
{.driver = {.name  = "led",.owner = THIS_MODULE,},.probe = led_probe,.remove = led_remove,
};//注册LED驱动
static int __init led_driver_init(void)
{return platform_driver_register(&led_drv);
}//注销LED驱动
static void __exit led_driver_exit(void)
{platform_driver_unregister(&led_drv);
}module_init(led_driver_init);
module_exit(led_driver_exit);MODULE_AUTHOR("Lin");
MODULE_DESCRIPTION("led driver for x210");
MODULE_LICENSE("GPL");

3. 测试所用应用程序

运行应用程序之前,需确保上述两个模块(device、driver)被装载。运行结果表明LED设备能被应用程序操作。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>#define    FILE_NAME    "/dev/led0"#define    LED_ON    _IO(LED_IOC_MAGIC, 0)
#define    LED_OFF    _IO(LED_IOC_MAGIC, 1)char WriteBuf[30];
char ReadBuf[30];
char ScanBuf[30];int main(void)
{int fd = -1;int i = 0;//打开设备文件if ((fd = open(FILE_NAME, O_RDWR)) < 0){printf("%s open error\n", FILE_NAME);return -1;}while (1){memset(ScanBuf, 0, sizeof(ScanBuf));printf("please input data for LED\n");if (scanf("%s", ScanBuf)){//打开LED设备if (!strcmp(ScanBuf, "on")){write(fd, "1", 1);}//关闭LED设备else if (!strcmp(ScanBuf, "off")){write(fd, "0", 1);}//闪烁LED设备else if (!strcmp(ScanBuf, "flash")){for (i=5; i>0; i--){ioctl(fd, LED_ON);sleep(1);ioctl(fd, LED_OFF);sleep(1);}}else {break;}}}close(fd);return 0;
}

转载于:https://www.cnblogs.com/linfeng-learning/p/9376804.html

驱动程序实例(一):LED设备驱动程序( platform + cdev)相关推荐

  1. 嵌入式Linux设备驱动程序:编写内核设备驱动程序

    嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...

  2. linux kernel 2.6 i2c设备驱动程序框架介绍,linux设备驱动程序-i2c(2)-adapter和设备树的解析...

    linux设备驱动程序-i2c(2)-adapter和设备树的解析 (注: 基于beagle bone green开发板,linux4.14内核版本) 而在linux设备驱动程序--串行通信驱动框架分 ...

  3. linux设备驱动程序调试方法,Linux设备驱动程序学习(2)-调试技术

    Linux设备驱动程序学习(2)-调试技术 Linux设备驱动程序学习(2)-调试技术 今天进入<Linux设备驱动程序(第3版)>第四章调试技术的学习. 一.内核中的调试支持 在前面已经 ...

  4. 驱动程序开发:无设备树和有设备树的platform驱动

    1.Linux 驱动的分离与分层   对与对IO进行最简单的读写操作,无需考虑太多的怎么使它重用性强,而像I2C. SPI.LCD 等这些复杂外设的驱动,Linux 系统要考虑到驱动的可重用性,因此提 ...

  5. Linux 设备驱动程序(二)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...

  6. 嵌入式linux查看usb设备驱动程序,嵌入式Linux下USB驱动程序的设计

    嵌入式Linux下USB驱动程序的设计 usb概念:  USB(Universal Serial Bus)即通用串行总线,是一种全新的双向同步传输的支持热插拔的数据传输总线,其目的是为了提供一种兼容不 ...

  7. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

  8. 【网站推荐】Solaris 平台编写设备驱动程序

    Documentation Home  > 编写设备驱动程序 Book Information 索引 前言 第 1 部分 针对 Solaris 平台设计设备驱动程序 第 1 章 Solaris ...

  9. 嵌入式Linux设备驱动程序:发现硬件配置

    嵌入式Linux设备驱动程序:发现硬件配置 Embedded Linux device drivers: Discovering the hardware configuration Interfac ...

最新文章

  1. 操作系统原理之I/O设备管理(第六章下半部分)
  2. FileSystemWatcher监听文件是否有被修改
  3. matlab 返回变量类型的命令,MATLAB主要命令汇总
  4. Mblog 开源Java多人博客系统
  5. html网页制作每周食谱,新增食谱.html
  6. 下载部署和管理Windows Azure应用程序评估
  7. Duplicate entry 'xxx' for key 'xxx'
  8. Azure上部署FTP服务
  9. 在Word中如何进行半行输入
  10. Linux-Grep命令详解
  11. ssm企业人事管理系统人事管理系统(企业人事管理系统)企业人事人力资源管理系统
  12. 熊猫烧香手工清除实验
  13. astrolog php,如何在苹果MAC上使用Astrolog32 zet9等占星软件
  14. GoAhead2.5源代码分析之19-web层(webs.c)
  15. 微信小程序input组件里,自动聚焦focus没反应
  16. android小米4c 权限,小米手机4c详细刷成开发版获得Root权限的步骤
  17. sccm安装linux系统,SCCM Client for LINUX on FIPS Enabled Systems
  18. python怎么输入空行_python如何添加空行
  19. primeng dropdown ngmodel 选择项初始化
  20. 提示用户输入一个高考_高考报名用户名密码显示错误怎么办

热门文章

  1. html5 Canvas画图教程(5)—canvas里画曲线之arc方法
  2. Python 入门篇-用Notepad++编写出第一个python程序
  3. CTFshow php特性 web102
  4. [BJDCTF2020]EzPHP 1
  5. 一个python网上文档
  6. 删除链表中重复的结点
  7. php amp ldquo 转换,php与html代码的若干转换
  8. 最全的C#图片处理帮助类ImageHelper
  9. IDEA编译的JAR包运行出现“没有主清单属性”
  10. L1-003 个位数统计