最近在学习Linux的驱动程序开发,连续啃了好几天书,也算是有了点最基本的认识,然后写了一个超级简单的驱动程序,JDEV,程序清单如下:
JDEV源代码:
jdev.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h> /* for timers */
#include <linux/fs.h> /* file modes and device registration */
#include <linux/poll.h> /* for poll */
#include <linux/wrapper.h> /* mem_map_reserve,mem_map_unreserve */
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <linux/init.h>
#include <asm/io.h>
#if defined(CONFIG_SMP)
#define __SMP__
#endif
#define JDEV_MAJOR 43 //主设备号,表明是哪一类的设备。设备号的具体概念,参考Documentation/devices.txt
#define BUFF_LENGTH 128 //JDEV缓冲区大小
#define JDEV_IOCTL_BASE 0xbb //IOCTL基本号
#define JDEV_GET_POOL _IOR(JDEV_IOCTL_BASE, 1, unsigned int) //取得缓冲区实际大小的IOCTL命令
static char *jdev_name = NULL; //设备名称
static char jdev_buf[BUFF_LENGTH]; //JDEV缓冲区定义
static unsigned int dev_buf_count; //实际缓冲区大小
//MODULE_LICENSE("GPL");
//几个很重要的驱动函数声明
static ssize_t jdev_read(struct file *file, char *buf, size_t count, loff_t *offset);
static ssize_t jdev_write(struct file *file, const char *buf, size_t count, loff_t *offset);
static int jdev_open(struct inode *inode, struct file *file);
static int jdev_release(struct inode *inode, struct file *file);
static int jdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
//驱动程序在Linux里面是以文件的形式来描述的,这里就是文件的操作函数定义。
static struct file_operations jdev_fops = {
read: jdev_read,
write: jdev_write,
ioctl: jdev_ioctl,
open: jdev_open,
release: jdev_release,
};
//初始化驱动模块,加载的时候调用。
int init_module(void)
{
int res;
if (jdev_name == NULL)
jdev_name = "jdev";
//注册字符设备
res = register_chrdev(JDEV_MAJOR, jdev_name, &jdev_fops);
if (res) {
printk("can't register device with kernel\n");
return res;
}
printk("jdev module loaded\n");
return 0;
}
//清理驱动模块,卸载的时候调用。
void cleanup_module(void)
{
//取消注册字符设备
unregister_chrdev(JDEV_MAJOR, "jdev");
printk("jdev unloaded\n");
return;
}
//打开设备,好像在读写,IOCTL的时候都会首先调用到。
static int jdev_open(struct inode *inode, struct file *file)
{
MOD_INC_USE_COUNT;//设备打开引用计数
printk("jdev open.\n");
return 0;
}
//关闭设备,好像在读写,IOCTL完成的时候都会调用到。
static int jdev_release(struct inode *inode, struct file *file)
{
MOD_DEC_USE_COUNT;//设备打开引用计数
printk("jdev release\n");
return 0;
}
//IOCTL接口,核心层和应用之间的一个桥梁。
static int jdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
printk("jdev ioctl.\n");
switch(cmd)
{
case JDEV_GET_POOL://取得BUF大小。
printk("running command JDEV_GET_POOL. \n");
if (put_user(dev_buf_count, (unsigned int *)arg))
return -EFAULT;
break;
default:
printk("error command number.\n");
}
return 0;
}
//JDEV读接口
static ssize_t jdev_read(struct file *file, char *buf, size_t count,
loff_t *offset)
{
printk("jdev read.\n");
copy_to_user(buf, jdev_buf, count);
return count;
}
//JDEV写接口
static ssize_t jdev_write(struct file *file, const char *buf, size_t count,
loff_t *offset)
{
if(count > BUFF_LENGTH)
{
printk("data size must less than 128 byte.\n");
return 0;
}
copy_from_user(jdev_buf, buf, count);
dev_buf_count = count;
printk("jdev1 write count: %d, buf: %s.\n", dev_buf_count, jdev_buf);
return count;
}
JDEV编译命令:
gcc -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4/include -Wall -O2 -o jdev.o -c jdev.c
编译驱动的时候必须要安装内核代码,我的代码是装在/usr/src/linux-2.4/include
测试程序代码:
jdev_io.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
//IOCTL命令,和JDEV定义保持一致,其实应该用一个H文件来表示。
#define JDEV_IOCTL_BASE 0xbb
#define JDEV_GET_POOL _IOR(JDEV_IOCTL_BASE, 1, unsigned int)
int main(int argc, char *argv[])
{
int fd = open("/dev/jdev", O_RDWR);//打开设备
unsigned int j = 0;
if (fd == -1) {
perror("open");
return 1;
}
//取得缓冲区大小
if (ioctl(fd, JDEV_GET_POOL, &j) == -1) {
perror("ioctl");
return 2;
}
//打印缓冲区大小
printf("Get buffer size is %d.\n", j);
return 0;
}
测试程序编译命令:
gcc jdev_io.c
生成测试程序a.out
安装及测试命令:
mknod /dev/jdev c 43 0
insmod jdev.o
cp test /dev/jdev
test是一个文本文件,里面有一点点测试文本。
./a.out
如果一切正常,就会输出:Get buffer size is XX.
之后rmmod jdev删除驱动模块
同时可以用dmesg看到JDEV中printk输出的调试信息。
参考资料:
Beginning Linux Programming(Third Edition)
(fanged) |