2013-01-21 22:32:01 lingzhi007 阅读数 39
  • linux设备驱动模型-linux驱动开发第5部分

    本课程是linux驱动开发的第5个课程,主要内容是linux的设备驱动模型,包括总线、类、设备、驱动等概念,重点通过platform平台总线的工作来演示设备驱动模型的工作方法,实践环节对上个课程的LED驱动进行平台总线式改造,终目标是让大家彻底掌握linux的总线式设备驱动模型。

    4829 人正在学习 去看看 朱有鹏

vmware linux 自动安装  分区自动处理不能满足需要

 

手动模式

1、i will install the oprationg system later 

2、 建好虚拟机后 再转入光盘 重启 。

 

我机器 用的 ext4 的格式   rl5 只有 Ide 的驱动  要把系统自己建的硬盘删掉,手工增加ide 的硬盘。

2015-06-18 14:23:22 weixin_33895657 阅读数 26
  • linux设备驱动模型-linux驱动开发第5部分

    本课程是linux驱动开发的第5个课程,主要内容是linux的设备驱动模型,包括总线、类、设备、驱动等概念,重点通过platform平台总线的工作来演示设备驱动模型的工作方法,实践环节对上个课程的LED驱动进行平台总线式改造,终目标是让大家彻底掌握linux的总线式设备驱动模型。

    4829 人正在学习 去看看 朱有鹏

OSSIM安装与驱动问题

 

大家在部署OSSIM系统常遇到的就是驱动安装的问题,或是网卡没驱动或是硬盘没驱动(服务器上部署ESXi同样会遇到无法识别硬件设备的问题),其实在Linux手动安装驱动是一项必须掌握技能。在《Unix/Linux网络日志分析与流量监控分析》讲过OSSIM平台是一套基于Debian Linux的系统,但是IBM,HP,DELL等厂家的多数服务器对Debian Linux系统支持的并不太好,所以磁盘和网卡的驱动通常都是安装完系统之后再手动加载模块。

 

在安装之前我怎么知道目前这款机器是否能够安装上Debian呢? 有个办法,不用拆机箱,查看机器硬件的芯片,http://kmuto.jp/debian/hcl  大家访问这个网址就能获取到硬件驱动信息。

wKiom1WCZAmS-ZDSAAWQ2ADOzc8204.jpgwKioL1WCZn7QbywWAAaWYvD37FY157.jpgwKiom1WCY4bTReWnAAQVndcRwOg375.jpg

下面,接着需要到Debian官网wiki继续查询,地址为https://wiki.debian.org/DeviceDatabase/PCI

验证你所选择的PCI网卡,Debian是否支持,但不代表内核默认支持,如果你不会打网卡模块,最后还要到查看内核模块/lib/modules/2.6.32-5-amd64/kernel/drivers/net/下查看,是否默认支持。

 

点击进入Debian软件查询


已测试通过的网卡:

  • Intel Corporation 82575EB Gigabit Network connection


  • Intel Corporation 82579V Gigabit Network Connection


  • Intel Corporation 82557/8/9/0/1 Ethernet Pro 100


  • RTL81088 PCI Express Gigabit Ethernet connection


  • Realtek PCIe GBE Family Controller


IBM 3650服务器上安装OSSIM实例

 

很多人在3650上安装过Windows,下面我们先回忆一下,首先用到Serverguide光盘,启动后按提示逐步进行。安装硬盘的活儿就自己仔细操作啦。提示按<CTRL><A>,一会儿进入Raid设置界面。设置完毕退出。

记住,在这类机器上安装OSSIM不要用 ServerGuide盘,要用自带的WebBios,当你做好Raid0,而不是Raid5,之后准备好驱动(将firmware-bnx2_0.33_all.deb,复制到U盘),装好后,挂载U盘,安装这个驱动文件即可。

下次给大家分享在HP GL380系列服务器和DELL PowerEdge上安装OSSIM。

2018-08-20 21:05:58 wf19930209 阅读数 98238
  • linux设备驱动模型-linux驱动开发第5部分

    本课程是linux驱动开发的第5个课程,主要内容是linux的设备驱动模型,包括总线、类、设备、驱动等概念,重点通过platform平台总线的工作来演示设备驱动模型的工作方法,实践环节对上个课程的LED驱动进行平台总线式改造,终目标是让大家彻底掌握linux的总线式设备驱动模型。

    4829 人正在学习 去看看 朱有鹏

Linux安装NVIDIA显卡驱动的正确姿势

可能想玩Linux系统的童鞋,往往死在安装NVIDIA显卡驱动上,所以这篇文章帮助大家以正常的方式安装NVIDIA驱动。

本文将介绍四种NVIDIA驱动安装方式。具体选择需要根据你的情况而定。

  • 使用标准Ubuntu仓库进行自动化安装
  • 使用PPA仓库进行自动化安装
  • 使用官方的NVIDIA驱动进行手动安装

什么是nouveau驱动?

nouveau,是一个自由及开放源代码显卡驱动程序,是为Nvidia的显示卡所编写,也可用于属于系统芯片的NVIDIA Tegra系列,此驱动程序是由一群独立的软件工程师所编写,Nvidia的员工也提供了少许帮助。

该项目的目标为利用逆向工程Nvidia的专有Linux驱动程序来创造一个开放源代码的驱动程序。

所以nouveau开源驱动基本上是不能正常使用的,性能极低,所以网上有很多人都在骂:干死黄仁勋!!

这里写图片描述

想了解历史的可以去看看这篇知乎,腾讯和AMD是linux的罪人吗?

好了不扯了,正式开始讲安装把!

检测NVIDIA驱动是否成功安装

  1. 使用nvidia-setting命令
nvidia-setting

终端执行这个命令会调出NVIDIA的驱动管理程序,如下:

这里写图片描述

如果出现这个界面可以看到 NVIDIA Driver Version:390.48,这就代表nvidia-setting安装正常。

  1. 使用nvidia-smi命令测试

英伟达系统管理接口(NVIDIA System Management Interface, 简称 nvidia-smi)是基于NVIDIA Management Library (NVML) 的命令行管理组件,旨在(intened to )帮助管理和监控NVIDIA GPU设备。

nvidia-smi

执行这条命令将会打印出当前系统安装的NVIDIA驱动信息,如下:

这里写图片描述

我们可以看到我们显卡的型号,我的是GTX 960M,包括显存大小都可以看见。

  1. 系统信息查看

这一步不重要,因为有时候系统信息里面显示的可能会有误,只显示集显不显示独显的情况。

比如我的就没有显示出独显,如下:

这里写图片描述

这里面不显示没有关系,可以略过。

  1. 命令行搜索集显和独显

打开终端执行以下命令:

lspci | grep VGA     # 查看集成显卡
lspci | grep NVIDIA  # 查看NVIDIA显卡

这里写图片描述

如果都能搜索到说明正常。

查看nouveau是否启动运行可以执行下面命令:

lsmod | grep nouveau

没有返回代表没有运行。

集显与独显的切换

当我们需要切换独显与集显的时候,一般就是外出的时候,想节省电量,增长待机时间。下面讲解两种切换方式。

  1. 使用nvidia-setting切换

终端执行nvidia-setting,在弹的界面中选择独显与集显:

这里写图片描述

  1. 命令行切换

NVIDIA提供了一个切换显卡的命令:

sudo prime-select nvidia # 切换nvidia显卡
sudo prime-select intel  # 切换intel显卡
sudo prime-select query  # 查看当前使用的显卡

这里写图片描述

注意: 每一次切换显卡都需要重新启动电脑才能生效

使用标准仓库进行自动化安装

在安装的发行版中,如 ubuntu, Linux Mint等,找到附加驱动管理软件,下面是Linux Mint界面:

这里写图片描述

选择推荐的驱动安装,点击应用更改,等待下载然后重启即可。

这种安装方式有如下缺点

  1. 如果你的显卡比较新可能会出现安装低版本的NVIDIA驱动而造成即可安装完成,但是并没有真正安装成功,可能会出现循环登录,关机死机等等原因。

  2. 当你更换驱动的时候可能原有的NVIDIA驱动删除不干净。

当然这种方式也是有优点的:

  1. 不需要手动禁止nouveau
  2. 操作方便

可能有的童鞋还使用过命令行的方式安装:

sudo apt-get install nvidia*

如图:

这里写图片描述

这种方式安装同样也是使用ubuntu官方源的形式安装的,你可以选择不同的驱动版本来安装,但是本质上和标准仓库进行自动化安装是一样的。

其实ubuntu自带命令行版本安装工具ubuntu-drivers,终端输入:

ubuntu-drivers devices   # 查询所有ubuntu推荐的驱动

这里写图片描述

这路我是有一个推荐安装的驱动,那就是nvidia-driver-390,明显我已经安装完成了。

然后就可以使用下面一条命令安装所有推荐的驱动程序:

sudo ubuntu-drivers autoinstall

安装完成后重启就可以了,这里要注意,这种安装方式和驱动管理器软件安装的效果是一样的,就是一个是UI版本,一个是命令行版本。

使用PPA仓库进行自动化安装

使用图形驱动程序PPA存储库允许我们安装NVIDIA beta驱动程序,这有可能会出现兼容性的问题,但是有些时候必须使用这种方式,比如显卡比较新,使用上面所讲的方式检测驱动的安装情况,如果不正常那么只能使用这种方式安装最新的NVIDIA驱动。

  1. 添加PPA到我们的系统:
sudo add-apt-repository ppa:graphics-drivers/ppa

更新系统源:

sudo apt update

此时我们就可以下载最新的NVIDIA驱动了:

安装的方式有以下三种,其实前面已经讲过,这里总结一下:

  • 附加驱动管理软件
  • sudo apt-get install nvidia-xxx
  • ubuntu-drivers方式

这三种都可以,选择一个版本安装,然后重启即可。

使用官方的NVIDIA驱动进行手动安装

这种安装方式我认为是比较野蛮的,也是最正规,最原始的的方式,当然难度最高的。你可以来挑战一下!!!!

1. 查看当前电脑的显卡型号

lshw -numeric -C display

执行完毕后我的显卡型号为 GTX 960M,如下图:

这里写图片描述

2. 下载NVIDIA官方驱动

到NVIDIA的官方驱动网站下载对应显卡的驱动程序,下载后的文件格式为run

下载好之后放到用户目录下,等下后面会用到。

3. 删除原有的NVIDIA驱动程序

如果你没有安装过,或者已经卸载,可以忽略:

sudo apt-get remove –purge nvidia*

4. bios禁用禁用secure boot,也就是设置为disable

如果没有禁用secure boot,会导致NVIDIA驱动安装失败,或者不正常。

5. 禁用nouveau

打开编辑配置文件:

sudo gedit /etc/modprobe.d/blacklist.conf

在最后一行添加:

blacklist nouveau

这一条的含义是禁用nouveau第三方驱动,之后也不需要改回来。

由于nouveau是构建在内核中的,所以要执行下面命令生效:

sudo update-initramfs -u

6. 重启

reboot

重启之后,可以查看nouveau有没有运行:

lsmod | grep nouveau  # 没输出代表禁用生效

7. 停止可视化桌面:

为了安装新的Nvidia驱动程序,我们需要停止当前的显示服务器。最简单的方法是使用telinit命令更改为运行级别3。执行以下linux命令后,显示服务器将停止,因此请确保在继续之前保存所有当前工作(如果有):

sudo telinit 3

之后会进入一个新的命令行会话,使用当前的用户名密码登录

8. 安装驱动

给驱动文件增加可执行权限:

sudo chmod a+x NVIDIA-Linux-x86_64-390.48.run

然后执行安装:

sudo sh ./NVIDIA-Linux-x86_64-390.48.run --no-opengl-files

安装完成后重启即可,记得验证是否安装成功,参考前面所讲。

–no-opengl-files 参数必须加否则会循环登录,也就是loop login

参数介绍:

  • –no-opengl-files 只安装驱动文件,不安装OpenGL文件。这个参数最重要
  • –no-x-check 安装驱动时不检查X服务
  • –no-nouveau-check 安装驱动时不检查nouveau
    后面两个参数可不加。

关于使用此方式可以参照Ubuntu 18.04安装NVIDIA(英伟达) RTX2080Ti显卡 这篇文章。

注意:

  • 安装CUDA时一定使用runfile文件,这样可以进行选择。不再选择安装驱动,以及在弹出xorg.conf时选择NO

到此NVIDIA的安装方式讲解完了。。。。

END

2010-12-17 01:03:00 zhenwenxian 阅读数 3653
  • linux设备驱动模型-linux驱动开发第5部分

    本课程是linux驱动开发的第5个课程,主要内容是linux的设备驱动模型,包括总线、类、设备、驱动等概念,重点通过platform平台总线的工作来演示设备驱动模型的工作方法,实践环节对上个课程的LED驱动进行平台总线式改造,终目标是让大家彻底掌握linux的总线式设备驱动模型。

    4829 人正在学习 去看看 朱有鹏

Linux驱动设备驱动模型


序言

从这一章开始,我们将详细的介绍Linux的设备驱动模型。Linux设备驱动模型是一个相当复杂的系统,对于初学者来说真有些无从入手。而且更加困难的是,随着新的Linux KernelreleaseLinux的设备驱动模型总会有或大或小的变化,我们将尽量展现 Linux Kernel 的这种变化。

早期的Linux内核(版本2.4之前)并没有实现一个统一的设备模型,设备节点的创建一般是mknod命令手动创建或利用devfs文件系统创建。早期的Linux发行版一般会采用手动创建的方式预先把通常用到的节点都创建出来,而嵌入式系统则会采用devfs的方式。起初Linux 2.6 内核还支持devfs,但从2.6.18开始,内核完全移除了devfs系统而采用的udev的方式动态的创建设备节点。因此,新的Linux发行版都采用udev的方式管理设备节点文件。(关于udev的详细信息,请参考:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html )。

Linux2.6
设备驱动模型的基本元素是ClassBusDeviceDriver,下面我们分别介绍各个部分。

Class
Class Device
驱动模型最基本的概念是设备及其类别,Linux中使用struct class struct class_device来管理不同类别的设备。由于设备驱动模型是一个复杂的系统,我们还是从一个简单的例子开始介绍,然后在逐步展开。其实实现设备节点的动态创建是一个很简单的事情,并不需要太多的代码。我们修改我们的驱动初始化函数如下:

#include <linux/device.h>
#define DEVNAME "hello"

static dev_t dev;

static struct class *hello_class;

static struct cdev *hello_cdev;
static int __init hello_init(void)
{
    int error;

    error = alloc_chrdev_region(&dev, 0, 2, "hello");
    if (error)
    {
        printk("hello: alloc_chardev_region failed!/n");
        goto out;
    }
    hello_cdev = cdev_alloc();
    if (hello_cdev == NULL)
    {
        printk("hello: alloc cdev failed!/n");
        error = -ENOMEM;
        goto out_chrdev;
    }
    hello_cdev->ops = &hello_fops;
    hello_cdev->owner = THIS_MODULE;
    error = cdev_add(hello_cdev, dev, 1);
    if (error)
    {
        printk("hello: cdev_add failed!/n");
        goto out_cdev;
    }
    hello_class = class_create(THIS_MODULE, DEVNAME);

    if (IS_ERR(hello_class))

    {

        error = PTR_ERR(hello_class);

        goto out_chrdev;

    }

    class_device_create(hello_class, NULL, dev, NULL, DEVNAME);

    memset (hello_buf, 0, sizeof(hello_buf));
    memcpy(hello_buf, DEFAULT_MSG, sizeof(DEFAULT_MSG));
    printk("hello: Hello World!/n");
    return 0;

out_cdev:
    cdev_del(hello_cdev);
out_chrdev:
    unregister_chrdev_region(hello_cdev->dev, 2);
out:
    return error;
}
static void __exit hello_exit(void)
{
    class_device_destroy(hello_class, dev);

    class_destroy(hello_class);

    unregister_chrdev_region(hello_cdev->dev, 2);
    cdev_del(hello_cdev);
    printk("hello: Goodbye World/n");
}


重新编译这个驱动程序,当加载这个驱动到内核中时,系统(一般是hotplugudev系统)就会自动的创建我们指定的设备名字:/dev/hello,同时,你也可以发现在sysfs系统中添加了新的文件:/sys/class/hello/hello/
当然并不需要把所有的代码都贴到这里,但是这样做可能更加清楚。我们把添加的代码显示成蓝色,这样你可以清楚的看到代码的简单程度。这里主要用到了class_create class_device_create函数,它们定义在<linux/device.h>头文件中。

extern struct class *class_create(struct module *owner, const char *name);
extern void class_destroy(struct class *cls);
extern struct class_device *class_device_create(struct class *cls,
                        struct class_device *parent,
                        dev_t devt,
                        struct device *device,
                        const char *fmt, ...)
                    __attribute__((format(printf,5,6)));
extern void class_device_destroy(struct class *cls, dev_t devt);


Linux
是通过设备与设备类来管理设备的,当你调用这些函数向系统注册设备及其类的时候,内核会自动的在sysfs文件系统中创建对应的文件。如果想了解更多的关于设备及其类的函数接口,请你阅读Linux kernel的源文件(include/device.h头文件和drivers/base/class.c实现文件)。这里简单说明 class_device_create函数,它的最后一个参数是一个变参,类似于printf函数参数,用于指定添加设备的名称也就是显示在/dev /目录下设备文件名称。

创建的class_device设备会自动注册到系统中,这样对于给定的设备,系统会自动找到匹配的设备驱动。

你可以在设备类或设备目录下(/sys/class)创建文件,这个文件提供了内核同用户空间程序的交互接口。内核提供了设备、设备类的内核函数接口,这些接口也定义在<include/device.h>头文件中。

int class_create_file(struct class *cls, const struct class_attribute *attr)
void class_remove_file(struct class *cls, const struct class_attribute *attr)
int class_device_create_file(struct class_device *class_dev,
                 const struct class_device_attribute *attr)
void class_device_remove_file(struct class_device *class_dev,
                  const struct class_device_attribute *attr)

int class_device_create_bin_file(struct class_device *class_dev,
                 struct bin_attribute *attr)
void class_device_remove_bin_file(struct class_device *class_dev,
                  struct bin_attribute *attr)


其实,这些函数仅仅是简单的封装了sysfs文件系统函数,但它为设备及设备类提供了统一的函数接口。

Drivers
当一个设备注册到系统中时,它就向系统表明了哪个驱动匹配这个设备。Linux内核会在注册的设备驱动中查找匹配的驱动并调用对应的探测函数(probe)来初始化设备。设备驱动接口也定义在<linux/device.h>头文件中。

struct device_driver {
    const char        *name;
    struct bus_type        *bus;
    struct module        *owner;
    const char         *mod_name;   
    int (*probe) (struct device *dev);
    int (*remove) (struct device *dev);
    void (*shutdown) (struct device *dev);
    int (*suspend) (struct device *dev, pm_message_t state);
    int (*resume) (struct device *dev);
    struct attribute_group **groups;
    struct driver_private *p;
};


其中,比较重要的数据是nameLinux内核就是根据name来匹配设备与驱动的。probe函数用于设备的探测及初始化,remove函数在设备移出系统时被触发调用。

一般来说,我们的驱动需要实现namemoduleproberemove函数。

Bus
通常我们的驱动并不需要实现Bus接口,也没有这个必要,因此你完全可以跳过这段,除非你想添加一个新的总线到系统中。

Linux2.6内核中,struct bus_type描述了一个bus对象,它定义在<linux/device.h>头文件中。(你发现没有,到目前Linux设备模型接口都定义在这个文件中)

struct bus_type {
    const char        *name;
    struct bus_attribute    *bus_attrs;
    struct device_attribute    *dev_attrs;
    struct driver_attribute    *drv_attrs;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*suspend_late)(struct device *dev, pm_message_t state);
    int (*resume_early)(struct device *dev);
    int (*resume)(struct device *dev);

    struct bus_type_private *p;
};

extern int __must_check bus_register(struct bus_type *bus);
extern void bus_unregister(struct bus_type *bus);

extern int __must_check bus_create_file(struct bus_type *,
                    struct bus_attribute *);
extern void bus_remove_file(struct bus_type *, struct bus_attribute *);


在这个结构中,name描述了bus的名字,如它会显示在/sys/bus/中。match函数用于匹配属于这个bus的设备和驱动,uevent用于处理Linux uevent事件。proberemove类似与driver的函数接口,主要用于设备的Hotplug处理。其他的函数是Power相关的函数接口。

同样,bus也需要注册到系统中,并可以在sysfs中创建文件。

sysfs
文件系统
sysfs
类似于proc文件系统,用于用户空间程序和内核空间交互数据的接口。但sysfs提供了更多的功能,其中之一就是显示Linux驱动程序模型的分层结构关系。Ubuntu 804sysfs文件系统的目录显示如下:

 

          block  bus  class  devices  firmware  fs  kernel  module  power  slab
当你浏览这个文件系统的时候,你会发现里面有很多链接文件,其实正是这些链接文件展现了Linux驱动模型各个组成部分之间的关系。

sysfs
文件系统中,最重要的就是struct attribute结构,它被用来管理内核sysfs文件的接口(名字,属性,读写函数等)。内核sysfs提供了基本的attribute接口,不同的设备如busdevice在基本attribute的基础上定义了自己的读写函数,sysfs提供了对应的宏来简化属性的操作。请参考<linux/sysfs.h>头文件中。

struct attribute {  
    const char        *name;
    struct module     *owner;
    mode_t            mode;
};

#define __ATTR(_name,_mode,_show,_store) { /
    .attr = {.name = __stringify(_name), .mode = _mode },    /
    .show    = _show,                    /
    .store    = _store,                    /
}

int __must_check sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
int __must_check sysfs_create_dir(struct kobject *kobj);


我们看到,sysfsstruct attribute结构本身并不包含读写访问函数,驱动模型的各个部分都会扩展这个结构并定义自己的属性结构来引入各自的操作函数,如 class:(这个结构定义在<linux/device.h>头文件中)。

struct class_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct class *, char * buf);
    ssize_t (*store)(struct class *, const char * buf, size_t count);
};
#define CLASS_ATTR(_name, _mode, _show, _store)            /
struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)


关于sysfs的更多信息,请参考 Linux内核源代码树中的Documentation/filesystems/sysfs.txt文件。

Platform
总线
platform
总线是Linux内核中的一个虚拟总线,它使得设备的管理更加简单化。目前大部分的驱动都是用platform总线来写的。 platform总线模型的各个部分都是继承自Device模型(姑且这么说吧),它在系统内实现了个虚拟的总线,即platform_bus,如果你的设备需要platform总线管理,那么就需要向系统中注册platform设备及其驱动程序。就像前面所介绍的那样,platform总线分为 platform_bus, platform_device platform_driver几个部分,他们的接口定义在<linux/platform.h>头文件中。

·                  platform bus

我们先来看看platform_bus的定义:

struct device platform_bus = {
    .bus_id        = "platform",
};

struct bus_type platform_bus_type = {
    .name        = "platform",
    .dev_attrs    = platform_dev_attrs,
    .match        = platform_match,
    .uevent        = platform_uevent,
    .suspend    = platform_suspend,
    .suspend_late    = platform_suspend_late,
    .resume_early    = platform_resume_early,
    .resume        = platform_resume,
};

int __init platform_bus_init(void)
{
    int error;

    error = device_register(&platform_bus);
    if (error)
        return error;
    error =  bus_register(&platform_bus_type);
    if (error)
        device_unregister(&platform_bus);
    return error;
}


platform_bus
数据结构描述了platform bus设备,platform_bus_type描述了platform bus总线,它提供了platform总线设备和驱动的匹配函数。platform总线是由函数platform_bus_init(void)初始化的。
对于Linux我们一般的设备驱动程序来说,就像前面Bus一段提到的那样,我们不需要关心platform总线本身,我们只要调用我们的设备和驱动接口就可以了。

·                  Platform Device

如果你想让platform总线来管理设备,那么,你需要先向platform系统注册设备,这个过程是通过下面的函数接口来实现的:

int platform_device_add(struct platform_device *pdev)
int platform_device_register(struct platform_device *pdev)

 

我们一般需要调用platform_device_register 函数来向系统添加platform设备。这两个函数唯一的差别就是platform_device_register 在添加设备前会初始化platform_devicedev数据成员,它是一个struct device类型数据。当一个platform_device添加到platform总线中后,platform总线就会为它找到匹配的设备驱动程序,很显然,在这之前,你需要向系统注册platform_driver

·                  Platform Driver

我们先来看看platform总线设备驱动的结构:

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*suspend_late)(struct platform_device *, pm_message_t state);
    int (*resume_early)(struct platform_device *);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
};

extern int platform_driver_register(struct platform_driver *);


很显然,它继承struct device_driver ,同样类似于struct device_driver ,一般我们需要实现probe函数,及指定platform_driver能驱动的设备的名字。

·                  使用Platform总线

下面这个例子告诉你如何使用platoform总线,这是一个Android Goldfish GPIO驱动程序。它本身就是一个platform设备驱动(goldfish-gpio),同时,它又会向系统注新的设备(android-timed-gpio,这个新设备又被timed_output.c驱动程序驱动。

......
#include <linux/platform_device.h>

struct platform_device timed_gpio_device = {
    .name  = "android-timed-gpio ",
    .id    = -1,
    .dev.platform_data = &timed_gpio_platform_data,
};

static int goldfish_gpio_probe(struct platform_device *pdev)
{
    struct goldfish_gpio_data *gpio_data;
    ......
    error = platform_device_register(&timed_gpio_device);
    ......
    return 0;
}

static int goldfish_gpio_remove(struct platform_device *pdev)
{
    int i;
    struct goldfish_gpio_data *gpio_data;
    ......
        platform_device_unregister( &timed_gpio_device );
    ......
    return 0;
}

static struct platform_driver goldfish_gpio_driver = {
    .probe    = goldfish_gpio_probe,
    .remove   = goldfish_gpio_remove,
    .driver = {
        .name = "goldfish-gpio"
    }
};

static int __init goldfish_gpio_init(void)
{
    return platform_driver_register(&goldfish_gpio_driver);
}

static void __exit goldfish_gpio_exit(void)
{
    platform_driver_unregister(&goldfish_gpio_driver);
}

 

这个新注册的设备(timed_gpio_device)由timed_output驱动管理,通过浏览这段代码,你应该对如何使用platform总线有个全面的了解。(本想把全部code放在这里,但超过最大字数限制!)

static struct class *timed_gpio_class;
struct timed_gpio_data {
    struct device *dev;
    struct hrtimer timer;
    spinlock_t lock;
    unsigned     gpio;
    int         max_timeout;
    u8         active_low;
};
......

static int android_timed_gpio_probe(struct platform_device *pdev)
{
    struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
    struct timed_gpio *cur_gpio;
    struct timed_gpio_data *gpio_data, *gpio_dat;
    int i, ret = 0;
    ......
}

static int android_timed_gpio_remove(struct platform_device *pdev)
{
}

static struct platform_driver android_timed_gpio_driver = {
    .probe        = android_timed_gpio_probe,
    .remove        = android_timed_gpio_remove,
    .driver        = {
        .name        = "android-timed-gpio",
        .owner        = THIS_MODULE,
    },
};

static int __init android_timed_gpio_init(void)
{
    timed_gpio_class = class_create(THIS_MODULE, "timed_output");
    if (IS_ERR(timed_gpio_class))
        return PTR_ERR(timed_gpio_class);
    return platform_driver_register(&android_timed_gpio_driver);
}

static void __exit android_timed_gpio_exit(void)
{
    class_destroy(timed_gpio_class);
    platform_driver_unregister(&android_timed_gpio_driver);
}


Kobject
kset
提到Linux的设备模型,就不得不提kobjectkset这两个内核对象,他们才是Linux内核设备模型的最基础的结构,但讲解他们却是一个枯燥过程,限于篇幅,这个就不作介绍了,请参考Linux文档<documentation/kobject.txt>


后记
在这里,我们简单的介绍了Linux的设备模型,包括基本总线、设备、驱动的关系,同时也简单的介绍了Linux2.6内核的platform总线。这些内容应该足够让你了解如何使用Linux设备模型来管理设备了。

 

2016-05-31 16:11:33 Devil_box 阅读数 558
  • linux设备驱动模型-linux驱动开发第5部分

    本课程是linux驱动开发的第5个课程,主要内容是linux的设备驱动模型,包括总线、类、设备、驱动等概念,重点通过platform平台总线的工作来演示设备驱动模型的工作方法,实践环节对上个课程的LED驱动进行平台总线式改造,终目标是让大家彻底掌握linux的总线式设备驱动模型。

    4829 人正在学习 去看看 朱有鹏

模块代码chr_test.c

/**
 * To test the chr_dev for AP operation
 * by wozon
 *
 * */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>


MODULE_LICENSE ("GPL");


dev_t dev;

struct cdev cdev;


int chr_open(struct inode *i, struct file *f)
{
    printk("AP opened!\n");
    return 0;
}

int chr_close(struct inode *i, struct file *filp)
{
    printk("AP closed!\n");
    return 0;
}

struct file_operations chr_fops={
    .owner = THIS_MODULE,
    .open = chr_open,
    .release = chr_close,
};


//模块加载函数
int __init init_module (void)
{
    int rst = 0;
    dev = MKDEV(249,1);   //创建设备节点
    rst = register_chrdev_region(dev,1,"shuibian");  //向内核注册设备号
    if(rst != 0)
    {
        rst = -1;
        goto exit0;
    }

    cdev_init(&cdev,&chr_fops);//字符设备驱动控制块初始化
    rst = cdev_add(&cdev,dev,1);//向内核添加字符设备驱动控制块
    if(rst != 0)
    {
        rst = -2;
        goto exit1;
    }
    printk("Chr,This module was added by the kernel!\n");

    goto exit0;

exit1:
    unregister_chrdev_region(dev,1);//注销设备号

exit0:
    return rst;
}


//模块卸载函数
void __exit cleanup_module (void)
{
    cdev_del(&cdev);//从内核中删除字符设备控制块。
    unregister_chrdev_region(dev,1);
    printk("This module was removed by the kernel!\n");
}

应用程序代码test.c

/*
 * test.c
 */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>

int main (void) 
{
    int fd;
    fd = open ("/dev/sb13",O_RDWR);
    if (fd < 0) {
      perror("open");
      exit(0);
    }
    printf ("/dev/chrdev opened, fd=%d\n",fd);

    close (fd);
    printf ("/dev/chrdev closed!\n");
    return 0;
}

Makefile

#To display info during Ccompiling
$(warning KERNELRELEASE=$(KERNELRELEASE))

#To check the KERNELRELEASE enviroment value
ifeq ($(KERNELRELEASE),)
#It's NULL, so to set the enviroment value

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

PWD :=$(shell pwd)

modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*



.PHONY: modules modules_install clean
#To create obj file
else
        obj-m := chr_test.o
endif

先编译模块文件,在终端里面输入make,按下回车
这里写图片描述
生成chr_test.ko文件。
进入root用户,手动创建设备节点:mknod /dev/ c 249 1
这里写图片描述
可以看出在/dev/目录下面生成了sb13设备
将chr_test.ko文件加载进内核里面 insmod chr_test.ko
编译test.c文件,生成应用程序
这里写图片描述
执行应用程序 sudo ./a.out
这里写图片描述
可以看出,正常访问到了我们刚刚创建的设备节点。

ps:我们这样通过手动方式创建设备节点,然后编辑模块代码,用应用程序来访问,很复杂,尤其是作为别人项目一部分时,就更加不便维护,下一节我们将讲解自动创建设备节点的方法。

没有更多推荐了,返回首页