LINUX设备模型

15 篇文章 0 订阅
12 篇文章 0 订阅
一,基于总线的编程
    platform_bus 总线:是一条虚拟总线,外设通过总线与cpu相连。所有片内外设设备,通过smc与cpu相连的设备可以使用platfrom_bus总线。
    platform_bus_type :
       platform_driver:驱动的一个结构体代表驱动(去寻找 platform_device)eg:driver.name="a"
       platform_device:描述一个设备携带所有硬件相关信息eg:.name="a"
平台代码:用来描述一个板子, platform_device放在这里。现在使用设备树
设备树描述一个板子以/{}开头 node为一级节点代表连接每个总线上的设备(控制器节点),二级节点child-Node
/{
    node{
        child-Node{
        };
    };
    nodes;
}


基于platform总线的设备驱动:

struct platform_device
{
  const char *name;  设备的名字
  int id; 设备id
  struct resource *resource;  资源列表
  1.  num_resource; 资源个数
  ....
}

  name 是platform_device和platform_driver的匹配的依据
  id 是用来区分是第几个设备,如果只有一个设备则为-1
  resource 描述设备所使用的硬件资源
      struct resource {
        resource_size_t start;  资源的开始
        resource_size_t end;    资源结束
        unsigned long flags;    资源的类型
        ...
      }

      资源类型:
      IORESOURCE_MEM  内存资源
      IORESOURCE_IRQ  中断资源
      IORESOURCE_DMA  DMA资源
  num_resource  表示resource结构体数组的长度一般用ARRAY_SIZE取长度

  注册platform_device
  int platform_device_register(struct platform_device *)


struct platform_driver
{
  struct device_driver driver;
  int (*probe)(struct platform_device *);
  int (*revmoe)(struct platofrm_device *);
}

  driver.name 驱动名字,是platform_device和platform_driver的匹配的依据
  probe
  remove

  注册platformm_drvier
  int platform_driver_register(struct platform_driver *);

  probe 当platform_device和platform_driver匹配后会被执行,且platform_device以形参的形式传递到probe函数中,
      着这个函数中完成设备的初始化

  ‘remove 当注销platform_driver的时候调用,前提是platform_device和platform_driver匹配了
      与probe 相反


Linux内核中断机制:
    <linux/interrupt.h>
 int __must_check request_irq(
        unsigned int irq,
        irq_handler_t handler,
        unsigned long flag,
        const char *name,
        void *dev
      );

      参数:
      irq : 中断号---软件概念
        使用平台代码时:设备号定义在arch/arm/mach-xxxx/include/mach/irqs.h
        使用设备树时:由设备树中节点的属性interrupts转换而来,通过platfrom_device获得

      handler:中断处理函数
      例:
      irqreturn_t handler(int irq, void *dev)
      {
        /*处理中断*/
        return IRQ_HANDLED;
      }

      IRQ_NONE:?

      flag:中断类型 | 触发方式
       中断类型:
          IRQF_DISABLE :当中断处理函数执行期间,屏蔽全局中断
          IRQF_SHARED:允许多个设备驱动注册同一个中断
       触发方式:只针对外部中断
          IRQF_TRIGGER_FALLING
          IRQF_TRIGGER_RISING
          IRQF_TRIGGER_HIGH
          IRQF_TRIGGER_LOW
          IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING

      name: 驱动名或设备名

      dev:1、传参,这个变量在中断处理函数执行时被以形参的形式传递到中断处理函数中,不传则为NULL
           2、共享中断中,这个参数必须有,并且需要具备唯一性,用来做身份识别,为free_irq服务

    返回值:必须校验
        0:成功
        -errno:失败

    void free_irq(unsigned int irq, void *dev);
          irq: 中断号
          dev:必须与reqeust_irq所传的第五个参数一直,尤其是在共享中断时



按键驱动:
  1、原理图
   KEY2==>GPX1_1==>EINT9
   KEY3==>GPX1_2==>EINT10

   下降沿触发

  2、修改设备树
  arch/arm/boot/dts/exynos4412-fs4412.dts
  添加如下内容:
  fs4412-key {
    compatible = "fs4412,key";
    interrupt-parent = <&gpx1>;
    interrupts = <1 2>, <2 2>;
  };
  在内核启动过程中被转换为==>
  struct resource fs4412_key_resource[] = {
    [0] = {
      .start = EINT9中断号,
      .end = EINT9中断号,
      .flags = IORESOUCE_IRQ,
    },
    [1] = {
      .start = EINT10中断号,
      .end = EINT10中断号,
      .flags = IORESOUCE_IRQ,
    },
  };

  struct platform_device fs4412_key_device = {
     .name = "fs4412-key",
     .id = -1,
     .resrouce = fs4412_key_resource,
     .num_resoruce  = ARRAY_SIZE(fs4412_key_resource);
  };


  3、写驱动
  许可证声明
  构建platform_driver
  static const struct of_device_id test_of_match[] = {
    {.compatible = "fs4412,key"},
    {/*noting to done!*/},
  };
  MODULE_DEVICE_TABLE(of, test_of_match);

  struct platform_driver test_driver = {
    .probe = driver_probe,
    .remove = driver_remove,
    .driver = {
      .name = "fs4412-key",
      .of_match_table = of_match_ptr(test_of_match),
    },
  };

  加载函数
    注册platform_driver---->platform_driver_register
  卸载函数
    注销platform_driver---->platform_driver_unregiser

  实现platform_driver中的probe方法

  static int driver_probe(struct platform_device *pdev)
  {
      申请设备号
      注册设备
      初始化设备
      获取中断号 pdev->resrouce[0].start X
                 pdev->resource[1].start X
      一般做法:
        struct resource *platform_get_resource(struct platform_device *pdev, unsigned int type, unsigned int num);
            作用:从pdev中获得第num个类型为type的资源

        struct resource *key1_resouce = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        struct resource *key2_resouce = platform_get_resource(pdev, IORESOURCE_IRQ, 1);

        中断号分别为key1_resouce->start, key2_resource->start

        request_irq(key1_resource->start, handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, "key", NULL);

  }

  driver_remove
    与probe相反

  写中断处理函数
        irqreturn_t handler(int irq, void *dev)
      {
        /*处理中断*/
        printk("irq = %d\n", irq);
        return IRQ_HANDLED;
      }

  为应用程序提供接口
    read -- 阻塞读


工作队列的实现

内核定时器
获取当前时间


下半部机制属于延迟机制

Linux内核下半部实现机制:
  内核线程
  软中断
  tasklet
  workq ueue

  tasklet 
  1、定义tasklet结构
    struct tastlet_struct my_struct;
  2、写下半部函数
    void func(unsigned long)
  3、初始化---将tasklet结构与下半部函数关联
    tasklet_init(struct tasklet_struct *, void (*func)(unsigned long), unsigned long);
  4、调度下半部函数---在上半部最后阶段执行,下半部函数在上半部函数返回后,会被执行
    tasklet_schedule(struct tasklet_struct *);

  workqueue
  1、定义workqueue结构
    struct work_struct my_struct;
  2、写下半部函数
    void func(struct work_struct *)
  3、初始化---将tasklet结构与下半部函数关联
    INIT_WORK(struct work_struct *, void (*func)(struct work_struct *));
  4、调度下半部函数---在上半部最后阶段执行,下半部函数在上半部函数返回后,会被执行
    schedule_work(struct work_struct *);


  tasklet 下半部代码执行在中断上下文
  workqueue 下半部代码执行在进程上下文


内核定时器:
  1、定义 
    struct timer_list list;
  2、初始化
    a) init_timer(struct timer_list *);==> init_timer(&list);

    b) list.function = myfunc;
  • st.expires = jiffies + 5 * HZ;

  3、激活定时器
    add_timer(struct timer_list *); ==> add_timer(&list);

  4、删除定时器
    del_timer(struct timer_list *); ==> del_timer(&list);


ADC驱动
1、原理图
  adcAIN3

2、芯片手册
  1. ADCCON  0x126C0000
  2. 1<< 0 | 0xff << 6 | 0x1 << 14 | 0x1 << 16

  ADCDAT 0x126C000C 用来存放转换后的数据-----注意:读出数据后必须清掉高位
        CLR  0x126C0018 写任意值清中断
        ADCMUX 0x126C001C 通道选择寄存器
  2、修改设备树
  arch/arm/boot/dts/exynos4412-fs4412.dts
  添加如下内容:
   fs4412-adc {
    compatible="fs4412,adc";   
reg=<0x126C0000 0x20>;
interrupt-parent=<&combiner>;
    interrupts=<10 3>;

  };
  在内核启动过程中被转换为==>
  struct resource fs4412_adc_resource[] = {
    [0] = {
      .start = 0x126C0000,
       .end= 0x126C0000 + 0x20,
      .flags = IORESOUCE_MEM,
    },
    [1] = {
      .start = ADC中断号,
      .end = ADC中断号,
      .flags = IORESOUCE_IRQ,
    },
  };

  struct platform_device fs4412_adc_device = {
     .name = "fs4412-adc",
     .id = -1,
     .resrouce = fs4412_adc_resource,
     .num_resoruce  = ARRAY_SIZE(fs4412_adc_resource);
  };

  3、写驱动
  许可证声明
  构建platform_driver
  static const struct of_device_id test_of_match[] = {
    {.compatible = "fs4412,adc"},
    {/*noting to done!*/},
  };
  MODULE_DEVICE_TABLE(of, test_of_match);

  struct platform_driver test_driver = {
    .probe = driver_probe,
    .remove = driver_remove,
    .driver = {
      .name = "fs4412-adc",
      .of_match_table = of_match_ptr(test_of_match),
    },
  };
  加载函数
    注册platform_driver---->platform_driver_register
  卸载函数
    注销platform_driver---->platform_driver_unregiser

  实现platform_driver中的probe方法

  static int driver_probe(struct platform_device *pdev)
  {
      申请设备号
      注册设备
      初始化设备

      获得资源
      一般做法:
        struct resource *platform_get_resource(struct platform_device *pdev, unsigned int type, unsigned int num);
            作用:从pdev中获得第num个类型为type的资源

        struct resource *irq_resouce = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        struct resource *mem_resouce = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    申请中断
    寄存器映射及初始化

  }


  driver_remove
    与probe相反

  写中断处理函数
        irqreturn_t handler(int irq, void *dev)
      {
        唤醒read
        /*处理中断*/
        清中断
        printk("irq = %d\n", irq);
        return IRQ_HANDLED;
      }

  为应用程序提供接口
       read -- 阻塞读
        开始转换---ADCCON、ADCMUX
        等待转换结束--睡眠
        读数据---ADCDAT
        将数据传递给上层



Linux下I2C驱动的编写:

i2c_msg构建:
1、例 写一个数据到i2c设备中
  设备寄存器地址为 0x20  
  1.  0x00 0x11

    消息个数由一个完整时序中开始为的个数决定
  unsigned char txbuf[3] = {0x20, 0x00, 0x11};
  struct i2c_msg msg[] = {
      {
        client->addr, 0/*表示写操作*/, 3, txbuf,
      },
  };

  i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));

2、例 从i2c设备的0x20地址读出一个数据,数据长度为16bit
  unsigned char txbuf[1] = {0x20};
 unsigned char rxbuf[2] = {0};
  struct i2c_msg msg[] = {
    {client->addr, 0, 1, txbuf},
    {client->addr, I2C_M_RD, 2, rxbuf},
  };

  i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_27205523

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值