> 文章列表 > 基于linux内核的驱动开发

基于linux内核的驱动开发

基于linux内核的驱动开发

1 自动创建设备文件
   创建设备文件的方式:1 手动创建 sudo mknod /dev/haha0 c 250 0
                            2 自动创建--》使用内核函数
        linux内核为我们提供了一组内核函数,用于在模块加载时自动在/dev目录下创建响应的设备文件,并在卸载时删除该设备文件
         创建:1 class_create--》创建设备文件类
                2 device_create--》创建设备文件

        删除:3 class_destory--》删除设备文件类
               4 device_destory

      设备  主设备--》一类设备
            次设备--》该类设备中的某一个设备
     设备文件:一个设备文件---》一个次设备

        1  class_create--》创建设备文件类
            struct class *class_create(owner, name)
                owner:表示模块本身THIS_MODULE
                name:设备模块名
                 返回值:指向设备文件类的指针(struct class表示设备文件类)
        2 device_create--》创建设备文件
            struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)
                *class:指向设备文件类的指针
                *parent:指向符设备的指针,一般为NULL
                devt:设备号 由主次设备号构成
                *drvdata:指向设备私有数据,若无,给NULL
                *fmt:设备文件名(haha0/haha1...)
                ...:可变参数
                返回值:成功 struct device *;失败err,需要判断
            3 class_destroy
            void class_destroy(struct class *cls)    
                    作用:删除设备文件类
                    *cls:指向设备文件类的指针    
            4 device_destroy
            void device_destroy(struct class *class, dev_t devt)
                作用:删除设备文件
                *class:指向设备文件类的指针
                devt:  设备号

           测试步骤:
            1 sudo insmod hello.ko
            2 dmesg |tail
            3 ls -l /dev/hah*--》查看是否生成了设备文件haha0/haha1?
            4 sudo ./test
            5 sudo ./test1
            6 sudo rmmod hello.ko
            7 ls -l /dev/hah*--->查看设备文件是否被删除

2 ioctl      man ioctl
    ioctl-->系统调用函数-->应用程序用它给设备&内核发送控制指令
     Linux内核给用户提供了两类系统调用函数:
            一类是数据操作函数,如read write...
            另一类是非数据操作函数,如ioctl,内核将对设备的操作交给了ioctl接口
                (换句话说,应用程序可以使用ioctl接口去控制底层设备)
        fd=open(“/dev/haha0”)-->250 0--->字符设备
    int ioctl(int d, int request, ...);
            d:文件描述符
            request:指令码
            ...:可变参数,若有,那么该参数是传给内核驱动的参数
            返回值:0成功 非0 失败
       
    命名码:1 可以自定义  1 0
               2 使用标准命名码  int--》4部分
                30-31:数据的控制方向(get/set)
                16-29:数据大小(14bit)
                8-15:设备类型(8bit)
                0-7:cmd-->命令码(区分命令的顺序序号)
                #define _IOC(dir,type,nr,size) \\
                    (((dir)  << _IOC_DIRSHIFT) | \\
                    ((type) << _IOC_TYPESHIFT) | \\
                    ((nr)   << _IOC_NRSHIFT) | \\
                    ((size) << _IOC_SIZESHIFT))

        #define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
        #define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
        #define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
        #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))

        common.h
            #define  TEST_CMD  0x1
            #define  TEST_CMD1  _IO('h',1)
            #define  TEST_CMD2  _IOW('h',2,int)
        
    应用:test.c
            fd=open("/dev/haha0",);
            ioctl(fd,TEST_CMD);
            ioctl(fd,TEST_CMD1);
            ioctl(fd,TEST_CMD2,12345);
                        
     驱动:HelloIoctl
            long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long arg)            
            {
                switch(cmd)
                    case TEST_CMD:{printk();break;}
                    case TEST_CMD1:{printk();break;}
                    case TEST_CMD2:{printk();break;}
            }
    
      测试步骤:
            1 sudo insmod hello.ko
            2 ls -l /dev/haha*
            3 sudo ./test
            4 dmesg |tail-->查看ioctl发的指令是否被解析?
            5 sudo rmmod hello.ko

3  驱动的互斥
   
    设备号:主设备+次设备
   主设备:代表一类设备--》驱动
   次设备:一类设备中的某一个设备--》使用驱动程序的终端个体
   多个子设备共用一个驱动程序,若多个子设备同时访问一个驱动程序时,就产生了竞态
    访问,那么如何解决驱动的竞态呢?linux内核提供了多种互斥方法:
        有:互斥锁 信号量 原子变量 自旋锁。。
    3.1 互斥锁
        mutex_t  mutex;-->应用层互斥锁
        1 定义互斥锁--》全局
        struct mutex  g_Mutex;-->内核互斥锁
        2 初始化互斥锁
            mutex_init(&g_Mutex);-->HelloModule
        3 加锁
            mutex_lock(&g_Mutex);-->HelloOpen
        4 解锁
            mutex_unlock(&g_Mutex);-->HelloClose
    3.2 信号量      
        1 定义信号量
            struct semaphore  sem;//内核信号量
        2 初始化信号量
            sema_init(&sem,1)-->HelloModule
        3 获取信号量-->HelloOpen
            down_interruptible(&sem)-->p操作 >0:sem--;函数返回  =0:阻塞进程
            down(&sem)
        4 释放信号量-->HelloClose
            up(&sem)--> v操作   sem_post(&semb)-->给sem++               
    3.3 原子变量   
        1 定义原子变量并初始化 
         atomic_t   a=ATOMIC_INIT(1);
        2 使用原子变量
        需要通过linux内核提供的函数来操作原子变量
        atomic_dec_and_test(&a): 该函数给原子变量a减1,然后测试其值是否等于0?
                                            如果为0,返回true;如果不为0,返回false。
        
        atomic_inc(&a):该函数给原子变量加1
        
                sudo ./test-->open("/dev/haha0")
                sudo ./test1--->open("/dev/haha1")

        helloOpen:
            if(!atomic_dec_and_test(&a))  1->0-->-1
            {
                atomic_inc(&a); 0
                return -EBUSY;
                //不能使用驱动
            }
            //可以使用驱动资源
            return 0;

        helloClose:
            atomic_inc(&a)0-->1

    3.4 自旋锁(忙等锁)
        1 定义自旋锁
            spinlock_t   g;-->自旋锁
        2 初始化自旋锁
            spin_lock_init(&g)-->HelloModule
        3 获取自旋锁
            spin_lock(&g)-->HelloOpen
           4 释放自旋锁
            spin_unlock(&g)-->HelloClose

    测试步骤:
        1 sudo insmod hello.ko
       2 dmesg |tail
       3 ls -l /dev/hah*
       4 sudo ./test-->write  sleep
       5 sudo ./test1--->read
       6 sudo rmmod hello.ko