打印

Linux Kernel 字符驱动的深入浅出讲解

[复制链接]
143|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
车水马龙|  楼主 | 2018-9-21 13:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
注:在写Linux 内核驱动,并将这一过程发生的技术,和菜鸟们可能会碰的问题进行一次解释,希望对后来都有用,阅读此文需要一定Linux基础,或者不要浪费时间,好品德看完全文要顶一下,看完此文你应该肻定会开发自己的字符驱动,请注意注解部分。



我反着来讲整个过程,讲解是为让人更快明白,有些术语不是标准的哦,对于正在找这个朋友此文就是你的宝,如果你乐意你可以给我付费。



第一步:内核加入到你设备中,整个调用驱动过程代码以下



MyTest::MyTest(QWidget *parent, const char *name, WFlags f):led(parent, name, f)

{

/*   1.int m_fd 是句柄 WIN32的名词 类的私有变量。private:  int m_fd;

     2.:: 这是名字空间符

     3.open Linux打开设备的系统function

     4. "/dev/pwcs", 这句话的解释 /dev 表示linux系统驱动 可以指示 内核工程中 如图

              /pwcs   自定义的设备名称

     5. O_RDWR   系统标准备读写宏

  */     

    m_fd = ::open("/dev/pwcs", O_RDWR);       //..打开设备.



    if (m_fd < 0) {

       //..没有打开设备

    }

    /*1.connect 系统函数  

       2.0,1,这些指驱动自定义要控制设备的系列号 ,

       3.SIGNAL(什么事件)

       4.this 这是什么呀,自己查去

       5.SLOT(事件动作)

     */

    connect(0,  SIGNAL(clicked()), this, SLOT(checkBoxClicked()) );   //..

    connect(1,  SIGNAL(clicked()), this, SLOT(checkBoxClicked()) );

   //.......可以有很多

      

    checkBoxClicked();

}



MyTest::~MyTest()

{

    ::close(m_fd);  //..关闭设备

}



void MyTest::checkBoxClicked()

{

    /* ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的参数个数如下:int ioctl(int fd, int cmd, …);其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支持,用户就能在用户程序中使用ioctl函数控制设备的I/O通道。

    */

    ::ioctl(m_fd, int(m_pwc1->isChecked()), 0);

    ::ioctl(m_fd, int(m_pwc2->isChecked()), 1);

    ::ioctl(m_fd, int(m_pwc3->isChecked()), 2);

    ::ioctl(m_fd, int(m_pwc4->isChecked()), 3);

}

}



第二步:增加内核驱动控制代码



#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/miscdevice.h>

#include <linux/fs.h>

#include <linux/types.h>

#include <linux/moduleparam.h>

#include <linux/slab.h>

#include <linux/ioctl.h>

#include <linux/cdev.h>

#include <linux/delay.h>#include <mach/gpio.h>

#include <mach/regs-gpio.h>

#include <plat/gpio-cfg.h>



#define DEVICE_NAME "pwcs"  //..注意这里  m_fd = ::open("/dev/pwcs", O_RDWR);  打开设备.

/*



    connect(0,  SIGNAL(clicked()), this, SLOT(checkBoxClicked()) );   //..

    connect(1,  SIGNAL(clicked()), this, SLOT(checkBoxClicked()) );   



   S3C64XX_GPK(XX) 来自这里  \u-kernel\arch\arm\mach-s3c64xx\include\mach\gpio.h

*/

static int power_gpios[] = {

      S3C64XX_GPK(0),    //..设备中要控制CPU的GPIO                    :0  connect自定义要控制设备的系列号

      S3C64XX_GPF(14),   //..                                                          :1   connect自定义要控制设备的系列号

      S3C64XX_GPA(2),    //..                                                           :2   connect自定义要控制设备的系列号

      S3C64XX_GPF(13),   //..                                                          :3   connect自定义要控制设备的系列号

      S3C64XX_GPK(1),    //..                                                           :4  connect自定义要控制设备的系列号

};



#define PW_GPIO_NUM  ARRAY_SIZE(power_gpios)     //..数组大小



static long power_gpio_ctrls_ioctl(struct file *filp, unsigned int cmd,

  unsigned long arg)

{

   switch(cmd) {

             case 0:

             case 1:

                         if (arg > PW_GPIO_NUM) {

                         return -EINVAL;

                        }

                        gpio_set_value(power_gpios[arg], cmd);  //..\u-kernel\arch\arm\mach-s3c64xx\include\mach\gpio.h

                        //printk(DEVICE_NAME": %d %d\n", arg, cmd);

                        break;

            default:

            return -EINVAL;

                      }

            return 0;

}

static struct file_operations power_gpio_ctrl_dev_fops = {

.owner   = THIS_MODULE,

.unlocked_ioctl = power_gpio_ctrls_ioctl,

};

static struct miscdevice power_gpio_ctrl_dev = {

.minor  = MISC_DYNAMIC_MINOR,

.name   = DEVICE_NAME, //..注意这里

.fops   = &power_gpio_ctrl_dev_fops,

};

static int __init power_gpio_ctrl_dev_init(void) {

int ret;

int i;

for (i = 0; i < PW_GPIO_NUM; i++) {

  ret = gpio_request(power_gpios<i>, "PWC"); //..用户控制符 “PWC”注册时用

  if (ret) {

   printk("%s: request GPIO %d for ctrl failed, ret = %d\n", DEVICE_NAME,

     power_gpios<i>, ret);

   return ret;

  }

  s3c_gpio_cfgpin(power_gpios<i>, S3C_GPIO_OUTPUT);

  gpio_set_value(power_gpios<i>, 0);

}

ret = misc_register(&power_gpio_ctrl_dev); //..注册

printk(DEVICE_NAME"\tinitialized\n");

return ret;

}

static void __exit power_gpio_ctrl_dev_exit(void) {

int i;

for (i = 0; i < PW_GPIO_NUM; i++) {

  gpio_free(power_gpios<i>);

}

misc_deregister(&power_gpio_ctrl_dev); //..干掉它

}

module_init(power_gpio_ctrl_dev_init);             //..模块初始化  给open用的

module_exit(power_gpio_ctrl_dev_exit);            //..模块初退出 给close用的

MODULE_LICENSE("GPL");                           //..模块许可授权 注意这个是必须的或者注册不了,其它方式我不说了,

                                                                    //..给钱告 诉你。

MODULE_AUTHOR("IFOCOOL@QQ.COM");   //..作者信息



第三步:使用make menuconfig 可以看到.



    source "drivers/char/Kconfig"  在内核配置菜单Kconfig文件中增加以下内容注意以下两个图



在drivers/char/Kconfig的文件 menu "Character devices" 菜单下增加以下内容



config POWER_CTRL                  #配置关键字

  tristate  "GPIO Power On/Off Ctrl"       #这段菜单名称

  depends on  CPU_S3C6410              #depends on CPU_S3C6410     表示还要这个菜单项前“CPU_S3C6410”看我写的LINUX

                                                         #专题里面有讲到。

  default y                                           #默为y





在drivers/char/Makefile的文件 增加以下内容



obj-$(CONFIG_POWER_CTRL)   += power_gpio_ctrl.o   #C文件编绎后目标文件 power_gpio_ctrl.c的目标文件

                                                                                  #CONFIG_POWER_CTRL=CONFIG+POWER_CTRL(注:这来自

                                                                                   Kconfig)



第四步:必须完成第三步后执行这一步

  linux 命令台中执行: make menuconfig

进行字符驱动选择菜单如图







最后一步make内核

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

474

主题

476

帖子

0

粉丝