初级驱动DAY4

(一)新的申请设备号的方法

    1》静态申请设备号
    int register_chrdev_region(dev_t from, unsigned count, const char *name)
    //参数1:要申请的设备号-----包含主设备号和次设备号
    //参数2:设备的个数
    //参数3: 设备号的描述,字符串,自定义
    //返回值:成功---0;失败---错误码
    
    2》动态申请设备号
        int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
        //参数1:保存设备号变量的地址
        //参数2:次设备号的起始值
        //参数3: 设备的个数
        //参数4: 设备号的描述,字符串,自定义
        //返回值:成功---0;失败---错误码

    
    //如果使用上面方式申请设备号,必须自己创建cdev对象,并且初始化对象,将对象注册到系统中
        struct cdev *cdev ;  //首先创建一个cdev的对象
        drv_led->cdev=cdev_alloc();  //给这个cdev的对象分配空间
        cdev_init(drv_led->cdev,&fops); //初始化这个对象
        cdev_add(drv_led->cdev,drv_led->devno,1);//将cdev的对象注册到系统中

    struct cdev {
        struct kobject kobj;
        struct module *owner;
        const struct file_operations *ops;  //设备操作接口
        struct list_head list;
        dev_t dev;    //设备号
        unsigned int count;
    };


    void cdev_del(struct cdev *p)  //删除cdev对象
    
二,应用程序调用底层驱动的过程分析
    了解几个重要的结构体
        1,当应用进程每次打开一个文件时,就会创建一个结构体:struct file  -----//动态产生的
        struct file {
        const struct file_operations    *f_op;   //设备操作接口

        atomic_long_t        f_count;
        unsigned int         f_flags;
        fmode_t            f_mode;        //文件的权限和属性
        loff_t            f_pos;        //文件的位移量
        struct fown_struct    f_owner;
        const struct cred    *f_cred;
        struct file_ra_state    f_ra;

        u64            f_version;
        /* needed for tty driver, and maybe others */
        void            *private_data;  //私有数据
    };
    
        2,当你创建一个文件时,系统会在磁盘上或者内存上创建一个结构体:struct inode
        struct inode {
        /* RCU path lookup touches following: */
        umode_t            i_mode;   //文件的类型和属性
        uid_t            i_uid;
        gid_t            i_gid;
        const struct inode_operations    *i_op;
        struct super_block    *i_sb;

        spinlock_t        i_lock;    /* i_blocks, i_bytes, maybe i_size */
        unsigned int        i_flags;
        unsigned long        i_state;
    #ifdef CONFIG_SECURITY
        void            *i_security;
    #endif
        struct mutex        i_mutex;


        unsigned long        dirtied_when;    /* jiffies of first dirtying */


        dev_t            i_rdev;   //设备号   

        const struct file_operations    *i_fop;    /* former ->i_op->default_file_ops */  //设备操作接口

        void            *i_private; /* fs or device private pointer */  //私有数据
    };
    
    3,设备操作接口 
        struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                  loff_t len);
    };
    
    4,封装了设备号和设备操作接口的结构体
        struct cdev {
        struct kobject kobj;
        struct module *owner;
        const struct file_operations *ops;  //设备操作接口
        struct list_head list;
        dev_t dev;    //设备号
        unsigned int count;
        };
        
    三,硬件初始化----中断申请
    
    1》申请中断
        static inline int __must_check request_irq
        (unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
        //参数1:中断通道号
            IRQ_EINT(x) 获取中断通道号   //比如获取外部中断0的中断通道号  IRQ_EINT(0)//返回值就是中断通道号
        //参数2:中断处理函数的函数名--led_irq_handler
        //参数3:中断触发方式
                #define IRQF_TRIGGER_NONE    0x00000000  //内部中断触发
                #define IRQF_TRIGGER_RISING    0x00000001  //上升沿触发
                #define IRQF_TRIGGER_FALLING    0x00000002//下降沿触发
                #define IRQF_TRIGGER_HIGH    0x00000004  //高电平触发
                #define IRQF_TRIGGER_LOW    0x00000008  //低电平触发
        //参数4:字符串,描述中断
        //参数5:传给中断处理函数的参数
        //返回值:成功---0;失败---错误码                    
            typedef irqreturn_t led_irq_handler(int, void *) //中断处理函数  参数1:中断通道号
            {
                
            }
        
        //中断服务函数
        irqreturn_t drv_keyirq_handler(int irq, void *dev)
        {
            printk("------------%s--------------\n",__FUNCTION__);
            printk("IRQ_HANDLED: %d\n",*((unsigned int *)dev));
            return IRQ_HANDLED;
        }
            
        // 4,硬件初始化   ------中断申请
        drv_key->irq=IRQ_EINT(0);  //获取中断通道号
        drv_key->key_code=0x01;   //给传递给中断处理函数的参数赋值
        //进行中断申请
        ret=request_irq(drv_key->irq, drv_keyirq_handler, IRQF_TRIGGER_FALLING,"drv_key1",&drv_key->key_code);
        if(ret<0){
            printk("key1_request_irq is error\n");
            goto device_err;
        }
        
        //释放中断
        free_irq(drv_key->irq,&drv_key->key_code);
        参数1:中断通道号
        参数2:传给中断服务函数的参数
        
        
        作业:实现key0----key4打印不同键值
        
        
        四,阻塞IO和非阻塞IO在内核驱动中的实现
        
                        
        //实现的功能:按键按下后,将按键值和中断通道号,上传到应用层,并且打印
        
            //要实现此功能,就要实现read()函数------因为read函数时能够将内核空间数据传递到应用
            
                //由于read函数时阻塞函数,所以要实现阻塞------没有数据,等待数据,有数据上传到应用层
                        
                    //实现阻塞需要以下几步
        
        1》阻塞IO的实现
            在应用空间中,很多函数默认时阻塞的,read(),accept(),connect(),recv()等等
            
            在驱动中如何实现阻塞IO:
            1> 定义一个等待队列头,并且初始化这个等待队列头---------------//在硬件初始化的位置初始化等待队列头
                wait_queue_head_t Wq;
                init_waitqueue_head(wait_queue_head_t *q)
                
            2>根据条件使当前的进程进入阻塞状态--------//在设备的操作接口中:read()中实现
               wait_event_interruptible(wait_queue_head_t q, condition)
               //参数1:-----等待队列头
               //参数2:-----条件变量,condition=0-----休眠  condition不为0 -----不休眠
            3>当有资源可以用时,要唤醒进程-------//在中断处理函数中-----按键按下
                wake_up_interruptible(wait_queue_head_t *q)
               
               
               ssize_t drv_key_read (struct file *filp, char __user *buf, size_t size, loff_t *flags)
                {
                    int ret;
                    printk("------------%s--------------\n",__FUNCTION__);
                    wait_event_interruptible(drv_key->wq,drv_key->have_data);

                    ret=copy_to_user(buf, &drv_key->key_infos,size);
                    if(ret!=0){
                        printk("drv_key copy_to_user is error\n ");
                        return ret;
                    }
                    //清除数据包,并且将是否有资源清零
                    drv_key->have_data=0;
                    memset(&drv_key->key_infos,0,sizeof(drv_key->key_infos));
                    
                    return size;
                }

        2》非阻塞IO的实现
            阻塞IO                                         非阻塞IO
            fd=open("/dev/drv_key",O_RDWR);                fd=open("/dev/drv_key",O_RDWR);
            
            read(fd,......)                                 read(fd,.....)
            
            如果有数据-----读取数据,返回读到数据           如果有数据-----读取数据,返回读到数据
            如果没有数据-----使进程阻塞                     如果没有数据----立即返回  
            
            如果应用层程序open("/dev/drv_key",O_RDWR|O_NONBLOCK),代表此应用程序调用的函数,实现非阻塞
            
            //应用层,告诉内核阻塞或不阻塞(|O_NONBLOCK),内核实现阻塞或不阻塞
            
            //如果没有数据且应用空间传过来了O_NONBLOCK
            if(!drv_key->have_data&&filp->f_flags&O_NONBLOCK){    
                    return -EAGAIN;
            }

你可能感兴趣的:(嵌入式)