欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

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

发布时间:2025/4/9 编程问答 65 豆豆
生活随笔 收集整理的这篇文章主要介绍了 驱动程序实例(一):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)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。