查看: 1016|回复: 0

pcDuino Linux驱动开发三 -- 最简单的GPIO驱动

[复制链接]
  • TA的每日心情
    奋斗
    2022-9-16 05:52
  • 签到天数: 1368 天

    连续签到: 1 天

    [LV.10]以坛为家III

    发表于 2014-11-30 23:12:31 | 显示全部楼层 |阅读模式
    分享到:
    本着从易到难的过程,现有程序完全基于那个最精简的驱动程序框架来实现,也就是字符设备+寄存器操作的方式,以后有时间再改为总线设备驱动。
    pcDuino上有两个LED,TX和RX,就用它们来做实验了。

    下面上代码,可参照《pcDuino Linux驱动开发二 -- 最简单的驱动程序框架》这个最精简的框架来看看增加的部分,无非就是多了些寄存器的操作:

    • #include <linux/init.h>
    • #include <linux/module.h>
    • #include <linux/fs.h>
    • #include <linux/cdev.h>
    • #include <linux/device.h>
    • #include <asm/io.h>
    • #include <asm/uaccess.h>
    • #include "../A10.h"
    • /**********************************************************************/
    •      #define LED_TX            15
    •      #define LED_RX            16
    •      #define LED_ON(Pin)                     (*REG_PH_DAT &= ~(0x1 << Pin))
    •      #define LED_OFF(Pin)                     (*REG_PH_DAT |= (0x1 << Pin) )
    •      #define LED_REVERSE(Pin)     (*REG_PH_DAT ^= (0x1 << Pin) )
    •      volatile static unsigned long* REG_PH_CFG1;
    •      volatile static unsigned long* REG_PH_DAT;

    •      int Inited = 0;
    • int LED_open(struct inode* n, struct file* f)
    • {
    •      Inited++;
    •      if (Inited > 1) return Inited;
    •      REG_PH_CFG1 = (volatile unsigned long*) ioremap(PH_CFG1, 4);
    •      REG_PH_DAT = (volatile unsigned long*) ioremap(PH_DAT, 4);

    •      *REG_PH_CFG1 &= ~(0xF << 28);
    •      *REG_PH_CFG1 |= (0x1 << 28);
    •      LED_OFF(LED_TX);
    •      LED_OFF(LED_RX);

    •      printk("Open Finished!\n");
    •      return 0;
    • }
    • ssize_t LED_write(struct file* f, const char __user* buf, size_t len, loff_t* l)
    • {
    •      unsigned char Buf[2];

    •      if (copy_from_user(&Buf, buf, len)) return -1;

    •      if ((Buf[0] != LED_TX) && (Buf[0] != LED_RX)) return -1;
    •      switch (Buf[1])
    •      {
    •             case 0:
    •                  LED_OFF(Buf[0]);
    •                  break;
    •             case 1:
    •                  LED_ON(Buf[0]);
    •                  break;
    •             case 2:
    •                  LED_REVERSE(Buf[0]);
    •                  break;
    •             default:
    •                  return -1;
    •      }
    •      return len;
    • }
    • ssize_t LED_read(struct file* f, char __user* buf, size_t len, loff_t* l)
    • {
    •      //int Buf;
    •      //copy_to_user(&Buf, buf, sizeof(int));

    •      printk("Read Finished!\n");
    •      return 0;
    • }
    • int LED_release(struct inode* n, struct file* f)
    • {
    •      Inited--;
    •      if (Inited <= 0)
    •      {
    •             Inited = 0;
    •             return 0;
    •      }

    •      iounmap(REG_PH_CFG1);
    •      iounmap(REG_PH_DAT);

    •      printk("Release Finished!\n");
    •      return 0;
    • }
    • /**********************************************************************/
    •      #define DEV_NAME     "LED"
    •      #define DEV_COUNT     1

    •      static dev_t Dev_Num;
    •      static struct cdev* pDev;
    •      static struct class* pClass;

    •      static struct file_operations fops =
    •      {
    •             .owner = THIS_MODULE,
    •             .open = LED_open,
    •             .write = LED_write,
    •             .read = LED_read,
    •             .release = LED_release,
    •      };
    • static int __init LED_init(void)
    • {
    •      int Result;

    •      Result = alloc_chrdev_region(&Dev_Num, 0, DEV_COUNT, DEV_NAME);
    •      if (Result)
    •      {
    •             printk("alloc_chrdev_region fail!\n");
    •             return Result;
    •      }

    •      pDev = cdev_alloc();
    •      if (pDev == NULL)
    •      {
    •             printk("cdev_alloc fail!\n");
    •             unregister_chrdev_region(Dev_Num, DEV_COUNT);
    •             return -1;
    •      }

    •      cdev_init(pDev, &fops);
    •      Result = cdev_add(pDev, Dev_Num, DEV_COUNT);
    •      if (Result)
    •      {
    •             printk("cdev_add fail!\n");
    •             cdev_del(pDev);
    •             return Result;
    •      }

    •      pClass = class_create(THIS_MODULE, DEV_NAME);
    •      if (pClass == NULL)
    •      {
    •             printk("class_create fail!\n");
    •             cdev_del(pDev);
    •             unregister_chrdev_region(Dev_Num, DEV_COUNT);
    •             return -1;
    •      }

    •      device_create(pClass, NULL, Dev_Num, "%s", DEV_NAME);
    •      printk("Init Finished!\n");
    •      return 0;
    • }
    • static void __exit LED_exit(void)
    • {
    •      if (pClass)
    •      {
    •             device_destroy(pClass, Dev_Num);
    •             class_destroy(pClass);
    •      }

    •      if (pDev)
    •      {
    •             cdev_del(pDev);
    •      }

    •      unregister_chrdev_region(Dev_Num, DEV_COUNT);
    •      printk("Exit Finished!\n");
    • }
    • MODULE_LICENSE("GPL");
    • MODULE_AUTHOR("LiuYang");
    • module_init(LED_init);
    • module_exit(LED_exit);
    复制代码
    这里没有实现Read函数,可以略过。
    make 后生成LED.ko,使用insmod LED.ko加载模块,可以在/dev文件夹中看到,或者使用cat /proc/devices也可以看到。
    调用rmmod LED后,该设备驱动模块被卸载,使用dmesg | tail -10来观察各个接口的加载情况,调试用。
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-12-19 00:35 , Processed in 0.114956 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.