第一个驱动 led_drive

前面记录了根文件系统,现在开始写驱动程序,关于框架什么的都是一些概念的东西,这里就不详述,先写一个最简单的LED驱动

从现在开始进入Linux驱动的大门,如何自已写,参考内核自带的字符驱动,仿照写出自已的驱动,这些驱动只适合自已使用,想

做成通用的驱动,后面会慢慢深入,现在先了解和熟悉

linux驱动有以下几个组成

  1:file_operations 结构体

    包含open write read 等操作函数

  2:入口和出口函数

    入口 注册驱动 创建类 类下创建设备 地址映射

    出口 同入口相反 卸载驱动 类 类设备 取消地址映射

  3:功能操作函数

    当应用程序/测试程序 执行对就函数时,就会调用到具体的操作函数,在这里函数里实现具体功能实现

下面看代码比较直观:参考其它驱动和不断调试修改的最终代码

#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/mc146818rtc.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/sysctl.h>
#include <linux/wait.h>
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/ratelimit.h>
#include <linux/bfs_fs.h>

#include <asm/current.h>


static struct class *leddev_class; /* 定义一个类用于自动创建设备节点 */
static struct device *dev_led;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

/* 测试程序执行open 时会调用这个函数 

 * 这个函数实现 GPIO的设置

 */

static int led_dev_open(struct inode *inode , struct file *file)
{
  printk("open function test!\n");
  *gpfcon &= ~ ((0x03 << 8) | (0x03 << 10) | (0x03 << 12));
  *gpfcon |= ((0x01 << 8) | (0x01 << 10) | (0x01 << 12));
  return 0;
}

/* 测试程序执行write时 调用驱动的这个函数 

 * copy_from_user 拷贝测试程序的数据到驱动

 * 根据数据执行对就的硬件操作

 */

static int led_dev_write(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{
  int val;

  copy_from_user(&val,buf,count); //copy_to_user;

  if(val == 1)
  {
    *gpfdat &= ~((1 << 4) | (1 << 5) | (1 << 6));
    printk("led1 led2 led3 on!\n");
  }
  else
  {
    *gpfdat |= ((1 << 4) | (1 << 5) | (1 << 6));
    printk("led1 led2 led3 off!\n");
  }
  return 0;
}

/* 定义一个file_operations结构体
* 实现 open write 两个函数
*/
static struct file_operations led_dev_per = {
  .owner = THIS_MODULE,
  .open = led_dev_open,
  .write = led_dev_write,
};
/* 驱动程序入口函数 入口函数 注册字符设备 装载驱时调用
* 创建类 并在类下创建设备 用于mdev 自动创建设备节点
* 寄存器地址映射 用虚拟地址操作GPIO
*/
static int major; /* 定义一个变量用于主设备号 */
static int led_dev_init(void)
{
  major = register_chrdev(0, "led_dev", &led_dev_per);
  leddev_class = class_create(THIS_MODULE, "led_dev");
  dev_led = device_create(leddev_class, NULL, MKDEV(major,0), NULL, "leds");
  gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
  gpfdat = gpfcon + 1;
  return 0;
}
/* 驱动程序出口函数 卸载驱动时调用
* 卸载字符设备 卸载类设备 卸载类
* 取消寄存器地址映射
*/
static void led_dev_exit(void)
{
  unregister_chrdev(major, "led_dev");
  device_destroy(dev_led, MKDEV(major,0));
  class_destroy(leddev_class);
  iounmap(gpfcon);
}
/* 入口函数和出口函数需要用下面的宏修饰
* 还需要加入 "GPL" 协议
*/
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

下面列出测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc,char **argv)
{
int fd;
int val = 1;
fd = open("/dev/leds",O_RDWR); /* 打开设备/dev/leds 可读可写 */
if(fd < 0) /* 判断打开是否成功 */
printf("can't open this dev!\n"); /* 失败就打印这条信息 */
if(argc != 2) /* 判断串口输入参数 不等于2时 */
{
printf("Usage: \n"); /* 打印使用帮助信息 */
printf(" %s <on/off> \n",argv[0]);
}
if(strcmp(argv[1],"on") == 0) /* 判断第二个参数 等ON 打开LED */
val = 1;
else if(strcmp(argv[1],"off") == 0) /* 判断第二个参数 等OFF 关闭LED */
val = 0;
write(fd,&val,4); /* 把值发送给驱动程序 */
return 0;
}

这里列出Makefile

KERN_DIR = /work/linux/linux-3.4.2

all:
make -C $(KERN_DIR) M=`pwd` modules

clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order

obj-m += led_char_dev.o

驱动个人认为主要是记住需要的东西和流程即可,内核如何操作,后面会学到

猜你喜欢

转载自www.cnblogs.com/x2i0e19linux/p/11688289.html