字符设备驱动程序之LED驱动程序

字符设备驱动程序之LED驱动程序
驱动系统架构:
①应用程序APP---调用open("/dev/xxx")、read()、write()
②系统调用接口(system call interface)----根据 SWI  传入的参数值的不同,即发生异常的原因,调用不同的异常处理函数。
③虚拟文件系统(VFS)----(sys_open,sys_read,....) 根据打开的不同设备文件,找到不同的驱动,调用相应的函数。例如,打开的是字符设备,则在内核定义的数组chrdev[ ]中查找,数组的索引是主设备号,内容是 file_operations 结构。
④硬件

问1:怎么写字符设备驱动程序?
答:
1. 写出open()、read()、write()等函数。
static int first_drv_open(struct inode *inode, struct file *file)
{
      //printk("first_drv_open\n");
      /*
       * LED1,LED2,LED4对应GPB5、GPB6、GPB7、GPB8
       */
      /* 配置GPB5,6,7,8为输出 */
      *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));
      *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
      return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
      int val;
      //printk("first_drv_write\n");
      copy_from_user(&val, buf, count); //      copy_to_user();
      if (val == 1)
      {
            // 点灯
            *gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));
      }
      else
      {
            // 灭灯
            *gpbdat |= (1<<5) | (1<<6) | (1<<7) | (1<<8);
      }      
      return 0;
}
2. 怎么告诉内核调用的是这些方法?
①定义一个 file_operations 结构,并用以上方法来初始化。
static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
      .write      =     first_drv_write,     
};
②把这个file_operations 结构注册进内核。
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
③ 谁来调用这个驱动----驱动的入口
static struct class *firstdrv_class;
static struct class_device    *firstdrv_class_dev;
static int first_drv_init(void)
{
      major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核,主设备号写0--系统会自动给我们分配一个空缺的主设备号
      firstdrv_class = class_create(THIS_MODULE, "firstdrv");
      firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
      gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);//ioremap--把物理地址映射成虚拟地址
      gpbdat = gpbcon + 1;
      return 0;
}
static void first_drv_exit(void)
{
      unregister_chrdev(major, "first_drv"); // 卸载
      class_device_unregister(firstdrv_class_dev);
      class_destroy(firstdrv_class);
      iounmap(gpbcon);
}
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
以上就实现了最简单的led字符设备驱动。
然后,编写一个简单的Makefile,把这个驱动编译成内核模块:
KERN_DIR = /work/system/linux-2.6.22.6    #指定用哪个linux内核
all:
      make -C $(KERN_DIR) M=`pwd` modules    #在当前目录下编译成modules
clean:
      make -C $(KERN_DIR) M=`pwd` modules clean
      rm -rf modules.order
obj-m += first_drv.o                 #obj-m  编译成模块
下面来编译和装载这个驱动模块:
#make              现在就可以在目录下看到这个led.ko文件了
#insmod ./led.ko            装载这个驱动
如果要卸载这个驱动,可以用:#rmmod led

问2:应用程序来怎么调用这个驱动?
答:在这个驱动中已经有自动注册设备节点的功能,即通过以下代码实现:
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "led"); 
成功的话,会在 /sys/class/ 目录下出现一个fistdrv类的目录,并且在这个./firstdrv/ 目录下存在一个led目录,./led目录下有一个dev文件。
通过:#cat  dev  可以查看到主/次设备号。
MKDEV(major, 0) 就是通过这些信息来创建设备节点。
下面我们就是通过这个设备节点来操作led硬件,下面ledtest.c是测试程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/* firstdrvtest on
  * firstdrvtest off
  */
int main(int argc, char **argv)
{
      int fd;
      int val = 1;
      fd = open("/dev/xyz", O_RDWR);
      if (fd < 0)
      {
            printf("can't open!\n");
      }
      if (argc != 2)
      {
            printf("Usage :\n");
            printf("%s <on|off>\n", argv[0]);
            return 0;
      }
      if (strcmp(argv[1], "on") == 0)
      {
            val  = 1;
      }
      else
      {
            val = 0;
      }
      
      write(fd, &val, 4);
      return 0;
}

问3:为什么我们一执行 insmod 、rmmod 这个设备节点就会自动生成和删除掉呢?
答:这是因为我们在脚本文件:/etc/init.d/reS 中,加入了
echo /sbin/mdev  >  /proc/sys/kernel/hotplug
它支持热插拔,一插入或拔掉就会自动生成设备节点。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值