linux驱动开发之platform平台总线的编程(一)

痛定思痛。 2022-06-01 12:39 442阅读 0赞

总线分成三部分
bus
driver
device
什么时候用平台总线
1, 只要有设备的地址和中断都可以用平台总线
2, 如果写的驱动需要在多个平台中升级使用
3, 平台总线只是一个功能代码:将操作方法和操作资源进行了分离。
注意:平台总线不属于子系统,只是一个功能。
这里写图片描述
图1 platform总线模型
(1)平台总线:struct bus_type总线对象

  1. struct bus_type platform_bus_type = {
  2. .name = "platform",
  3. .dev_attrs = platform_dev_attrs,
  4. .match = platform_match, //匹配方法
  5. .uevent = platform_uevent,
  6. .pm = &platform_dev_pm_ops,
  7. };

(2)pdev

  1. // 描述一个设备的信息
  2. struct platform_device {
  3. const char * name; // 名字,用于匹配
  4. int id; // 表示不同寄存器组的编号, 一般可以填-1
  5. struct device dev; //父类
  6. u32 num_resources; //资源的个数
  7. struct resource * resource; //资源的详细信息--描述中断和内存资源
  8. };
  9. struct resource {
  10. resource_size_t start;
  11. resource_size_t end;
  12. const char *name;
  13. unsigned long flags;
  14. struct resource *parent, *sibling, *child;
  15. };
  16. 注册:
  17. int platform_device_register(struct platform_device *);
  18. 注销:
  19. void platform_device_unregister(struct platform_device *);

(3)pdrv

  1. struct platform_driver {
  2. int (*probe)(struct platform_device *); //表示匹配之后的函数
  3. int (*remove)(struct platform_device *); //解除匹配
  4. struct device_driver driver; //父类
  5. const struct platform_device_id *id_table; //可以匹配列表
  6. };

注册和注销:
extern int platform_driver_register(struct platform_driver *);
extern void platform_driver_unregister(struct platform_driver *);

下面写设备程序plat_led_dev.c

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/platform_device.h>
  4. //设备程序
  5. #define LED_GPC0_CONF 0xE0200060
  6. #define LED_GPC0_SIZE 8
  7. //这里描述多个设备
  8. struct resource led_resource[] = {
  9. [0] = {
  10. .start = LED_GPC0_CONF,
  11. .end = LED_GPC0_CONF + LED_GPC0_SIZE -1,//寄存器从0开始计算,所以要减1
  12. .flags = IORESOURCE_MEM, //表示内存资源
  13. },
  14. // 以下部分为演示部分
  15. [1] = {
  16. .start = 8888,
  17. .end = 8888, //中断是没有连续性的
  18. .flags = IORESOURCE_IRQ, //表示中断资源
  19. },
  20. [2] = {
  21. .start = 0xE0200160,
  22. .end = 0xE0200160 + 8 -1,
  23. .flags = IORESOURCE_MEM,
  24. },
  25. };
  26. struct platform_device led_pdev = {
  27. .name = "s5pv210_led", //只要保证与pdrv一致就行
  28. .id = -1,
  29. .num_resources = ARRAY_SIZE(led_resource), //求数组个数
  30. .resource = led_resource,
  31. };
  32. static int __init plat_led_dev_init(void)
  33. {
  34. printk("-------%s-----------------\n", __FUNCTION__);
  35. //注册一个pdev
  36. //int platform_device_register(struct platform_device *); //注册到总线上
  37. return platform_device_register(&led_pdev); //正常情况返回0
  38. }
  39. static void __exit plat_led_dev_exit(void)
  40. {
  41. printk("-------%s-----------------\n", __FUNCTION__);
  42. //void platform_device_unregister(struct platform_device *);//从总线上注销出来
  43. platform_device_unregister(&led_pdev); //一般卸载类的没有返回值
  44. }
  45. module_init(plat_led_dev_init);
  46. module_exit(plat_led_dev_exit);
  47. MODULE_LICENSE("GPL");
  48. MODULE_AUTHOR("752800373@qq.com");

接下来写驱动程序plat_led_drv.c
其中,platform_driver结构体中,
probe中要做事情:(下一个博客里面写)
//为用户提供接口
0, 实例化全局的设备对象– kzalloc
1, 申请主设备号—register_chrdev
2, 自动创建设备节点—class_create, device_create
3, 初始化硬件–ioremap
4,实现 file_operation
5,拿到pdev中的资源对硬件进行初始化

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/platform_device.h>
  4. #include <asm/io.h>
  5. //驱动程序
  6. volatile unsigned long *gpc0_conf;
  7. volatile unsigned long *gpc0_data;
  8. //led_drv_probe中主要任务是拿到资源
  9. //匹配成功第一个要执行的函数
  10. int led_drv_probe(struct platform_device *pdev)//这里的pdev指针是总线bus传过来的
  11. {
  12. printk("-------%s-----------------\n", __FUNCTION__);
  13. /* 编写驱动的套路(下次文件里面写) 0, 实例化全局的设备对象-- kzalloc 1, 申请主设备号---register_chrdev 2, 自动创建设备节点---class_create, device_create 3, 初始化硬件--ioremap 4,实现 file_operation */
  14. // 肯定拿到pdev中的资源对硬件进行初始化
  15. //获取到内存资源
  16. //参数2--拿到资源的类型
  17. //参数3--相同资源中第几个,这里是第0个,记住是同一类的累加数字
  18. //platform_get_resource_byname(struct platform_device *, unsigned int, const char *)
  19. struct resource *addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  20. //ioremap第二个参数等价于addr_res->end - addr_res->start + 1
  21. gpc0_conf = ioremap(addr_res->start, resource_size(addr_res));
  22. gpc0_data = gpc0_conf + 1;
  23. //如果要对硬件进行初始化
  24. *gpc0_conf &= ~(0xff<<12);
  25. *gpc0_conf |= (0x11<<12);
  26. *gpc0_data |= (0x3<<3); //亮灯
  27. //如果要获取到中断资源(按键)
  28. //参数3--相同资源中第几个,这里是第0个,记住是同一类的累加数字
  29. //struct resource *irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  30. //printk("irqno = %d\n", irq_res->start);//打印出起始地址
  31. int irqno = platform_get_irq(pdev, 0);
  32. printk("irqno = %d\n", irqno);
  33. return 0;
  34. }
  35. int led_drv_remove(struct platform_device *pdev)
  36. {
  37. printk("-------%s-----------------\n", __FUNCTION__);
  38. return 0;
  39. }
  40. const struct platform_device_id led_id_table[] = {
  41. {
  42. "s5pv210_led", 0x5210},//一定要和pdev里面的name一致,这里优先匹配
  43. {
  44. "s3c2410_led", 0x4444},
  45. {
  46. "s3c6410_led", 0x4444},
  47. };
  48. #if 0
  49. //用来参考的
  50. struct platform_driver {
  51. int (*probe)(struct platform_device *);
  52. int (*remove)(struct platform_device *);
  53. struct device_driver driver;
  54. const struct platform_device_id *id_table;
  55. };
  56. #endif
  57. struct platform_driver led_pdrv = {
  58. .probe = led_drv_probe, //表示匹配之后的函数
  59. .remove = led_drv_remove, //解除匹配
  60. .driver = {
  61. .name = "samsung_led_drv", //随便写,但是一定要有
  62. //可以用于和pdev进行匹配的,优先级较id_table低
  63. },
  64. .id_table =led_id_table , //一定是用于和pdev进行匹配, 优先进行匹配
  65. };
  66. static int __init plat_led_drv_init(void)
  67. {
  68. printk("-------%s-----------------\n", __FUNCTION__);
  69. //注册一个pdrv
  70. //extern int platform_driver_register(struct platform_driver *);
  71. return platform_driver_register(&led_pdrv);
  72. }
  73. static void __exit plat_led_drv_exit(void)
  74. {
  75. printk("-------%s-----------------\n", __FUNCTION__);
  76. platform_driver_unregister(&led_pdrv);
  77. }
  78. module_init(plat_led_drv_init);
  79. module_exit(plat_led_drv_exit);
  80. MODULE_LICENSE("GPL");
  81. MODULE_AUTHOR("752800373@qq.com");

Makefile

  1. #指定内核源码的绝对路径
  2. KERNEL_DIR = /home/ubuntu/s5pv210/kernel/linux-3.0.8
  3. CUR_DIR = $(shell pwd)
  4. MYMODULE = plat_led_dev
  5. MYMODULE2 = plat_led_drv
  6. #MYAPP = buttons_v5
  7. all:
  8. #make进入到内涵源码目录,将当前目录下的源程序作为内核模块一起编译
  9. make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
  10. #arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c
  11. clean:
  12. #将编译生成的文件删除
  13. make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
  14. rm -rf (MYAPP)
  15. install:
  16. cp -raf $(MYAPP) *.ko /opt/rootfs/drv_module
  17. #指定编译哪个源文件
  18. obj-m = $(MYMODULE).o
  19. obj-m += $(MYMODULE2).o

这里,在开发板转载程序,可以不分先后顺序。
insmod plat_led_dev.ko
insmod plat_led_drv.ko
这里写图片描述
并且可以看到开发板上的两个LED灯亮了。
可在/sys/bus/platform/中查看程序是否成功加载到总线
程序代码:led_plat_v1.rar(链接:https://pan.baidu.com/s/1i5XRNHr 密码:qbx2)

发表评论

表情:
评论列表 (有 0 条评论,442人围观)

还没有评论,来说两句吧...

相关阅读