欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux 驱动中用到的一些结构体的说明

程序员文章站 2024-02-23 10:00:10
...

最近开始,重新回顾原来的驱动入门资料了。

还是那些特别的结构体。想做个简单的总结一起

目录

一、字符设备驱动涉及的

1.设备号类型dev_t

2.字符设备的结构cdev

3.字符设备相关函数中涉及的参数

3.1

3.2

3.3struct file_operations 里函数参数

二、其他基础的使用

1.驱动符号的导出

2.linux驱动模块的参数


一、字符设备驱动涉及的

1.设备号类型dev_t

在linux内核里用类型”dev_t”来表示一个设备号. 其实就是一个unsigned int.

dev_t类型有32位数, 其中高12位用于存放主设备号,低20位用于存放次设备号.
在”include/linux/kdev_t.h”里有提供设备号的操作宏:

#define MINORBITS   20
#define MINORMASK   ((1U << MINORBITS) - 1)

#define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

#include <linux/fs.h>    

//静态:申请指定的设备号, from指设备号(需已指定主设备和次设备号), count指使用该驱动有多少个设备(次设备号), name设备名(用于查看用, 长度不能超过64字节   )
int register_chrdev_region(dev_t from, unsigned count, const char *name);


//动态申请设备号, 由内核分配没有使用的主设备号, 分配好的设备号存在dev(不需初始化), baseminor指次设备号从多少开始, count指设备个数, name设备名 
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name)

//释放设备号, from指设备号, count指设备数
void unregister_chrdev_region(dev_t from, unsigned count)

2.字符设备的结构cdev

  ”struct cdev”类型的一个对象来描述一个字符设备驱动

#include <linux/cdev.h>
struct cdev {
    struct kobject kobj;       //内核用于管理字符设备驱动, kobject就是内核里最底层的类. 内核里会自动管理此成员.
    struct module *owner;      //通常设为THIS_MODULE, 用于防止驱动在使用中时卸载驱动模块
    const struct file_operations *ops;  //怎样操作(vfs), 也就是实现当用户进程进行open/read/write等操作时,驱动里对应的操作.
    struct list_head list;     //内核链表节点,内核里自动管理此成员.
    dev_t dev;                 //设备号
    unsigned int count;        //设备数
};

3.字符设备相关函数中涉及的参数

3.1 struct inode

在linux内核,用一个inode节点对象描述一个要操作的文件/设备文件, 包括权限,设备号等信息. 就是描述一个要操作的文件的属性. 一个文件可以打开很多次, 但都是共用一个inode对象来描述属性的. 文件描述符属于一个进程的资源,不同进程里有可能相同的文件描述符.

struct inode {
    ...
    dev_t  i_rdev;     //设备文件对应的设备号, 驱动里即可通过区分次设备号来区别不同的具体硬件
    struct cdev *i_cdev; //指向对应的字符设备驱动cdev对象的地址.
    ...
};

3.2 struct file

在用户进程里用一个int类型来表示文件描述符.但文件描述符里有还存有对文件位置的偏移,打开标志等信息, 用一个int数无法记录下来的,所在每个文件描述符的信息都是由内核里用file对象描述文件描述符, 在文件打开时创建, 关闭时销毁

struct file {
    ...
    struct path     f_path;
    const struct file_operations    *f_op; //对应的文件操作对象的地址
    unsigned int        f_flags; //文件打开的标志
    fmode_t         f_mode;  //权限
    loff_t          f_pos;   //文件描述符的偏移
    struct fown_struct  f_owner; //属于哪个进程
    unsigned int        f_uid, f_gid; 
    void            *private_data; //给驱动程序员使用
    ...
};

如果打开设备文件,那么得到的file对象:
    file对象里的成员f_path.dentry->d_inode->i_rdev可以获取到设备文件的设备号
    file对象里的成员f_path.dentry->d_inode可以获取到设备文件的inode对象的地址

3.3struct file_operations 里函数参数

     //inode表示应用程序打开的文件的节点对象,  file表示打开文件获取到的文件描述符
int (*open) (struct inode *, struct file *);
     //返回值0表示成功打开,负数表示打开失败。内核根据open函数的返回值来确定是否给调用的用户进程分配文件描述符。
     //在驱动可以不实现此函数, 如不实现。则表示每次打开都是成功的.  

     //buf指向用户进程里的缓冲区, len表示buf的大小(由用户调用read时传进来的)
     //off表示fl文件描述符的操作偏移, 返回值为实际给用户的数据字节数.
ssize_t (*read) (struct file *fl, char __user *buf, size_t len, loff_t *off);
     //注意,必须通过off指针来改变文件描述符的偏移(*off += 操作字节数). 不可以直接通过"fl->f_pos"来设置    

     //用户进程把数据给驱动, 也就是让驱动存放用户进程传进来的数据.
     // 参考read函数
ssize_t (*write) (struct file *, const char __user *buf, size_t len, loff_t *off);

      // cmd表示用户进程调用ioctl时的第二个参数, arg表示第三个参数(可选)
     long (*unlocked_ioctl) (struct file *fl, unsigned int cmd, unsigned long arg);
      // 返回值为0表示ioctl成功, 返回负数表示失败.

      // app: lseek(fd, 54, SEEK_SET)
     loff_t (*llseek) (struct file *fl, loff_t offset, int whence);

二、其他基础的使用

1.驱动符号的导出

在一个驱动模块中定义了下面全局的函数,可以在其他的驱动模块中使用,类似C中的全局函数extren 关键字的使用

//在驱动模块1中进行声明和定义
   void myfunc(char *str)
    {
        printk("in myfunc: %s\n", str);
    }

    EXPORT_SYMBOL(myfunc);


//可以在驱动模块2或者其他的驱动中,使用
    extern void myfunc(char *);
    static int __init test_init(void)
    {
        myfunc("test init");
        return 0;
    }

2.linux驱动模块的参数

模块参数是用于在加载驱动模块时,指定模块里面的特定变量的具体值.

  1).  #include <linux/moduleparam.h>

   2). 在驱动源码里定义变量
    static int num = 0; //当加载模块不指定num的值时则为0

   3). 声明指定的变量为驱动模块参数
    module_param(变量名, 类型, 权限);
      //类型可有: byte, int, uint, short, ushort, long, ulong, bool, charp
      //权限:    其它用户所占的权限里不能有写的权限

   4). 加载驱动模块时指定模块参数的具体值:
          insmod test.ko 变量名1=值1  变量名2=值2
eg:


#include <linux/init.h>
#include <linux/module.h>
#include <mach/gpio.h>  // 芯片io口的宏定义
#include <linux/gpio.h>  // io口的调用函数
#include <linux/moduleparam.h>

static int on = 1;
module_param(on, int, 0644);  //声明模块参数

#define LED_GPIO  GPIOA(15) //PA15
static int __init test_init(void)
{
    int ret;

    ret = gpio_request(LED_GPIO, "myled"); //如请求失败,则表示此io口已被其它驱动使用
    if (ret < 0)
        return ret;

    gpio_direction_output(LED_GPIO, on); //根据外面的参数值来决定led灯的亮灭
    return 0;
}

static void __exit test_exit(void)
{
    gpio_set_value(LED_GPIO, 0);

    gpio_free(LED_GPIO);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");


加载驱动模块时,通过下面的指令完成,传值


insmod test.ko on=1  或者 insmod test.ko on=0

相关标签: 内核驱动基础