前方的路,充满了艰辛。-
分类: LINUX
2013-04-20 10:18:17
原文地址:LINUX设备驱动开发入门一例(一) 作者:GentleHacker
驱动程序源代码如下,关键地方提供了注释,并且此驱动在开发板上面测试通过。
//首先是头文件部分,必须包含像cdev.h和module.h等头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 定义了驱动名字 主设备号 从设备号,设备号注意不要与系统已有的冲突
// 本例没有采用动态分配的方法!!
#define DEVICE_NAME "s3c2440_seg"
#define SEG_MAJOR 232
#define SEG_MINOR 0
// 数码管的位选和段选地址,不同开发板地址可能不一样
#define SEG_SELECT 0xfe007000
#define VALUE_SELECT 0xfe006000
#define LED_NUM 6
#define LED_VALUE_MAX 16
#define ALL_LIGHT 7
// 定义驱动的ioctl函数,其中第一个unsigned int表示数码管的位选,选择第几
// 个数码管亮,第二个是段选,选择亮的数值
int device_ioctl(struct inode *,struct file *,unsigned int ,unsigned long );
// 定义主设备号变量及从设备号变量,并初始化了
int major = SEG_MAJOR;
int minor = SEG_MINOR;
// 数码管显示对应数值的十六进制值,是共阳型的
unsigned char seg7table[16] =
{
/* 0 1 2 3 4 5 6 7*/
0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8,
/* 8 9 A B C D E F*/
0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e
};
// 位选值
unsigned char seg7select[6] =
{
/*0 1 2 3 4 5*/
0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf
};
// seg_ioctl具体实现
static int seg_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
printk("Device Ioctl\n");
// choose wich ligth to be lighted
unsigned int value;
value = (unsigned int)arg; // 强制转换,表示对应数码管应该显示的数值
if (cmd < LED_NUM)
(*(char *)SEG_SELECT) = seg7select[cmd - 1];
else if (cmd == ALL_LIGHT)
(*(char *)SEG_SELECT) = 0x0;
else
printk(KERN_EMERG"The light is not existed!!\n");
// choose the value to light
if (value < LED_VALUE_MAX)
(*(char *)VALUE_SELECT) = seg7table[value];
else
printk(KERN_EMERG"The light value illegal!!\n");
return 0;
}
// 初始化file_operations结构体,只有一个操作
static struct file_operations seg_fops = {
.owner = THIS_MODULE,
.ioctl = seg_ioctl,
};
// 定义一个字符设备结构体
static struct cdev seg_devs;
// 初始化字符设备
static void seg_setup_cdev(struct cdev *seg_dev, struct file_operations *fops)
{
int err;
dev_t devno = MKDEV(major, minor);
cdev_init(seg_dev, fops);
seg_dev->owner = THIS_MODULE;
seg_dev->ops = fops;
err = cdev_add(seg_dev, devno, 1);
if (err)
{
printk(KERN_EMERG"cdev initial failed\n");
}
}
// 注册设备并调用初始设备函数
static int __init seg_init( void )
{
int ret = 0;
dev_t dev = MKDEV(major, minor);
if (major)
{
ret = register_chrdev_region(dev, 1, DEVICE_NAME);
}
else
{
printk(KERN_EMERG"device number allocated failed!!\n");
return ret;
}
seg_setup_cdev(&seg_devs, &seg_fops);
printk (DEVICE_NAME" initialized\n");
return ret;
};
// 驱动卸载时调用
static void __exit seg_exit(void)
{
cdev_del(&seg_devs); // log out the device
unregister_chrdev_region(MKDEV(major, minor), 1);
printk("The light device is uninstalled\n");
}
module_init(seg_init);
module_exit(seg_exit);
// 辅助信息
MODULE_AUTHOR("GetleHacker");
MODULE_DESCRIPTION("S3C2440 Led Device Driver");
MODULE_LICENSE("GPL");
在完成上述简单的驱动代码编写之后,可以使用下列makefile文件中的内容进行编译生成驱动安装文件s3c2440_seg.ko。
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /opt/linux-2.6.30.4
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 *.mod.c *.markers *.order *.symvers .tmp_versions
.PHONY: modules modules_install clean
else
obj-m:=s3c2440_seg.o
endif
注意第二行中的KERNELDIR非常关键,此内核目录是你ARM板系统同一内核必须。
完成上述工作之后,启动ARM板,通过nfs服务器,将LINUX系统根目录下的文件夹share与ARM板中/mnt/nfs连接共享。此时可以直接在ARM板上的LINUX系统访问PC机端的share目录中的内容(具体请参考上一篇博文ARM开发板中LINUX系统访问PC端的共享文件目录)。将生成的s3c2440_seg.ko文件放在共享目录share中。
在ARM开发板上进入/mnt/nfs目录下,执行如下命令:
# insmod s3c2440_seg.ko
# mknod /dev/s3c2440_seg c 232 0
注意这里的主设备号和从设备号与驱动程序中的相一致。
完成上面的工作之后,驱动就安装上去了,接着可以测试驱动工作是否正常了。