Linux kernel 2.6.32.2下mini 2440的led字符驱动

原创文章,转载请注明: 转载自勤奋的小青蛙
本文链接地址: Linux kernel 2.6.32.2下mini 2440的led字符驱动

在完成这个驱动代码的时候,中间经历了很多的小问题,具体的总结我都放在了这个地方:

http://www.jyguagua.com/?p=723

针对字符驱动,可以参考LDD3去学习研究,LDD3讲的非常详细,本文只是针对kernel 2.6.32.2环境下对mini 2440的开发板的led驱动做演示:

首先看下leddrive.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
//#include <asm/irq.h>
//#include <asm/arch/regs-gpio.h>
//#include <asm/hardware.h>
#include <asm/io.h>
#include <mach/irqs.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");

struct class *led_class;
//struct class_device *led_dev;

/*led IO寄存器定义*/
volatile unsigned long *gpbcon = NULL;
volatile unsigned long *gpbdat = NULL;
volatile unsigned long *gpbup = NULL;
/*主设备号*/
int major;

static int led_open(struct inode *inode, struct file *file)
{
	printk("first_drv_open\n");
	/*配置GPBCON, GPBDAT, GPBUP寄存器,根据mini2440原理图可知是GPB_IO口*/
	*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));
	*gpbdat |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
	//上拉电阻关闭
	*gpbup |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8); 
	return 0;
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	printk("first_drv_write1\n");
	int val;
	//将数据从用户空间拷贝至内核空间
	copy_from_user(&val, buf, count);

	if (val == 1) {
		printk("first_drv_write2\n");
		//如果是1的话,就给各端口写入低电平,这样led就点亮,反之则灭,如下一种情况
		*gpbdat &= ~((1 << 5) | (1 << 6) | (1 << 7) | (1 << 8));
	} else {
		printk("first_drv_write3\n");
		*gpbdat |= (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8);
	}
	return 0;
}

static struct file_operations led_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.write = led_write,
};

static int led_init(void)
{
	//获取主设备号
	major = register_chrdev(0, "led_drv", &led_fops);
	//通过class_create以及device_create来在/dev路径下创建设备led
	led_class = class_create(THIS_MODULE, "leddrv");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "led");

	//内存映射,调用ioremap函数,对应的GPBDAT, GPBUP只是增加单位而已
	gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);
	gpbdat = gpbcon + 1;
	gpbup = gpbdat + 1;

	return 0;
}

static void led_exit(void)
{
	//注销设备号
	unregister_chrdev(major, "led_drv");
	//删除设备
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	iounmap(gpbcon);
}

module_init(led_init);
module_exit(led_exit);

然后是Makefile:

ifneq ($(KERNELRELEASE),)
	obj-m := leddrive.o
else
	#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
	KERNELDIR ?= /usr/src/linux-2.6.32.2 
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

最后是我们的测试代码,测试代码就是读取dev下我们新创建的设备,然后发送参数,使得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/led", 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;
}

如上代码编好之后,可以通过如下命令获得leddrive.ko 和测试程序ledtest:

[ypf@localhost linux]#make #获得leddrive.ko
[ypf@localhost linux]#arm-linux-gcc -o ledtest ledtest.c #获得测试程序ledtest

运行如下:

[ypf@localhost linux]#insmod leddrive.ko
[ypf@localhost linux]#./ledtest on #打开led
[ypf@localhost linux]#./ledtest off #关闭led
原创文章,转载请注明: 转载自勤奋的小青蛙
本文链接地址: Linux kernel 2.6.32.2下mini 2440的led字符驱动

文章的脚注信息由WordPress的wp-posturl插件自动生成



|2|left
打赏

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: