驱动程序实例(一):LED设备驱动程序( platform + cdev)
结合之前对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)相关推荐
- 嵌入式Linux设备驱动程序:编写内核设备驱动程序
嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...
- linux kernel 2.6 i2c设备驱动程序框架介绍,linux设备驱动程序-i2c(2)-adapter和设备树的解析...
linux设备驱动程序-i2c(2)-adapter和设备树的解析 (注: 基于beagle bone green开发板,linux4.14内核版本) 而在linux设备驱动程序--串行通信驱动框架分 ...
- linux设备驱动程序调试方法,Linux设备驱动程序学习(2)-调试技术
Linux设备驱动程序学习(2)-调试技术 Linux设备驱动程序学习(2)-调试技术 今天进入<Linux设备驱动程序(第3版)>第四章调试技术的学习. 一.内核中的调试支持 在前面已经 ...
- 驱动程序开发:无设备树和有设备树的platform驱动
1.Linux 驱动的分离与分层 对与对IO进行最简单的读写操作,无需考虑太多的怎么使它重用性强,而像I2C. SPI.LCD 等这些复杂外设的驱动,Linux 系统要考虑到驱动的可重用性,因此提 ...
- Linux 设备驱动程序(二)
系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...
- 嵌入式linux查看usb设备驱动程序,嵌入式Linux下USB驱动程序的设计
嵌入式Linux下USB驱动程序的设计 usb概念: USB(Universal Serial Bus)即通用串行总线,是一种全新的双向同步传输的支持热插拔的数据传输总线,其目的是为了提供一种兼容不 ...
- 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序
嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...
- 【网站推荐】Solaris 平台编写设备驱动程序
Documentation Home > 编写设备驱动程序 Book Information 索引 前言 第 1 部分 针对 Solaris 平台设计设备驱动程序 第 1 章 Solaris ...
- 嵌入式Linux设备驱动程序:发现硬件配置
嵌入式Linux设备驱动程序:发现硬件配置 Embedded Linux device drivers: Discovering the hardware configuration Interfac ...
最新文章
- 操作系统原理之I/O设备管理(第六章下半部分)
- FileSystemWatcher监听文件是否有被修改
- matlab 返回变量类型的命令,MATLAB主要命令汇总
- Mblog 开源Java多人博客系统
- html网页制作每周食谱,新增食谱.html
- 下载部署和管理Windows Azure应用程序评估
- Duplicate entry 'xxx' for key 'xxx'
- Azure上部署FTP服务
- 在Word中如何进行半行输入
- Linux-Grep命令详解
- ssm企业人事管理系统人事管理系统(企业人事管理系统)企业人事人力资源管理系统
- 熊猫烧香手工清除实验
- astrolog php,如何在苹果MAC上使用Astrolog32 zet9等占星软件
- GoAhead2.5源代码分析之19-web层(webs.c)
- 微信小程序input组件里,自动聚焦focus没反应
- android小米4c 权限,小米手机4c详细刷成开发版获得Root权限的步骤
- sccm安装linux系统,SCCM Client for LINUX on FIPS Enabled Systems
- python怎么输入空行_python如何添加空行
- primeng dropdown ngmodel 选择项初始化
- 提示用户输入一个高考_高考报名用户名密码显示错误怎么办