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

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

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

目录

一、字符设备驱动涉及的

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值