本文的目的是为了掌握字符设备驱动的第一原理,掌握字符设备的基本框架。
下面写出最基本的框架:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
static struct class *forthdrv_class;
static struct class_device *forthdrv_class_dev;
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
static int forth_drv_open(struct inode *inode, struct file *file)
{
printk("this is the open function\r\n");
return 0;
}
ssize_t forth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
printk("this is the read function\r\n");
return 0;
}
static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* ����һ���꣬�������ģ��ʱ�Զ�������__this_module���� */
.open = forth_drv_open,
.read = forth_drv_read,
};
int major;
static int forth_drv_init(void)
{
major = register_chrdev(0, "forth_drv", &sencod_drv_fops);
forthdrv_class = class_create(THIS_MODULE, "forth_drv");
forthdrv_class_dev = class_device_create(forthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
return 0;
}
static void forth_drv_exit(void)
{
unregister_chrdev(major, "forth_drv");
return 0;
}
module_init(forth_drv_init);
module_exit(forth_drv_exit);
MODULE_LICENSE("GPL");
首先第一个结构体
static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, .open = forth_drv_open, .read = forth_drv_read, };
这个结构体是字符设备和内核之间的入口函数。其中.open 函数是当测试的应用程序程序调用open("XXX.txt",XXX)的时候应该调用驱动程序的哪个函数
read也是如此 当测试的应用程序调用read("XXX.txt")的时候应该调用的驱动的程序程序。
那么如何注册一个字符设备呢?
就是要利用
register_chrdev 函数。
register_chrdev 的函数原型是:
/**
* register_chrdev() - Register a major number for character devices.
* @major: major device number or 0 for dynamic allocation
* @name: name of this range of devices
* @fops: file operations associated with these devices
*
* If @major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If @major > 0 this function will attempt to reserve a device with the given
* major number and will return zero on success.
*
* Returns a -ve errno on failure.
*
* The name of this device has nothing to do with the name of the device in
* /dev. It only helps to keep track of the different owners of devices. If
* your module name has only one type of devices it's ok to use e.g. the name
* of the module here.u
* This function registers a range of 256 minor numbers. The first minor number
* is 0.
*/
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
上面的注释说明了 此函数是给字符设备设置一个主设备号,但是不生成相应的设备。如果第一个参数给0 这个值,则设备会自动分配一个主设备号。
forthdrv_class = class_create(THIS_MODULE, "forth_drv"); forthdrv_class_dev = class_device_create(forthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
这两个函数一个是建立一个类 这个类的返回值是为了给下面的class_device_create 函数提供一个指针。 下面的class_device_create 是真正的把设备创建了出来我们在/dev下面可以看到这个设备。
首先我们先写出这么一个程序 在板子中试验一下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <signal.h> #include <unistd.h> #include <unistd.h> #include <fcntl.h> int fd; int main(int argc, char **argv) { int flags; fd = open("/dev/buttons", O_RDWR); if (fd < 0) { printf("can't open!\n"); return -1; } }
最终得到的结果是: