添加内核驱动模块(4)(mydriver.c+ Konfig+Makefile )
之前在注册SPIDRIVER时,probe函数中,使用了
gpio_pmodoled_setup_cdev()
gpio_pmodoled_init_gpio()
gpio_pmodoled_disp_init()
我们现在来编写这些函数。
先编写gpio_pmodoled_setup_cdev()。
static int gpio_pmodoled_setup_cdev(struct gpio_pmodoled_device *dev, dev_t *dev_id, struct spi_device *spi)
{int status = 0;struct device *device;cdev_init(&dev->cdev, &gpio_pmodoled_cdev_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &gpio_pmodoled_cdev_fops;dev->spi = spi;*dev_id = MKDEV(MAJOR(gpio_pmodoled_dev_id), cur_minor++);status = cdev_add(&dev->cdev, *dev_id, 1);if(status < 0) {return status;}/* Add Device node in system */device = device_create(gpio_pmodoled_class, NULL,*dev_id, NULL,"%s", dev->name);if(IS_ERR(device)) {status = PTR_ERR(device);dev_err(&spi->dev, "failed to create device node %s, err %d\n",dev->name, status);cdev_del(&dev->cdev);}return status;
}
caller调用函数时,会传递一些参数。
dev是一个struct gpio_pmodoled_device型的指针,它执行PMOD设备描述块。
dev_id是一个dev_t型的指针,它指向一个DEVID描述块。
spi是一个struct spi_device型的指针,它指向一个SPIDEVICE设备描述块。
首先初始化CDEV描述块。
PMOD描述块中,内含一个CDEV描述块。我们要使用的,就是这个描述块。
另外,要给CDEV关联一个fops。
我么定义了一个全局变量gpio_pmodoled_cdev_fops,它是一个struct file_operations型的数据变量,不是指针。
cdev_init()负责对CDEV描述块进行初始化,并注册到内核中。
填充CDEV描述块,
cdev.owner,cdev.ops。
填充PMOD描述块,
spi。
生成DEV_ID。
MAJOR()从dev_id中抽取MAJOR代码。
MKDEV()负责将major代码和minor代码组合成dev_id。
注册CDEV。
cdev_add()负责将CDEV描述块和dev_id关联起来,并注册到内核中。返回状态值。
判断返回的状态,
如果非法,则直接返回return.
如果合法,则继续执行。
在系统中生成device_node。
device_create()负责将DEV_ID所指向的设备,和一个CLASS描述块关联起来,注册到内核中。返回一个struct device型的指针,指向一个DEVICE描述块。
将返回的指针赋值给device,这是一个struct device型的指针,指向DEVICE描述块,后续用device作为句柄。
判断device的指针合法性。
如果非法,则设置status,后面返回时需要使用。然后打印错误信息,然后从系统中注销CDEV。
如果合法,则继续执行。
至此,全部完成。直接return status。
再来编写gpio_pmodoled_init_gpio()
static int gpio_pmodoled_init_gpio(struct gpio_pmodoled_device *dev)
{struct gpio gpio_pmodoled_ctrl[] = {{dev->iVBAT, GPIOF_OUT_INIT_HIGH, "OLED VBat"},{dev->iVDD, GPIOF_OUT_INIT_HIGH, "OLED VDD"},{dev->iRES, GPIOF_OUT_INIT_HIGH, "OLED_RESET"},{dev->iDC, GPIOF_OUT_INIT_HIGH, "OLED_D/C"},};int status;int i;for (i = 0; i < ARRAY_SIZE(gpio_pmodoled_ctrl); i++) {status = gpio_is_valid(gpio_pmodoled_ctrl[i].gpio);if(!status) {dev_err(&dev->spi->dev, "!! gpio_is_valid for GPIO %d, %s FAILED!, status: %d\n",gpio_pmodoled_ctrl[i].gpio, gpio_pmodoled_ctrl[i].label, status);goto gpio_invalid;}}status = gpio_request_array(gpio_pmodoled_ctrl, ARRAY_SIZE(gpio_pmodoled_ctrl));if(status) {dev_err(&dev->spi->dev, "!! gpio_request_array FAILED!\n");dev_err(&dev->spi->dev, " status is: %d\n", status);gpio_free_array(gpio_pmodoled_ctrl, 4);goto gpio_invalid;}gpio_invalid:return status;
}
caller调用该函数时,会传递一些参数。
dev是一个struct gpio_pmodoled_device型的指针,指向PMOD设备描述块。
首先定义一个临时数组gpio_pmodoled_ctrl,它是一个struct gpio型变量的数组。
判断数组中的GPIO描述块,所定义的GPIO的合法性。
采用循环结构,逐个进行检查。
gpio_is_valid()负责检查GPIO描述块中给出的GPIO编号,是否有效。返回状态值。
判断返回的状态是否合法。
如果非法,打印错误信息。跳出循环体,并进入退出处理栈gpio_invalid。
如果合法,则继续执行。
循环结束后,向内核注册GPIO。
gpio_request_array()负责向内核中注册GPIO,将GPIO_ARRAY中的GPIO描述块,逐个注册到系统中。返回状态值。
判断返回的状态值,
如果非法,打印错误信息,并进入退出处理栈gpio_invalid。
如果合法,则继续执行。
至此,全部完成。直接return status。
再来编写gpio_pmodoled_disp_init()
static void gpio_pmodoled_disp_init(struct gpio_pmodoled_device *dev)
{int status;uint8_t wr_buf[20];// We are going to be sending commands// so clear the data/cmd bitgpio_set_value(dev->iDC, OLED_CONTROLLER_CMD);// Start by turning VDD on and wait for the power to come upgpio_set_value(dev->iVDD, 0);msleep(1);// Display off Commandwr_buf[0] = OLED_DISPLAY_OFF;status = spi_write(dev->spi, wr_buf, 1);/* Bring Reset Low and then High */gpio_set_value(dev->iRES, 1);msleep(1);gpio_set_value(dev->iRES, 0);msleep(1);gpio_set_value(dev->iRES, 1);// Send the set charge pump and set precharge period commandswr_buf[0] = 0x8D;wr_buf[1] = 0x14;wr_buf[2] = OLED_SET_PRECHARGE_PERIOD;wr_buf[3] = 0xF1;status = spi_write(dev->spi, wr_buf, 4);/* Turn on VCC and wait 100ms */gpio_set_value(dev->iVBAT, 0);msleep(100);/* Set Display COntrast */wr_buf[0] = OLED_CONTRAST_CTRL;wr_buf[1] = 0x0F;/* Invert the display */wr_buf[2] = OLED_SET_SEGMENT_REMAP; // Remap Columnswr_buf[3] = OLED_SET_COM_DIR; // Remap Rows// Select sequential COM configurationwr_buf[4] = OLED_SET_COM_PINS;wr_buf[5] = 0x00;wr_buf[6] = 0xC0;wr_buf[7] = 0x20;wr_buf[8] = 0x00;// Turn on Displaywr_buf[9] = OLED_DISPLAY_ON;status = spi_write(dev->spi, wr_buf, 10);
}
caller调用该函数时,会传递一些参数。
dev是一个struct gpio_pmodoled_device型的指针,它指向一个PMOD描述块。
首先设置GPIO,把数据刷到硬件上。
gpio_set_value()负责把数据设置到指定的GPIO编号的硬件引脚上。
msleep(),睡眠等待。
然后配置wr_buf,利用SPIDEVICE,生成SPI写时序,把数据刷到硬件上。
spi_write()负责将wr_buf中的数据,逐个生成SPI写时序,刷到硬件上。
设置GPIO,生成波形时序。
gpio_set_value()和msleep()配合,可以生成需要的波形。
然后配置wr_buf,利用SPIDEVICE,生成SPI写时序,把数据刷到硬件上。
spi_write()负责将wr_buf中的数据,逐个生成SPI写时序,刷到硬件上。
设置GPIO,生成波形时序。
gpio_set_value()和msleep()配合,可以生成需要的波形。
然后配置wr_buf,利用SPIDEVICE,生成SPI写时序,把数据刷到硬件上。
spi_write()负责将wr_buf中的数据,逐个生成SPI写时序,刷到硬件上。
添加内核驱动模块(4)(mydriver.c+ Konfig+Makefile )相关推荐
- linux内核驱动模块开发makefile实例解析
昨天整理了一篇关于linux内核驱动模块的开发介绍入门,其中介绍了一些关于驱动模块的基本开发步骤,不过面广而不深,很多细节都没有涉及到,其中就包括如何编写驱动模块的makefile.那么,今天我们就来 ...
- 《Linux内核驱动模块编程指南》
Foreword Table of Contents 作者声明 版本和注意 感谢 译者注 作者声明 <Linux内核驱动模块编程指南>最初是由Ori Pomerantz为2.2版本的内核编 ...
- 超详细!手把手演示编译OpenWrt内核驱动模块
一.前言 构建自己的内核驱动模块,相关知识可以参考OpenWrt软件编译构建系统文章:https://dongshao.blog.csdn.net/article/details/102545618. ...
- xmake v2.6.2 发布,新增 Linux 内核驱动模块构建支持
Xmake 是一个基于 Lua 的轻量级跨平台构建工具. 它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时. 它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLis ...
- linux内核驱动模块开发步骤及实例入门介绍
最近在搞一个linux的项目,其中主要是在编写一些应用模块,对内核及其驱动模块涉及很少,遇到了一些驱动模块的问题时,临时查了些资料,大致了解了一下驱动模块开发的基本步骤和常规步骤,并从网上也收集到了一 ...
- 【学习笔记】编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译
本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录,就可以发现源码目录及其子目录中有很多的KConfig文件和Makefile文件.这些文件什么作用呢?正是这些文件组成了Linux ...
- 编译arm linux内核,编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译...
转载自:http://blog.csdn.net/newthinker_wei/article/details/8022696 本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录, ...
- 无法添加内核模式驱动的打印机
症状描述 添加共享打印机时出现错误提示:"当前打印机驱动程序与计算机上启用的某个策略不兼容",导致无法添加成功. 原因分析 因为内核模式可以访问系统底层的内存,因此写得不好的内核模 ...
- Arm开发板内核驱动模块--Helloworld及Makefile
1. 在/home/sxy/目录下新建hello_1文件夹,在此文件夹下编写hello.c和Makefile文件(假设已经编写好了),make生成模块文件: PS:hello.ko就是用于需要使用的驱 ...
- Linux内核驱动模块示例--Helloword及Makefile
先看一个最简单的驱动程序: //hello.c #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE ...
最新文章
- kill()函数 详解
- Android onTouchEvent, onClick及onLongClick的调用机制
- MULE ESB学习笔记
- POJ3265 Problem Solving ——动态规划——Pku3265
- java格式化输出双精度小数,用Java格式化双精度类型
- 可以无限量服用的药材
- SAP CRM IBASE头部字段valid from和valid to的填充逻辑
- WCF三种通信模式(转)
- 使用鸢尾花数据集实现一元逻辑回归、多分类问题
- 【正式发布】火星人敏捷开发手册2012-12-25(基于Scrum的敏捷开发免费培训教材及公司内部宣传材料)...
- 用HTML,CSS和JavaScript创建iPhone/iPad应用程序
- 【Flink】Flink Kafka 消费卡死 消费组卡死 topic无写入 实际有数据 topic正常
- UOJ#450. 【集训队作业2018】复读机 排列组合 生成函数 单位根反演
- html 字符串 放到webbrowser,delphi 直接将html字符串读入WebBrowser中
- 设为主页代码及添加到收藏夹代码大全
- excel的poi和EasyExcel的基本读写
- Linux RT-PREEMPT的softirq机制
- 创业者妻子发声力挺老公:合伙创业七年未分股份被踢出局
- 精读《图解密码技术》--第一章 环游密码世界
- Traffic Shifting
热门文章
- un-app uni.navigateTo页面跳转做封装传参
- 大一高数下册笔记整理_高数下册学习笔记
- KBQA-Bert学习记录-CRF模型
- [培训-无线通信基础-6]:信道编码(分组码、卷积吗、Polar码、LDPC码、Turbo码)
- Win7连接共享打印机时,报 0x00000bcb 错误
- dism /online /get-packages与dism /image:e:\ /get-packages
- 云服务器带外管理-从IPMI到RedFish
- matlab 仿真短路故障设置,基于MATLAB的电力系统故障分析及仿真
- HTTP和HTTPS的工作原理及区别
- M6(面试)-01-牛客网Java面试题集锦