驱动笔记

1.系统开发相关的内容
  uboot
  kernel
  rootfs
2.linux系统的划分
  用户空间
  内核空间
3.linux内核子系统 
4.linux模块开发的特点
  七大注意事项
5.加载函数,卸载函数 
  insmod/modprobe    rmmod 
  lsmod modinfo
6.编译模块
  Makefile
7.模块信息
  MODULE_LICENSE("GPL");
  
8.设置内核默认的输出级别的方法:
  方法1:
  修改/proc/sys/kernel/printk文件
  echo 8 > /proc/sys/kernel/printk 打印所有的信息
  echo 4 > /proc/sys/kernel/printk 打印输出小于4的级别的信息
  方法2:方法1无法解决设置内核启动时候的输出信息
  在uboot的bootargs中设置默认的输出级别
  debug/quiet/loglevel=数字(级别)
 
  setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs 
  ip=192.168.1.6:192.168.1.8:192.168.1.1:255.255.255.0::eth0:on 
  init=/linuxrc console=ttySAC0,115200 debug //级别为10
 
  setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs 
  ip=192.168.1.6:192.168.1.8:192.168.1.1:255.255.255.0::eth0:on 
  init=/linuxrc console=ttySAC0,115200 quiet //级别为4
 
  setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/opt/rootfs 
  ip=192.168.1.6:192.168.1.8:192.168.1.1:255.255.255.0::eth0:on 
  init=/linuxrc console=ttySAC0,115200 loglevel = 4
 
 案例:要求在加载驱动模块时,点亮所有的灯
       要求在卸载驱动模块时,关闭所有的灯
       
       mount -t vfat /dev/sda1 /mnt
       cd /mnt
       ls 查看u盘的信息


总结:
1.内核模块参数
  作用:在加载模块和加载模块以后,能够
  给模块传递相应的参数信息
  module_param
  module_param_array
  权限问题:
  非0:在/sys/module/模块名/paramters/文件
  通过修改这个文件完成对变量的内容修改
  问题:会占用内存的资源
  
  权限为0:就不会有一个文件存在,只能在模块
  加载的时候才能修改


2.模块的符号导出
  作用:将函数和变量导出,供其他模块使用
  EXPORT_SYMBOL
  EXPORT_SYMBOL_GPL
  前者任何模块都能使用,后者只能给遵循GPL协议的
  模块使用,所有要求模块编程时,一律添加:
  MODULE_LICENSE("GPL");
  
3.printk
  能够指定打印输出的级别:8级
  数字越小,级别越高
  一般linux系统都有一个默认的输出级别:
  如何配置默认的输出级别:
  修改/proc/sys/kernel/printk
  在uboot中指定:debug quiet loglevel=数字


4.linux系统调用的原理和实现SCI
  作用:
  1.为用户提供统一的硬件抽象层
   操作一个文件,无需关注这个文件存在
   硬盘上,SD卡,U盘,只需调用open,read,
   write等函数操作即可
  2.安全保护
  原理:
  1.应用程序调用open
  2.进程会调用C库的open函数的实现
  3.C库的open实现会将open对应的系统调用号
  保存在寄存器中
  4.C库的open实现会调用swi(svc)触发一个软中断
  异常
  5.这时进程就会跳转到内核预先指定的一个位置
  6.对应的位置就在内核定义好的异常向量的入口
   vector_swi
  7.这个函数会根据系统调用号,在内核预先
  定义好的一个系统调用表中找到对应的open的
  内核实现(sys_open)
  8.找到这个函数以后,执行这个函数
  9.执行完毕以后,原路返回给用户空间
 
 如何添加一个系统调用:
  1.在内核代码中arch/arm/kernel/sys_arm.c
  添加一个系统调用的内核实现sys_add
  2.在内核代码中arch/arm/include/asm/unistd.h
  中添加一个新的系统调用号__NR_add
  3.在内核代码中arch/arm/kernel/calls.S
  中的系统调用表sys_call_table中添加
  一个项:CALL(sys_add)
  4.在用户空间调用syscall完成调用新添加的
  系统调用实现sys_add
  注意:syscall这个函数本身会帮你实现调用
  swi(svc),你只需传递一个系统调用号


总结:一个进程从用户空间到内核空间的转换靠软中断!
  
5.linux内核提供的GPIO操作的库函数
  CPU的GPIO对于内核来说是一种资源,
  这种资源新式是以软件编号的新式存在
  S5PV210_GPC0(3).....
  gpio_request
  gpio_direction_output
  gpio_direction_input
  gpio_set_value
  gpio_get_value
  gpio_free
  
  
    1.linux设备驱动分类
  按管理的设备硬件来分:
  字符设备
  按字节流来访问,能够顺序访问,也能够
  指定位置的访问
  按键,串口,终端,触摸屏,LCD等
  块设备
  在unix系统下,块设备按一定的数据块进行
  访问,数据块为512字节,或者1K等
  在linux系统下,块设备即可按数据块进行
  访问,也可以按字节流访问,那么他和字符
  设备本质的区别在于linux系统描述块设备和
  字符设备的数据结构和操作方法是不一样的。
  硬盘,U盘,SD卡,TF卡,nandflash,norflash
 
  网络设备
  网卡,网络设备一般都要结合TCP/IP协议栈
  来实现。
 
2.字符设备驱动
  驱动程序的作用:
  1.管理操作硬件
  2.给用户提供访问硬件操作的方法(接口)
    led_on
    led_off
    read_uart
    write_uart
    ....
  unix、linux:一切皆文件!
  问:应用程序如何访问硬件呢?
  答:硬件设备在linux系统下,会以设备文件的形式
  存在,设备文件(字符和块)在/dev/,那么应用程序
  要访问硬件其实就是对设备文件的访问。
  
  问:应用程序如何访问设备文件呢?
  答:通过调用系统调用函数来实现对其访问,访问
  设备文件和访问普通的文件的方式是一样的
  open,read,write,ioctl,mmap,close...
  
  问:应用程序通过设备文件如何在茫茫的内核驱动
  代码中找到自己对应的驱动程序呢?
  答:设备文件本身包含了一些属性:设备文件是字符
  设备文件(c)还是块设备文件(b),
  还包括了主设备号和次设备号这两个重要的属性。
  应用程序就是根据主设备号找到对应的驱动程序。
  一个驱动程序只有一个主设备号(进行绑定)。
  问:次设备号的作用是什么?
  答:主设备号用于应用程序找到驱动程序;
      次设备号用于应用程序找到具体要操作访问的
      设备个体,比如:
      S5PV210处理器有4个串口,只需一个串口驱动
      来管理即可,四个串口共享一个主设备号,
      应用程序通过次设备号来分区要访问的串口
      
设备号:主设备号和次设备号
数据类型:
dev_t (unsigned int)
高12位:主设备号
低20位:次设备号
设备操作宏
MAJOR
MINOR
MKDEV
如果要实现一个驱动和设备号的绑定,首先需要
向内核申请设备号资源,只有完成申请以后,才能
进行后续的绑定工作!


问:如何向内核申请设备号呢?
答:静态分配和动态分配
静态分配
1.首先通过cat /proc/devices查看linux系统中哪个主设备号没有
被占用
Character devices: 字符设备
  主设备号 设备名称 
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0 
  ...
  Block devices: 块设备
259 blkext
  7 loop
    8 sd
  31 mtdblock
   
  2.然后根据你的设备个数分配次设备号,
  如果设备个数只有一个,一般次设备号从0开始
  dev_t dev_id = MKDEV(主设备号,次设备号);
   
  3.调用register_chrdev_region;向内核
  申请即可
  4.如果主设备号为0,静态分配失败。
动态分配
1.调用alloc_chrdev_region直接向内核去
申请设备号,也就是让操作系统内核帮你分配
设备号。

释放设备号:
unregister_chrdev_region将设备号资源
归还操作系统内核

案例:要求驱动程序能够动态选择静态分配和动态分配
    两种方法实现设备号的申请
      提示:采用模块参数的方法


<<linux设备驱动程序>>第三版
<<linux内核设计与实现>>第三版


四个重要的数据结构:
1.struct file 
  作用:描述文件打开以后的状态属性
  生命周期:从open打开成功由内核创建
            到close关闭文件时进行销毁
  重要的成员:
  struct file_operations *f_op;//指向驱动
  程序中实现的各个硬件操作方法
  f_op = &led_fops;
  unsigned int f_flags;//文件的操作属性
  loff_t f_ops; //文件操作位置
  void *private_data; //??
  
总结:设备文件的操作方法最终来源于f_op
他指向驱动中的操作集合。


struct inode
   作用:描述一个文件的物理属性
   生命周期:文件存在,内核创建
             文件销毁,内核销毁对应的inode
   重要成员:
    dev_t i_rdev; //存放设备号
    struct cdev *i_cdev; //指向一个字符设备
   
一个文件只有一个inode,可以有多个file    


问:struct file和struct file_operations如何关联:
答:1.当应用程序调用open时,最终调用sys_open
    2.sys_open创建struct file结构体内存,描述
    打开的文件信息
    3.通过某种机制<?>获取到驱动的struct file_operations
    然后将驱动的file_operations的地址赋值给
    struct file的f_op
    4.sys_open最后再调用驱动的file_operations里的
    open函数,这个open指针指向驱动的led_open
    
问:应用程序如何read,write设备呢?
答:由于对设备的访问总是先open,一旦先open,就需要
做上面的过程,一旦过程执行,file和底层驱动的
file_operations就进行了关联,
以后read,write最终:
read->sys_read->file->f_op->read = led_read
write->sys_write->file->f_op->write = led_write


问:如何将驱动的file_operations注册到内核中?
问:应用程序如何通过设备号找到驱动程序的?
一旦找到驱动程序,就等于找到file_operations
答:struct cdev


问:应用程序如何通过系统调用函数完成对硬件设备的访问?
答:
1.编写安装字符设备驱动程序
  1.1 分配初始化struct file_operations 
  struct file_operations led_fops = {
  .open = led_open,
  .release = led_close,
  .read = led_read,
  ...
  };
  1.2 分配初始化struct cdev
      struct cdev led_cdev;
      cdev_init(&led_cdev, &led_fops);
      结果就是led_cdev.ops = &led_fops
      cdev_add(&led_cdev, 设备号,设备个数);
      结果就是将led_cdev添加到内核的cdev的数组之中
      下标是以设备号为索引!
      一旦完成对cdev的注册,就等于有了一个真实
      的字符设备,关键这个驱动有了对应的操作
      集合=>led_fops


2.应用程序首先要open打开设备
  2.1 应用程序调用open打开设备文件(打开设备)
  2.2 调用C库的open实现
  2.3 C库的open实现会保存open的系统调用号
  2.4 C库的open实现调用swi触发一个软中断,跳转到
      内核态
  2.5 根据系统调用号,在系统调用表中找到open对应的
  内核系统调用实现sys_open
  2.6 调用sys_open,sys_open会做如下工作:
      1.通过inode.i_rdev获取设备号
      2.根据设备号在内核cdev数组中找到对应的
      字符设备驱动cdev(led_cdev)
      3.然后将找到的cdev的地址赋值给inode.i_cdev指针
        用于缓存和别的用途(?)
      4.创建struct file结构体内存用于描述打开的
      设备文件信息
      5.根据已经获得的cdev,从而获取其中的驱动
      操作集合ops(led_fops)
      6.将字符设备驱动的操作接口ops在赋值
      给file->f_op = &led_fops
      7.最后在调用一次file->f_op->open = led_open
      
3.后续就可以对设备进行read,write等操作
  app:read(fd, buf, size);
  kernel:
  sys_read:
  获取struct file
  获取上一次操作的偏移file->f_pos
  直接调用:
  file->f_ops->read(file,buf,size,file->f_pos)
结果:最终完成对底层read函数的调用

总结:完成一个字符设备驱动主要对cdev和struct file_operations进行操作


案例:要求应用程序open设备时,打开所有的灯
      要求应用程序close设备时,关闭所有的灯
   
1.添加头文件
2.添加加载函数
申请设备号
分配初始化struct file_operations
.open = led_open
.release = led_close
分配初始化注册cdev
申请GPIO资源

3.添加卸载函数
释放GPIO资源
卸载cdev
释放设备号
实验步骤:
1.编译驱动模块
2.交叉编译应用程序
  arm-linux-gcc -o led_test led_test.c
3.insmod led_drv.ko
4.cat /proc/devices //查看动态申请的主设备号
5.创建设备节点
  mknod /dev/myled c 250 0    
6../led_test //查看打印信息和灯的开关状态
7.优化代码


app:read
kernel:
sys_read->led_read:
函数原型:
ssize_t (*read)(struct file *filp, 
char __user *buf, 
size_t count, 
loff_t *f_pos);
切记:buf在内核空间不能直接使用,buf指向用户空间
      *buf = 1(不允许)
      必须利用copy_to_user完成数据由内核空间
      到用户空间的转移


案例:要求用户写1,开灯
      要求用户写0,关灯
      int cmd;
      int stat;
      cmd = 1;
      write(fd, &cmd, 4);
      read(fd, &stat, 4);
      
      cmd = 0;
      write(fd, &cmd, 4);
      读取灯的状态:1表示灯亮,0表示灯灭


案例:指定其中一盏的操作
提示:在用户和内核定义一个结构体
struct led_cmd {
int cmd; //指定开关命令
int index;//指定灯的编号
};


int (*ioctl)(struct inode *inode, 
struct file *filp, 
unsigned int cmd, 
unsigned long arg);
app:
ioctl(fd, LED_ON);//仅仅发送命令
或者
int index = 2;
ioctl(fd, LED_ON, &index);//发送命令和写入输入到设备


driver:
cmd:对应的就是应用程序的LED_ON
arg:存放的就是用户空间的缓冲区地址&index
所以在内核空间不能直接对arg访问,
需要使用copy_from_user....







回顾:
1.设备节点、设备文件
  问:设备文件干什么用?
  答:用于表示设备,用户访问设备文件,就是在访问设备
  问:设备文件在哪里?
  答:在/dev
  问:设备文件如何创建?
  答:手工创建mknod /dev/设备文件名 <c|b> 主设备号 次设备号
      自动创建
      
2.设备号
  问:设备号包含什么内容?
  答:包含主,次设备号
  问:主,次设备号的作用?
  答:应用程序通过主设备号找到驱动程序
      应用程序通过次设备号找到同类型设备的不同个体,比如串口
  问:由于设备号对于内核来说是一种资源,如何向内核申请?
  答:静态申请和动态申请
      静态申请:事先查看当前系统里哪个主设备号是空闲的
      优点是可以提前创建好设备文件,缺点不便于驱动的推广
      动态申请:让内核帮你申请一个设备号
      优点是便于驱动的推广,缺点不能提前创建好设备节点
  问:设备号数据类型是什么?
  答:dev_t 本质上是一个unsigned int 
      12-》主
      20-》次
      MAJOR
      MINOR
      MKDEV
  
3.字符设备四个重要数据结构
  struct inode:
  问:这个结构描述什么内容?
  答:用于描述一个文件的物理特性,一个文件有一个对应的inode
  问:生命周期?谁来创建?
  答:文件存在,它存在,文件销毁,它销毁;
      内核创建
  问:有哪些重要的成员?
  答:i_rdev(存放设备文件对应的设备号信息)
      i_cdev(如果这个设备文件是字符设备文件,
      它存放的就是字符设备驱动对应的数据结构cdev)
      用于缓存
      
  struct file
  问:描述什么属性?
  答:描述设备文件被成功打开以后的状态属性
  问:生命周期?谁来创建?
  答:文件打开以后,内核创建
      文件关闭以后,内核进行销毁
  问:有哪些重要的成员?
  答:f_op(存放底层驱动的硬件操作集合)
      f_flags(存放应用程序调用open时指定的操作属性,O_RDWR|O_NONBLOCK)
      f_pos(存放文件操作的位置信息)
      private_data文件私有数据?
      
  struct cdev
  问:描述什么内容?
  答:描述一个字符设备
  问:生命周期?谁来创建?
  答:由驱动程序在加载函数中完成cdev的分配,初始化和注册工作
      由驱动程序在卸载函数中完成cdev的删除操作,一旦删除
      ,内核就不再存在一个字符设备设备驱动
  问:有哪些重要成员?
  答:ops(存放底层驱动的硬件操作集合)
      dev(存放设备号)
      count(存放设备的个数)
  问:cdev涉及的相关操作函数
  答:cdev_init
      cdev_add
      cdev_del
  问:如何完成cdev的注册过程?
  答:当调用cdev_add时,根据设备号,将cdev
  添加到内核事先定义好一个cdev的数组中,以后
  应用程序就可以通过设备号来访问这个数组,获取
  对应的cdev,也就是获取到对应的字符设备驱动
        
  struct file_operations
  问:描述什么内容?
  答:它仅仅就是包含了一堆的硬件操作的接口
  问:应用程序如何和这些接口对应的呢?
  答:一一对应
      open->sys_open->.open = x_open
      close->sys_close->.release = x_close
      read->sys_read->.read = x_read
      write->sys_write->.write = x_write
      ioctl->sys_ioctl->.ioctl = x_ioctl
  问:应用程序到sys_系统调用函数这个过程
  是内核已经帮你实现,sys_open->.open = x_open
  是如何关联的呢?
  答:答案参看day03.txt笔记
  
4.如何实现一个字符设备驱动


案例:按键驱动的一个版本
      要求:能够获取按键值
         按键按下,获取的键值为0x50
         按键松开,获取的键值为0x51
1.了解硬件原理
2.驱动编写过程
  1.分配初始驱动操作集合
    struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .release = btn_close,
    .read = btn_read
    };
  2.分配cdev
  3.在加载函数中
     分配设备号
     初始化cdev
     注册cdev
  4.在卸载函数中
    删除cdev
    释放设备号
    
  5.填充btn_open
     申请GPIO资源GPH0_0
     配置为输入口
  
  6.填充btn_close
     释放GPIO资源
  
  7.填充btn_read
    读取GPIO的管脚状态
    判断管脚状态
    如果是1:把0x51 copy_to_user到用户空间 
    如果是0:把0x50 copy_to_user到用户空间


实验步骤:
1.去除官方按键驱动
  make menuconfig //在内核源代码根目录
  Device Drivers->
  Input device support  ---> 
  [*]   Keyboards  ---> 
  [*]   S3C gpio keypad support //去掉
  make zImage
  cp arch/arm/boot/zImage /tftpboot
  重启开发板,用新内核引导


2.insmod btn_drv.ko
3.cat /proc/devices //查看申请主设备号
4.mknod /dev/buttons c 主设备号 0
5../btn_test 操作按键查看信息
6.去掉应用程序的打印信息
7../btn_test & //让应用程序后台运行
8.top //查看CPU的使用情况


案例:用ioctl实现读取按键信息


中断相关内容:
问:为什么有中断
答:由于外设的处理速度相对处理器比较慢,
    如果CPU采用轮训或者定期检查设备,浪费很多
    CPU的资源,做很多无用功。采用中断,可以最大
    的利用CPU


问:中断在硬件的连接方式是怎么样的?
答:外设->中断控制器->CPU
    首先外设操作,产生电信号,发送给中断控制器
    中断控制器能够检测和处理电信号,决定是否
    将信号发送CPU,如果发送给CPU,CPU相应这个
    电信号,做后续的中断处理


问:CPU进行中断的处理流程
    中断有优先级
    中断可以打断中断,当然也可以打断执行的进程
    中断异常向量表(内核启动时创建)
    保存现场
    处理中断(执行中断服务程序)
    恢复现场
    
问:内核如何实现中断编程的?
答:request_irq
    free_irq


问:内核要求中断处理程序应该注意哪些事项?
答:1.要求中断处理过程越快越好
    2.中断处理程序在内核空间,是随机执行,
      不隶属于任何进程,不参与进程的调度
    3.中断不能和用户空间进行数据的交互
      如果要交互,需要通过系统调用
    4.中断处理程序不能调用引起阻塞的函数
      copy_*
      
问:虽然理想状态是要求中断处理函数执行的越快越好,
但某些场合是无法满足这个要去的,比如网卡接收数据的过程,
如果网卡对应的中断处理程序长时间的占有CPU的资源,
就会影响系统的并发和响应能力,怎么办呢?


答:如果对于这种情况,可以采用顶半部+底半部的实现方法
其实就是将以前的中断处理程序分为两部分:
顶半部:就是中断处理程序,做一些比较紧急的事情,
比如将网卡数据包从网卡硬件缓冲区拷贝到主存中,
这个过程不可中断,然后一定要在顶半部中登记底半部
要做的事,CPU会在空闲,适当的时候再去执行底半部剩余的工作。


底半部:做一些不紧急,相对耗时的工作,比如将数据包
提交给协议层的过程,这个过程可以被别的新的中断所打断


问:底半部是如何的实现呢?
问:顶半部和底半部如何关联的呢?
答:底半部的实现机制如下:
tasklet
工作队列
软中断


问:tasklet怎么玩?
答:    


按键中断测试步骤:
1.insmod btn_drv.ko
2.cat /proc/interrupts //查看中断的注册信息
 中断号  中断触发的次数 中断类型     中断名称
            CPU0
 16:        137         s3c-uart      s5pv210-uart
 18:        273         s3c-uart      s5pv210-uart
 32:          0         s5p_vic_eint  KEY_UP
 
     

          




回顾:
1.设备节点、设备文件
  问:设备文件干什么用?
  答:用于表示设备,用户访问设备文件,就是在访问设备
  问:设备文件在哪里?
  答:在/dev
  问:设备文件如何创建?
  答:手工创建mknod /dev/设备文件名 <c|b> 主设备号 次设备号
      自动创建
      
2.设备号
  问:设备号包含什么内容?
  答:包含主,次设备号
  问:主,次设备号的作用?
  答:应用程序通过主设备号找到驱动程序
      应用程序通过次设备号找到同类型设备的不同个体,比如串口
  问:由于设备号对于内核来说是一种资源,如何向内核申请?
  答:静态申请和动态申请
      静态申请:事先查看当前系统里哪个主设备号是空闲的
      优点是可以提前创建好设备文件,缺点不便于驱动的推广
      动态申请:让内核帮你申请一个设备号
      优点是便于驱动的推广,缺点不能提前创建好设备节点
  问:设备号数据类型是什么?
  答:dev_t 本质上是一个unsigned int 
      12-》主
      20-》次
      MAJOR
      MINOR
      MKDEV
  
3.字符设备四个重要数据结构
  struct inode:
  问:这个结构描述什么内容?
  答:用于描述一个文件的物理特性,一个文件有一个对应的inode
  问:生命周期?谁来创建?
  答:文件存在,它存在,文件销毁,它销毁;
      内核创建
  问:有哪些重要的成员?
  答:i_rdev(存放设备文件对应的设备号信息)
      i_cdev(如果这个设备文件是字符设备文件,
      它存放的就是字符设备驱动对应的数据结构cdev)
      用于缓存
      
  struct file
  问:描述什么属性?
  答:描述设备文件被成功打开以后的状态属性
  问:生命周期?谁来创建?
  答:文件打开以后,内核创建
      文件关闭以后,内核进行销毁
  问:有哪些重要的成员?
  答:f_op(存放底层驱动的硬件操作集合)
      f_flags(存放应用程序调用open时指定的操作属性,O_RDWR|O_NONBLOCK)
      f_pos(存放文件操作的位置信息)
      private_data文件私有数据?
      
  struct cdev
  问:描述什么内容?
  答:描述一个字符设备
  问:生命周期?谁来创建?
  答:由驱动程序在加载函数中完成cdev的分配,初始化和注册工作
      由驱动程序在卸载函数中完成cdev的删除操作,一旦删除
      ,内核就不再存在一个字符设备设备驱动
  问:有哪些重要成员?
  答:ops(存放底层驱动的硬件操作集合)
      dev(存放设备号)
      count(存放设备的个数)
  问:cdev涉及的相关操作函数
  答:cdev_init
      cdev_add
      cdev_del
  问:如何完成cdev的注册过程?
  答:当调用cdev_add时,根据设备号,将cdev
  添加到内核事先定义好一个cdev的数组中,以后
  应用程序就可以通过设备号来访问这个数组,获取
  对应的cdev,也就是获取到对应的字符设备驱动
        
  struct file_operations
  问:描述什么内容?
  答:它仅仅就是包含了一堆的硬件操作的接口
  问:应用程序如何和这些接口对应的呢?
  答:一一对应
      open->sys_open->.open = x_open
      close->sys_close->.release = x_close
      read->sys_read->.read = x_read
      write->sys_write->.write = x_write
      ioctl->sys_ioctl->.ioctl = x_ioctl
  问:应用程序到sys_系统调用函数这个过程
  是内核已经帮你实现,sys_open->.open = x_open
  是如何关联的呢?
  答:答案参看day03.txt笔记
  
4.如何实现一个字符设备驱动


案例:按键驱动的一个版本
      要求:能够获取按键值
         按键按下,获取的键值为0x50
         按键松开,获取的键值为0x51
1.了解硬件原理
2.驱动编写过程
  1.分配初始驱动操作集合
    struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .release = btn_close,
    .read = btn_read
    };
  2.分配cdev
  3.在加载函数中
     分配设备号
     初始化cdev
     注册cdev
  4.在卸载函数中
    删除cdev
    释放设备号
    
  5.填充btn_open
     申请GPIO资源GPH0_0
     配置为输入口
  
  6.填充btn_close
     释放GPIO资源
  
  7.填充btn_read
    读取GPIO的管脚状态
    判断管脚状态
    如果是1:把0x51 copy_to_user到用户空间 
    如果是0:把0x50 copy_to_user到用户空间


实验步骤:
1.去除官方按键驱动
  make menuconfig //在内核源代码根目录
  Device Drivers->
  Input device support  ---> 
  [*]   Keyboards  ---> 
  [*]   S3C gpio keypad support //去掉
  make zImage
  cp arch/arm/boot/zImage /tftpboot
  重启开发板,用新内核引导


2.insmod btn_drv.ko
3.cat /proc/devices //查看申请主设备号
4.mknod /dev/buttons
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值