嵌入式驱动day03

复习day02
1.模块参数
    内核模块xxx.c文件中申请的全局变量声明为模块参数
    insmod xxx.ko mpint=10l
    /sys/module/xxx/parameters/mpint
2.内存管理
    逻辑地址     虚拟地址     物理地址
    linux 编程时使用到的都是虚拟地址
    linux内核对内存管理的最小单位是页。
    kmalloc/kfree
    vmalloc/vfree
    __get_free_pages/free_pages
    申请内存失败的时候不能够直接退出,需要释放之前申请的内存

    flags:GFP_KERNEL,在申请内存过程中可能会产生睡眠
                GFP_ATOMIC,不会导致睡眠
3.内核链表(include/linux/list.h)
    list_add
    list_del
    list_for_each
    INIT_LIST_HEAD
    list_entry:招到那个成员变量然后返回指针
————————————————————————————————————————
1.内核定时器
    时钟中断
         由系统的定时硬件以周期性的时间间隔变身,返回间隔(也就是频率)由内核根据常数HZ来确定
    HZ常数
         它是一个不体系结构无关的常数,可以配置50-1200之间,可以在内核中配置
    tick
         它是HZ的倒数,也就是每变生一次硬件定时器中断的时间间隔。如HZ为200,tick为5毫秒
    jiffies:
         它是linux核心变数(32位变数 unsigned long),它被用记录自开机以来,已经过了多少个tick.
         每变生一次硬件定时器中断,jiffies变数就会被加1
    HZ:常数,决定了时钟中断发生的频率 (1s中发生的次数)
    tick:发生时钟中断的时间间隔 tick=1/HZ
    jjffies:核心变数
    struct timer_list {
         ....
         unsigend long expires;超时时间
         void (*function)(unsigned long)调用函数
         unsigned long data;传递参数
        ......
    }
    
  
  
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/timer.h>
  4. MODULE_LICENSE("GPL");
  5. /*定时器时间间隔*/
  6. #define BARK_TIMER 5
  7. #define BARK_TIMES 10
  8. /*定义定时器变量*/
  9. struct timer_list bigdog_timer;
  10. void bigdog_timer_handler(unsigned long data){
  11. /*记录该函数被调用的次数*/
  12. static int bark_count=0;
  13. printk("wang wang~~");
  14. if(bark_count<BARK_TIMES){
  15. bigdog_timer.expires=jiffies+HZ*BARK_TIMER;
  16. bigdog_timer.data++;
  17. add_timer(&bigdog_timer);
  18. bark_count++;
  19. }
  20. }
  21. int __init kerneltimer_init(void){
  22. bigdog_timer.expires=jiffies+HZ*BRAK_TIMER;
  23. //注意此处与应用空间不同,不知道什么时候进行安装
  24. //
  25. bigdog_timer.function=bigdog_timer_handler;
  26. bigdog_timer.data=100;
  27. /*初始化定时器变量*/
  28. init_timer(&bigdog_timer);
  29. /*添加定时器到内核中去*/
  30. add_timer(&bigdog_timer);
  31. return 0;
  32. }
  33. /*初始化函数中对内核的影响在退出的时候都要取消*/
  34. void __exit kerneltimer_exit(void){
  35. /*从内核中删除指定的定时器*/
  36. del_timer(&bigdog_timer);
  37. }
  38. module_init(kerneltimer_init);
  39. module_exit(kerneltimer_exit);
#       #        #        #        #
重启tftp服务
sudo /etc/init.d/tftpd-hpa restart
#      #        #        #        #


2.系统调用
    ANSI C fopen(标准C的库函数)
    UNIX C open(系统调用)
    用户态的程序一般情况下是不能访问内核的资源
    只有通过中断或者系统调用才能从用户态进入内核态
    unistd.h
    2.6.35内核中系统调用的个数是366个
    linux下的驱动程序是为应用程序提供服务的
    驱动程序在内核态执行
系统调用的原理(实现方式)
    应用程序首先使用适当的值填充寄存器,
    然后调用一个特殊的指令, 跳转到内核某个固定的位置。
    内核根据应用程序所填充的固定的值来找到相应的函数执行。
    
    适当的值:arch/arm/asm/unistd.h
    特殊的指令:arm->SWI(软中断)
                                   X86 0x80
    固定位置:在ARM体系结构中,应用程序跳转到的位置是
                               entry-common.S ->vector_swi
                              会使用到sys_call_table(call.S )
    用户空间编程 fd=open("aa.txt",..);
                                  5->寄存器
                                  SWI
                             ----------------------
                                 vector_swi
                                       sys_call_tablep[5]
                                       sys_open
                                       SYSCALL_DEFINE3(open,const char __user*,........)//这是一个宏
添加一个新的系统调用
    1)添加新的内核函数
    vi arch/arm/kernel/sys_arm.c      文件最后添加
     asmlinkage int sys_add (int x ,int y)
    {
         printk("enter sys add!\n");
         return x+y;
     }
    2)更新头文件 arm/arm/include/asm/unistd.h
    vi arch/arm/include/asm/unistd,h
    nu:394 添加第366个系统调用
    #define     __NR_add     (__NR__SYSCALL_BASE+366)
    3)更新系统调用表(calls.S)
    vi arch/arm/kernel/calls.S
    CALL(sys_add)
    4)重新编译内核     make
    5)写一个用户程序测试该代码
    #include<stdio.h>
    //标准库中的实现使用以下的方法
    int add(int x.int y){
         return syscall(366,x,y);
    }

    int main(){
         int result=0;

         result=syscall(366,12,13);

         printf("sys_add call result =%d\n",result);
    }
    6)编译
    arm-linux-gcc calltest.c -o calltest
   
3.字符设备框架
 按照硬件特性分类
    字符设备(顺序读写,不带缓冲区)
    块设备(读写顺序不固定,带有读写缓冲)
    网络设备

struct cdev{
    struct kobject kobj;               //内嵌的kobject对象
    struct module *owner;          //所属模块
     struct file_operations *ops;//文件操作结构体
    struct list_head list;
     dev_t dev;                                   //设备号
    unsigned int count;
}
每一个cdv都有一个设备号
设备号(32bits)=主设备号(高12bits)+次设备号(低20bits)
    主设备号:代表一个类型的设备号
    次设备号:用于区分该设备中的不同个体
设备号的选取
    ->静态分配
         看看现在内核中哪些主设备号没有被使用,选择一个使用
         cat /proc/devices
         Documentation\devices

   
   
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/fs.h>
  4. MODULE_LICENSE("GPL");
  5. #define CDD_MAJOR 200
  6. #define CDD_MINOR 0
  7. #define CDD_COUNT 1
  8. dev_t dev=0;//dev_t 就是unsigned int为了内核方便的移植
  9. int __init cdd_init(void){
  10. int ret=0;
  11. /*生成一个设备号*/
  12. //dev = CDD_MAJOR<<20 +CDD_MINOR;
  13. dev=MKDEV(CDD_MAJOR,CDD_MINOR);
  14. /*
  15. * 注册设备号
  16. *ret =register_chrdev_region(dev_t from ,unsigned int count,char* name);
  17. *from.要注册的起始设备号
  18. *count,连续注册的设备号个数
  19. *name,the name of the device or driver
  20. *失败返回负数
  21. */
  22. ret =register_chrdev_region( dev ,CDD_COUNT,"cdddemo");
  23. if(ret<0){
  24. printk("register_chrdev_region failed\n");
  25. goto failure_register_chrdev;
  26. }
  27. return 0;
  28. failure_register_chrdev:
  29. return ret;
  30. }
  31. void __cdd_exit(void){
  32. /*注销设备号*/
  33. unregister_chrdev_region(dev,CDD_COUNT);
  34. }
  35. module_init(cdd_init);
  36. module_exit(cdd_exit);

    ->动态分配 
   
   
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/fs.h>
  4. MODULE_LICENSE("GPL");
  5. #define CDD_MAJOR 200
  6. #define CDD_MINOR 0
  7. #define CDD_COUNT 1
  8. dev_t dev=0;
  9. u32 cdd_major=0;
  10. u32 cdd_minor=0;
  11. int __init cdd_init(void){
  12. int ret =0;
  13. if(cdd_major){
  14. //静态方式分配设备号
  15. dev=MKDEV(CDD_MAJOR,CDD_MINOR);
  16. ret =register_chrdev_region(dev,CDD_COUNT,cdd_demo);
  17. }else{
  18. /*动态分配设备号
  19. *
  20. *
  21. * ret =all_chrdev_region(dev_t* dev,unsigend bassminor,
  22. count,const char* name)
  23. */
  24. ret =all_chrdev_region(&dev,cdd_minor,0,CDD_COUNT,cdd_demo);
  25. }
  26. if(ret<0){
  27. prink("register_chrdev_region failed!\n");
  28. goto failure_register_chrdev;
  29. }
  30. /*获取主设备号*/
  31. cdd_major=MAJOR(dev);
  32. return 0;
  33. failure_register_chrdev:
  34. return ret;
  35. }
  36. void __exit cdd_exit(void){
  37. unregister_chrdev_region(dev,CDD_COUNT);
  38. }
  39. module_init(cdd_init);
  40. moudle_exit(cdd_exit);

cdev的操作函数
   void  cdev_init(struct cdev*,struct file_operations*)//初始化cdev
   struct cdev* cdev_alloc(void);
   void cdev_put(struct cdev *p);
   int  cdev_add(struct cdev* ,dev_t ,unsigned );//添加到内核中
   void cdev_del(struct cdev *);

struct file_operations *fopen{}
   
  
  
  1. #include<linux/init.h>
  2. #include<linux/module.h>
  3. #include<linux/fs.h>
  4. #include<linux/cdev.h>
  5. MODULE_LICENSE("GPL");
  6. #define CDD_MAJOR 200
  7. #define CDD_MINOR 0
  8. #define CDD_COUNT 1
  9. dev_t dev=0;
  10. u32 cdd_major=0;
  11. u32 cdd_minor=0;
  12. /*定义cdev类型的变量*/
  13. struct cdev cdd_cdev;
  14. struct file_operations cdd_fops={
  15. .owner = THIS_MODULE,
  16. };
  17. int __init cdd_init(void){
  18. int ret =0;
  19. if(cdd_major){
  20. //静态方式分配设备号
  21. dev=MKDEV(CDD_MAJOR,CDD_MINOR);
  22. ret =register_chrdev_region(dev,CDD_COUNT,cdd_demo);
  23. }else{
  24. /*动态分配设备号*/
  25. ret =all_chrdev_region(&dev,cdd_minor,CDD_COUNT,cdd_demo);
  26. }
  27. if(ret<0){
  28. prink("register_chrdev_region failed!\n");
  29. goto failure_register_chrdev;
  30. }
  31. /*获取主设备号*/
  32. cdd_major=MAJOR(dev);
  33. /*初始化cdev
  34. *cdev_int(struct cdev* dev,const struct file_operations* fops)
  35. */
  36. cdev_int(&dev,&cdd_fops) ;
  37. /*添加cdev到内核中
  38. *cdev_add(struct cdev*p,dev_t dev ,unsigned count)
  39. */
  40. ret=cdev_add(&cdev,dev,CDD_COUNT);
  41. if(ret<0){
  42. printk("cdev_add failed!\n");
  43. goto failure_cdev_add;
  44. }
  45. return 0;
  46. failure_cdv_add:
  47. unregister_chrdev_region(dev,CDD_COUNT);
  48. failure_register_chrdev:
  49. return ret;
  50. }
  51. void __exit cdd_exit(void){
  52. /*从内核中删除cdev*/
  53. cdev_del(&cdd_cdev);
  54. /*注销设备号*/
  55. unregister_chrdev_region(dev,CDD_COUNT);
  56. }
  57. module_init(cdd_init);
  58. moudle_exit(cdd_exit);
test.c
   
   
  1. #include<stdio.h>
  2. #include<fcntl.h>
  3. #include<stdlib.h>
  4. int fd =0;
  5. int main(void){
  6. fd=open("/dev/cdd",O_RDWR);
  7. if(fd<0){
  8. printf("open failed!\n");
  9. return -1;
  10. }
  11. printf("open successed fd =%d",fd);
  12. while(1);
  13. return 0;
  14. }
手工创建设备节点文件
mknod /dev/cdd                        248                 0
                          字符设备       主设备号     次设备号
lsmod 显示内核装载模块  insmod remod
ls /dev/cdd -l






 
         














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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值