Chinaunix首页 | 论坛 | 博客

OS

  • 博客访问: 2219541
  • 博文数量: 691
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2660
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-05 12:49
个人简介

不浮躁

文章分类

全部博文(691)

文章存档

2019年(1)

2017年(12)

2016年(99)

2015年(207)

2014年(372)

分类:

2014-12-09 21:20:25

原文地址:简单的LED驱动 作者:xutianxi

编译模块的Makefile文件
ifeq ($(KERNELRELEASE),)
# set your object kernel dir
KERNELDIR ?= /source/kernel/linux-2.6.22.6-farsight
PWD := $(shell pwd)
modules:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers
.PHONY: modules modules_install clean
else
    obj-m := led_drv.o
endif

编译模块的Makefile文件、
注:如果一个模块由多个*.c文件编译而成,那么应该如下编写Makefile文件:
obj-m := led_drv.o
module-objs := file1.o file2.o

驱动程序详细设计:

#include
#include
#include
#include
#include
#include
#include
#include
#include

MODULE_LICENSE("GPL");

#define rGPFDAT 0x56000054   //实际IO的物理地址
#define rGPFCON 0x56000050

#define LED_ON  0x1
#define LED_OFF 0x2

//static dev_t my_led_id;
static int led_major = 257;  //主设备号

struct __my_led
{
 struct cdev dev;
 unsigned char *vrGPFDAT;
 unsigned short int *vrGPFCON;
}my_led;

static int my_led_open(struct inode *inode, struct file *filp)
{
 struct __my_led *p = container_of(inode->i_cdev, struct __my_led, dev);
 filp->private_data = p;
 //实际物理地址映射到内核虚拟地址
 p->vrGPFDAT = (unsigned char *)ioremap(rGPFDAT, 0x1);
 p->vrGPFCON = (unsigned short int *)ioremap(rGPFCON, 0x2);
 *p->vrGPFCON = (*p->vrGPFCON) & 0x00ff |(0x55<<8);
// *p->vrGPFDAT = 0x30;
// printk("open my_led, p->vrGPFDAT = 0x%x, p->vrGPFCON = 0x%x\n", p->vrGPFDAT, p->vrGPFCON);
  
 return 0;
}

static int  my_led_release(struct inode *inode, struct file *filp)
{
 printk("close my_led\n");
 filp->private_data = NULL;
 //解除映射
 iounmap(my_led.vrGPFDAT);
 iounmap(my_led.vrGPFCON);

 return 0;
}

static int my_led_ioctl(struct inode *inode, struct file *filp,
   unsigned int cmd, unsigned long arg)
{
 struct __my_led *p = filp->private_data;
 int ret = 0;
// printk("in ioctl_dri func , cmd = %d, p->vrGPFDAT = 0x%x\n", cmd, p->vrGPFDAT);
 switch (cmd)
 {
  case LED_ON:
   *p->vrGPFDAT &= 0x0f; //GPF4,5,6,7 置为低电平
   break;
  case LED_OFF:
   *p->vrGPFDAT |= 0xf0;//GPF4,5,6,7 置为高电平
   break;
  default:
   ret = EINVAL;
   break;
 }

 return ret;
}

static const struct file_operations my_led_fops =
{
 .owner = THIS_MODULE,
  .open = my_led_open,
 .release = my_led_release,
 .ioctl = my_led_ioctl,
};

static void my_led_setup_cdev(void)
{
 int err;
 int devno=MKDEV(led_major, 0);
 
 //是字符设备关联一个my_led_fops
 cdev_init(&my_led.dev, &my_led_fops);
 my_led.dev.owner = THIS_MODULE;
 //添加字符设备
 err = cdev_add(&my_led.dev, devno, 1);
 if (err)
 {
  printk("add my_led device error, err = %d \n", err);
  return ;
 }

 return;
}

int __init my_led_init(void)
{
 int ret;
 dev_t dev = MKDEV(led_major,0);

 //注册设备号
 memset(&my_led, 0, sizeof(my_led));
 ret = alloc_chrdev_region(&dev, 0, 1, "my_led");
 if (ret < 0)
 {
  printk("alloc led device id error \n");
  return ret;
 }
 my_led_setup_cdev();
 printk("add my_led device ok!\n");
 printk(KERN_NOTICE"[DEBUG] adc device major is %d\n",led_major);

 return 0;
}

void __exit my_led_exit(void)
{
  //注意顺序不能颠倒,要和加载设备时步骤相反
 cdev_del(&my_led);
 unregister_chrdev_region(MKDEV(led_major,0) ,1);
 printk("my-adc device uninstalled\n");
}
module_init(my_led_init);
module_exit(my_led_exit);

阅读(1041) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~