字符设备驱动

1 引言

  • APP:open("/dev/xxx"),read,write    ——  设备字符驱动文件包含属性、主设备号和次设备号
  • C Library
  • System call interface
  • 内核:根据文件类型为字符设备,查找字符设备。根据主设备号查找file_operation;
  • 根据调用类型,选择不同的操作函数;

2 简单驱动程序

  • a. 定义file_operations结构体,然后填充该结构体,构造底层驱动函数;其中file_operations结构体定义在\include\linux\fs.h中。
  • b. 使用register_chrdev(主设备号,名字,结构体file_operations)在内核中注册该字符设备驱动;
  • c. 定义驱动入口函数fun1/fun2...,调用注册函数register_chrdev();
  • d. 用宏module_init(fun1),在内核中注册该字符设备;
  • e. 出口函数funx()调用unregister_chrdev();
  • f. 用宏module_exit(funx),从内核卸载该字符设备。

注:

  • APP调用接口时,调用字符设备时,主设备号查找外设类型。
  • Makefile基于内核编写,make后生成xxx.ko文件。
  • 使用insmod xxx.ko命令加载驱动;使用rmmod xxx卸载驱动;
  • 使用mknod /dev/xxx c 主设备号 次设备号创建字符设备;也可以使用程序class_create()/class_device_create()创建设备节点。
  • APP使用open("/dev/xxx",O_RWD)命令打开设备文件。

调试心得:

  1. Makefile文件中M=`pwd`语句,需要使用键盘按键中的~键,而不是“键;

3 查询机制

  • 软件框架
  • 操作硬件寄存器:将虚拟地址用ioremap()映射为物理地址,用iounmap()解除映射。
  • 使用copy_from_user从用户空间拷贝数据,使用copy_to_user拷贝数据到用户空间。

4 中断机制

  • 硬件中断信号产生;
  • CPU发生中断,跳到中断异常地址;
  • 调用中断处理函数,保存被中断现场,处理中断信号,恢复中断现成,退出
  • 中断。

注:

  使用request_irq()函数注册中断;

  使用exe 5</dev/xxxx命令打开中断;

  使用exec 5<&-命令关闭中断;

  使用free_irq()函数释放中断。

  wait_event_interruptible(wq, condition)函数使程序休眠

  wake_up_interruptible()唤醒程序

5 Polling机制

  在file_operations中定义一个poll的成员;

  在poll函数中调用poll_wait();

内核:

  - sys_poll

    - poll_initwait()

      - init_poll_funcptr()

        - __pollwait  将poll_wait的对象插入到poll队列中

    - do_poll()

      - do_pollfd()

      - 判断是否满足条件,如果满足,直接break

      - schedule_timeout()  定时休眠

Poll函数用法:

  include <poll.h>

  调用poll()函数

 6 异步通知

使用signal()框架:

应用程序:注册信号处理函数;

  调用signal(SIGIO, fun_pointer)

  调用open()函数,打开设备;

  调用fcntl(fd, F_SETOWN, pid)函数,告知驱动程序进程ID;

  调用oflags = fcntl(fd, F_GETFL);

  调用fcntl(fd, F_SETFL, oflags | FASYNC);改变fasync标记,最终调用驱动初始化或释放fasync:初始化/释放fasync_struct.

驱动程序:

  file_operations函数注册fasync系统调用,在该系统调用中初始化发信号时结构体fp;

中断:

  按键触发中断;

  调用kill_fasync(fp, sig, band)函数发送信号到应用层;

7 同步、互斥和阻塞

 原子操作框架:

  atomic_t v = ATOMIC_INIT(0);  定义原子变量v并初始化为0

  automic read(atomic_t *v);    返回原子变量的值

  void atomic_inc(atomic_t *v);   原子变量增加1

  void atomic_dec(atomic_t *v);   原子变量减少1

  int atomic_dec_and_test(atomic_t *v);  自减操作后测试其是否为0,为0则返回true,否则返回false。

信号量:

  定义信号量

    struct semaphore sem;

  初始化信号量

    void sema_init(struct semaphore *sem, int val);

    void init_MUTEX(struct semaphore *sem);

 

    static DECLARE_MUTEX(button_lock);  /*定义信号量并初始化*/

  获得信号量

    void down(struct semaphore *sem);

    int down_interruptible(struct semaphore *sem);

    int down_trylock(struct semaphore *sem);

  释放信号量

    void up(struct semaphore *sem);

阻塞

8 定时中断

1. 定义结构体timer_list

2. 在驱动入口函数中调用init_timer(),并且设置如下成员:

  function:中断函数,

  expires:设置中断定时时间(若不设置,默认为0,会立即进入一次中断处理函数),

  data:中断处理函数输入参数。

3. 调用add_timer()函数将定时器注册到内核中;

4. 调用mod_timer(),设置定时器超时时间:系统当前时间jiffies+超时时间。

  

  

posted @ 2019-06-15 00:28  寒霜未降  阅读(178)  评论(0)    收藏  举报