之前在注册SPIDRIVER时,我们注册了SPI的probe函数和remove函数。
我们来编写这两个函数。

static int gpio_pmodoled_spi_probe(struct spi_device *spi) {int status = 0;struct gpio_pmodoled_device *gpio_pmodoled_dev;/* We rely on full duplex transfers, mostly to reduce* per transfer overheads (by making few transfers).*/if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {status = -EINVAL;dev_err(&spi->dev, "SPI settings incorrect: %d\n", status);goto spi_err;}/* We must use SPI_MODE_0 */spi->mode = SPI_MODE_0;spi->bits_per_word = 8;status = spi_setup(spi);if(status < 0) {dev_err(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",spi->mode, spi->max_speed_hz / 1000,status);goto spi_err;}/* Get gpio_pmodoled_device structure */gpio_pmodoled_dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;if(gpio_pmodoled_dev == NULL) {dev_err(&spi->dev, "Cannot get gpio_pmodoled_device.\n");status = -EINVAL;goto spi_platform_data_err;}printk(KERN_INFO SPI_DRIVER_NAME " [%s] SPI Probing\n", gpio_pmodoled_dev->name);#ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_probe: setup char device\n", gpio_pmodoled_dev->name);
#endif/* Setup char driver */status = gpio_pmodoled_setup_cdev(gpio_pmodoled_dev, &(gpio_pmodoled_dev->dev_id), spi);   if (status) {dev_err(&spi->dev, "spi_probe: Error adding %s device: %d\n", SPI_DRIVER_NAME, status);goto cdev_add_err;}/* Initialize Mutex */mutex_init(&gpio_pmodoled_dev->mutex);/*** It is important to the OLED's longevity that the lines that * control it's power are carefully controlled. This is a good* time to ensure that the device is ot turned on until it is* instructed to do so.*/
#ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_probe: initialize device\n", gpio_pmodoled_dev->name);
#endifstatus = gpio_pmodoled_init_gpio(gpio_pmodoled_dev);if(status) {dev_err(&spi->dev, "spi_probe: Error initializing GPIO\n");goto oled_init_error;}gpio_pmodoled_disp_init(gpio_pmodoled_dev);memset(gpio_pmodoled_dev->disp_buf, 0x00, DISPLAY_BUF_SZ);status = screen_buf_to_display(gpio_pmodoled_dev->disp_buf, gpio_pmodoled_dev);if(status) {dev_err(&spi->dev, "spi_probe: Error sending initial Display String\n");goto oled_init_error;}return status;oled_init_error:if (&gpio_pmodoled_dev->cdev)cdev_del(&gpio_pmodoled_dev->cdev);
cdev_add_err:
spi_platform_data_err:
spi_err:return(status);
}

当syscall调用该函数时,传递一个参数,它是一个struct spi_device型的指针spi。
定义一系列指针,后续作为句柄使用。

判断SPIMASTER的模式。
spi->master->flags & SPI_MASTER_HALF_DUPLEX
如果是SPI_MASTER_HALF_DUPLEX模式,那么不能使用,打印错误信息,然后进入退出处理栈spi_err。
如果不是SPI_MASTER_HALF_DUPLEX模式,那么可以使用,继续执行。

填充SPIDEVICE描述块的成员。
mode,bits_per_word。
设置SPIDEVICE的配置信息。
spi_setup()负责对一个SPIDEVICE描述块进行设置,并返回状态值。

判断返回的状态值。
如果非法,则打印错误信息,并进入退出处理栈spi_err
如果合法,则继续执行。

利用SPIDEVICE扎到对应的PMOD设备描述块。
将spi->dev.platform_data赋值给gpio_pmodoled_dev,它是一个struct gpio_pmodoled_device型的指针。关联到PMOD的设备资源后,后续可以用gpio_pmodoled_dev作为句柄。

判断gpio_pmodoled_dev指针的合法性。
如果为NULL,则非法。打印错误信息,然后修改status,后面返回值需要使用status。然后进入退出处理栈spi_platform_data_err。
如果不为NULL,则合法。那么继续执行。

配置CDEV设备资源。
gpio_pmodoled_setup_cdev()负责配置PMOD中内含的CDEV设备资源,并关联到SPIDEVICE上。返回状态值。

判断返回的状态值。
如果非法,则打印错误信息,并进入退出处理栈cdev_add_err。
如果合法,则继续执行。

初始化PMOD设备资源中的mutex系统资源。
mutex_init()负责初始化一个mutex描述块。

初始化PMOD设备资源中用到的GPIO系统资源。
gpio_pmodoled_init_gpio()负责初始化PMOD设备描述块中使用的GPIO系统资源。返回状态值。
判断返回的状态值的合法性。
如果非法,则打印错误信息,并进入退出处理栈oled_init_error。
如果合法,则继续执行。

初始化PMOD设备资源中用到的显示缓冲资源。
gpio_pmodoled_disp_init()负责初始化PMOD设备描述块中使用的显示资源。

初始化缓冲区。
memset()负责将缓冲区清零。

把初始化好的显示缓冲区刷到硬件上。
screen_buf_to_display()负责把缓冲区flush到硬件上。返回状态值。
判断返回的状态值的合法性。
如果非法,则打印错误信息,并进入退出处理栈oled_init_error。
如果合法,则继续执行。

至此,全部执行完。正常退出,不需要进入退出处理栈。所以直接return。

再编写remove函数。

static int __devexit gpio_pmodoled_spi_remove(struct spi_device *spi)
{int status;struct gpio_pmodoled_device *dev;uint8_t wr_buf[10];dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;if(dev == NULL) {dev_err(&spi->dev, "spi_remove: Error fetch gpio_pmodoled_device struct\n");return -EINVAL; }#ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Clearing Display\n", dev->name);
#endif  /* Clear Display */memset(dev->disp_buf, 0, DISPLAY_BUF_SZ);status = screen_buf_to_display(dev->disp_buf, dev);/* Turn off display */wr_buf[0] = OLED_DISPLAY_OFF;status = spi_write(spi, wr_buf, 1);if(status) {dev_err(&spi->dev, "oled_spi_remove: Error writing to SPI device\n");}/* Turn off VCC (VBAT) */gpio_set_value(dev->iVBAT, 1);msleep(100);/* TUrn off VDD Power */gpio_set_value(dev->iVDD, 1);
#ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Free GPIOs\n", dev->name);
#endif  {   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"},};gpio_free_array(gpio_pmodoled_ctrl, 4);
}if(&dev->cdev) {
#ifdef CONFIG_PMODS_DEBUGprintk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Destroy Char Device\n", dev->name);
#endif  device_destroy(gpio_pmodoled_class, dev->dev_id);cdev_del(&dev->cdev);}cur_minor--;printk(KERN_INFO SPI_DRIVER_NAME " [%s] spi_remove: Device Removed\n", dev->name);return status;
}

当syscall调用remove函数时,会传递一个参数,它是一个struct spi_device型的指针。
首先定义一系列指针,后续作为句柄使用。

从SPIDEVICE设备描述块中,找到PMOD的描述块。
dev = (struct gpio_pmodoled_device*) spi->dev.platform_data;
将 dev.platform_data赋值给dev,它是一个struct gpio_pmodoled_device型的指针。我们用dev指向了PMOD的描述块,后续用dev作为句柄。

判断dev的指针合法性。
如果为NULL,则非法。打印错误信息,并直接return.
如果不为NULL,则合法。继续执行。

初始化显示缓冲区,memset()完成。
将显示缓冲区的内容flush到硬件上。
screen_buf_to_display()负责将内容flush到硬件上。

配置wr_buf,然后用SPI发送缓冲区字符。
spi_write()负责写操作,它利用一个SPIDEVICE设备资源实现写操作时序。返回状态值。
判断返回的状态值。
如果非法,则打印错误信息。由于不是致命错误。所以打印错误信息后,并不进入退出处理栈。
如果合法,则继续执行。

设置GPIO。
gpio_set_value()负责将一个在系统中注册的合法GPIO编号,设置为配置值。
msleep(),睡眠等待。

定义一个临时数组gpio_pmodoled_ctrl,它是一个struct gpio型的数组。
同是设置多个GPIO的状态。
gpio_free_array()负责将一个GPIO_ARRAY中GPIO系统资源,同时释放掉。

判断PMOD的CDEV的指针合法性。
如果不为NULL,说明CDEV仍占有内核资源,那么释放掉CDEV的系统资源。
device_destroy()负责注销CLASS描述块。它根据dev_id注销CDEV关联的CLASS描述块。
cdev_del()负责注销CDEV设备资源。它根据传入的CDEV的描述块,在设备中进行匹配查找,然后注销掉系统资源,并取消内核和CDEV描述块之间的关联。

我们定义了静态变量cur_minor。它用来记录当前系统中存在的CDEV设备数量。
修改cur_minor。
cur_minor–;

至此,全部完成。直接return status.

添加内核驱动模块(3)(mydriver.c+ Konfig+Makefile )相关推荐

  1. linux内核驱动模块开发makefile实例解析

    昨天整理了一篇关于linux内核驱动模块的开发介绍入门,其中介绍了一些关于驱动模块的基本开发步骤,不过面广而不深,很多细节都没有涉及到,其中就包括如何编写驱动模块的makefile.那么,今天我们就来 ...

  2. 《Linux内核驱动模块编程指南》

    Foreword Table of Contents 作者声明 版本和注意 感谢 译者注 作者声明 <Linux内核驱动模块编程指南>最初是由Ori Pomerantz为2.2版本的内核编 ...

  3. 超详细!手把手演示编译OpenWrt内核驱动模块

    一.前言 构建自己的内核驱动模块,相关知识可以参考OpenWrt软件编译构建系统文章:https://dongshao.blog.csdn.net/article/details/102545618. ...

  4. xmake v2.6.2 发布,新增 Linux 内核驱动模块构建支持

    Xmake 是一个基于 Lua 的轻量级跨平台构建工具. 它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时. 它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLis ...

  5. linux内核驱动模块开发步骤及实例入门介绍

    最近在搞一个linux的项目,其中主要是在编写一些应用模块,对内核及其驱动模块涉及很少,遇到了一些驱动模块的问题时,临时查了些资料,大致了解了一下驱动模块开发的基本步骤和常规步骤,并从网上也收集到了一 ...

  6. 【学习笔记】编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译

    本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录,就可以发现源码目录及其子目录中有很多的KConfig文件和Makefile文件.这些文件什么作用呢?正是这些文件组成了Linux ...

  7. 编译arm linux内核,编译Linux内核(下)---KConfig、Makefile详解以及ARM平台Linux内核的编译...

    转载自:http://blog.csdn.net/newthinker_wei/article/details/8022696 本文主要介绍Linxu2.6的内核配置系统. 如果你浏览一下源代码目录, ...

  8. 无法添加内核模式驱动的打印机

    症状描述 添加共享打印机时出现错误提示:"当前打印机驱动程序与计算机上启用的某个策略不兼容",导致无法添加成功. 原因分析 因为内核模式可以访问系统底层的内存,因此写得不好的内核模 ...

  9. Arm开发板内核驱动模块--Helloworld及Makefile

    1. 在/home/sxy/目录下新建hello_1文件夹,在此文件夹下编写hello.c和Makefile文件(假设已经编写好了),make生成模块文件: PS:hello.ko就是用于需要使用的驱 ...

  10. Linux内核驱动模块示例--Helloword及Makefile

    先看一个最简单的驱动程序: //hello.c #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE ...

最新文章

  1. 正则表达式的常用方法和属性
  2. 大型运输行业实战_day14_1_webserivce简单入门
  3. MongodDB数据库安装和简单使用
  4. id、构造方法、Category 分类、类的本质、description方法、SEL
  5. 1047. 删除字符串中的所有相邻重复项
  6. 良好的JavaScript编码风格(语法规则)
  7. Qt文档阅读笔记-QWindow的进一步认识
  8. PHP extension mcrypt must be loaded.
  9. 【转】QTableView 小结
  10. MultipartFile转为File
  11. 【ZBrush笔刷】实用笔刷和Alpah下载,及笔刷使用方法
  12. vue+elementui+自定义Vue-Quill-Editor富文本框(一)
  13. [转]互联网企业安全建设(一)
  14. opencv中对图片阀值的操作
  15. 概率论与数理统计学习笔记(1)——t检验与P值
  16. 微信公众号群发图文API插入视频音频解决方案
  17. 用打印服务器打印 打印机显示脱机,打印机提示脱机使用,无法打印,该怎么解决?...
  18. Array 属性和方法
  19. 跨公司销售利润中心替代
  20. linux+手机+gps,Linux环境下利用GPS+蓝牙实现移动定位

热门文章

  1. 华为手机邮箱发件服务器端口设置,华为手机设置-华为手机POP3设置
  2. 用于微信小程序的图文编辑器
  3. 恩智浦智能车(CNYouth)
  4. Unity 动态切换天空盒\反射天空盒材质
  5. Linux C 语言内联汇编
  6. 软考高项--项目管理概述
  7. 史诗级中日韩新字体诞生:思源黑体(Source Han Sans)
  8. oracle三大索引类型
  9. 拉扎维对于简单CMOS电路的增益计算方法
  10. simulink 状态空间加反馈报错