基于Linux内核的驱动开发

1 中断下半部
        一般来说,操作系统中的中断越短越好,如果系统中的中断处理时间过长,可以
        将中断处理过程分成两部分来处理。
        第一部分,专门接收和响应中断请求(登记中断)--》上半部-->handler
        第二部分,专门来处理中断的耗时业务逻辑(处理耗时操作)--》下半部
        Linux系统中的中断处理也是分成上半部和下半部来完成的。下半部的处理方法有:
          tasklet  工作队列  内核定时器
        1.1 tasklet--》小“片”任务
            1 声明一个 tasklet
            DECLARE_TASKLET(name, func, data)
            name:小任务的名称
            func:小任务对应的处理函数
                    void (*func)(unsigned long data);
            data:传给func函数的入参
            2 调度小任务---》handler
            tasklet_schedule(&name)
            static inline void tasklet_schedule(struct tasklet_struct *t)
            作用:调度小任务
            *t:指向小任务名称的指针        
        1.2 工作队列
            是指在中断处理中可以把耗时的操作推迟执行,可以把耗时的操作交到工作
            队列中等待被执行,内核中有一个线程events会去工作队列中找等待被执行
            的工作,然后执行这个等待的工作(工作:中断处理程序中耗时的那部分操作)
            1 定义一个工作队列
                struct work_struct   my_wq;
            2 初始化工作队列
                INIT_WORK(_work, _func)
                _work:指向工作队列的指针
                _func:工作队列对应的处理函数
            typedef void (*work_func_t)(struct work_struct *work);
            3 调度工作队列
                schedule_work(&my_wq);
        1.3 内核定时器
            1 struct time_list  mytimer;定义一个内核定时器
            2 helloprobe:
                init_timer(mytimer);初始化内核定时器
                mytimer.function=my_func;-->用来处理耗时的操作
                mytimer.expires=时间值--》表示超时时间
                add_timer(&mytimer)-->启动定时    
        
        2  块设备驱动
            2.1 块设备以及相关术语
                块设备:可以从设备的任意位置读取一定长度数据的设备
                常见块设备:硬盘 磁盘 光盘 U盘 sd卡 tf卡。。。
                硬件种类:机械硬盘 
                            固态硬盘
                            混合硬盘
                磁盘概念:
                    磁头:是磁臂顶端用来指向硬盘的读写数据的磁针
                    扇区:是读写数据的最小单位512byte
                    柱面:多张累加的磁盘上同一半径的磁道形成的面
                    磁道:磁头画的同心圆就是磁道,用来存放数据
                    硬盘容量:磁头数*柱面数*每个磁道包含的扇区个数*512byte

                磁头:8
                扇区:16
                柱面:128
            2.2 模拟创建一个硬盘,并实现其驱动
                    init流程:--》HelloModule
                        1 创建一个块设备号   
                    int register_blkdev(unsigned int major, const char *name)
                        作用:创建块设备号
                        major:主设备号。major>0,静态创建块设备号
                                major=0,动态创建块设备号
                        *name:块设备名称 系统中唯一,不能重复
                        返回值:成功 设备号
                                    失败 负数
                        2 创建并初始化请求队列--》用户对设备的读写请求
                        struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
                            作用:初始化一个请求队列(用自旋锁来控制对列的访问方向)
                            *rfn:请求处理函数
                            typedef void (request_fn_proc) (struct request_queue *q)
                            *lock:指向自旋锁的指针
                            返回值:指向请求队列的指针
                        3 分配一个gendisk结构体
                        struct gendisk *alloc_disk(int minors)
                            作用:分配一块存放块设备结构体的内存
                            minors:次设备的数量(最大分区数)
                            返回值:指向块设备的结构指针
                        用struct gendisk来描述一个块设备
                        struct gendisk {
                            int major;--》主设备号        
                            int first_minor;--》第一个次设备号
                            int minors;--》次设备的个数                   
                        char disk_name[DISK_NAME_LEN];    --》块设备名称
                        const struct block_device_operations *fops;--》块设备操作方法集
                        struct request_queue *queue;--》IO请求队列
                            。。。
                        }
                        4 设置gendisk结构体成员
                            如上
                        5 为磁盘分配内存
                        void *vmalloc(unsigned long size)
                            size:分配的内存大小
                            返回值:返回指向内存的指针
                        6 设置磁盘容量
                        static inline void set_capacity(struct gendisk *disk, sector_t size)
                         *disk:指向块设备的指针
                        size:总的扇区数
    
                        7 添加磁盘到内核中
                        void add_disk(struct gendisk *disk)
                        *disk:指向块设备的指针

                    exit流程:--->HelloExit
                        1 释放磁盘扇区缓存
                        2 释放gendisk结构体
                        void del_gendisk(struct gendisk *disk)
                            *disk:指向块设备结构体的指针
                        3 清除内存中的请求队列
                        void blk_cleanup_queue(struct request_queue *q)
                            *q:指向请求队列的指针
                        4 删除块设备号
                    void unregister_blkdev(unsigned int major, const char *name)
                        major:设备号
                        *name:设备名称

                        struct hd_geometry {--》用来描述磁盘的几何信息
                          unsigned char heads;--》磁头数
                          unsigned char sectors;--》每个磁道包含的扇区个数
                          unsigned short cylinders;--》柱面数
                          unsigned long start;--》起始地址
                        };
            
         实现读写的请求处理函数:
                void  RequestFunc(struct request_queue *p)
                1 从请求对列中获取一个请求
            struct request *blk_fetch_request(struct request_queue *q)
                    *q:指向请求队列的指针
                    返回值:从请求队列中获取到的一个请求
                2 获取当前请求的位置(在哪里读写)
                static inline sector_t blk_rq_pos(const struct request *rq)
                *rq:指向请求的指针
                返回值:扇区的位置
                blk_rq_pos*512=具体的字节位置                                    
                3 获取从当前位置要读/写多少个字节?
                static inline unsigned int blk_rq_cur_sectors(const struct request *rq)
                作用:获取该请求需要从当前位置获取多少扇区
                *rq:指向请求的指针
                返回值:扇区的个数
                blk_rq_cur_sectors*512=具体的字节大小
                4 判断请求的方向?
                rq_data_dir(rq)
                *rq:指向请求的指针
                返回值:读 READ  写 WRITE
                5 根据请求的方向读写数据
                    read:硬盘数据---》req->buffer
                            memcpy(req->buffer,g_Data+offset,nbytes)
                    write:req->buffer--》硬盘
                            memcpy(g_Data+offset,req->buffer,nbytes)
                6 判断当前请求是否处理完毕?
                bool __blk_end_request_cur(struct request *rq, int error)
                    *rq:指向请求的指针
                    error:0
                    返回值:结束 false;未结束  true
            
            测试:
                虚拟机:
                    1 hello.c--->交叉编译--》hello.ko
                    2 cp hello.ko /source4/rootfs

                开发板:
                1 启动开发板 配置ip相关项 配置bootarg为nfs
                2 重启开发板
                root@farsight#insmod hello.ko
                                    ls -l /dev/hellodisk*-->查看是否有模拟的块设备
                分区:
                    fdisk /dev/hellodisk -u(按照扇区划分)
                    m:help
                        n:创建分区
                            p-->创建主分区    16~5000
                            e--->创建扩展分区 5001~10000
                        w:保存并退出
                        q:只退出不保存
                        p:打印分区表信息
                    ls -l /dev/hellodisk*--->查看分区是否OK?
                格式化要使用的分区:
                    mkfs.ext2  /dev/hellodisk1
                挂载分区到mnt目录下并测试读写
                    mount -t ext2 /dev/hellodisk1 /mnt
                    cd /mnt
                    vi 1.txt---》创建并写入内容
                    dmesg |tail-->查看读写驱动有没有被调用
                    vi  1.txt-->打开

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值