一.驱动框架
初始化:insmod 加载
1.确定主设备号:
分为静态和动态分配,其中LED_GPIO_SIZE 表示支持的次设备号数目,一般默认为1. 相关实现代码如下:
int result;
dev_t dev;
/*分配主设备号*/
if (scull_major) /*静态分配一个主设备号*/
{
dev = MKDEV(scull_major,0);
result = register_chrdev_region(dev,LED_GPIO_SIZE,DEVICE_NAME);
}
else /*动态分配一个主设备号*/
{
result = alloc_chrdev_region(&dev,0,LED_GPIO_SIZE,DEVICE_NAME);
scull_major = MAJOR(dev);
}
if(result <0)
{
printk("LED:can not get major:%d\n",scull_major);
return result;
}
2.构造 file_operations 结构:结构成员对应相应的处理函数:
static struct file_operations mini2440_leds_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = mini2440_leds_open,
.write = mini2440_leds_write,
};
3.将相关操作告诉内核:
内核用cdev结构来表示字符设备,cev_init()将文件操作和cdev关联。cdev_add()将之前生成的主次设备号和cdev连接在一起,
led_class = class_create(THIS_MODULE,DEVICE_NAME);
cdev_init(&led_gpio_cdev, &mini2440_leds_fops);
result = cdev_add(&led_gpio_cdev, dev, 1);
if(result <0)
{
printk("LED:cdev_add error\n");
return result;
}
device_create(led_class, NULL, MKDEV(scull_major, 0), NULL, "led0");
卸载驱动 rmmod 卸载 代码实现如下:
dev_t dev_id = MKDEV(scull_major, 0);
/*卸载主设备号*/
unregister_chrdev_region(dev_id, LED_GPIO_SIZE);
device_destroy(led_class,MKDEV(scull_major, 0));
cdev_del(&led_gpio_cdev);
class_destroy(led_class);
最后附上一个较为完整的驱动框架,其中创建了主设备号和次设备号,驱动代码如下:
#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 <mach/io.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/device.h>
#include <linux/cdev.h>
#define DEVICE_NAME "led_1"
#define LED_GPIO_SIZE 4
static int scull_major = 0;
static struct class *led_class;
static struct cdev led_gpio_cdev[LED_GPIO_SIZE];
static int mini2440_leds_open(struct inode *inode, struct file *file)
{
int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev);
printk("/dev/led%d has opened\n",minor);
return 0;
}
static ssize_t mini2440_leds_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
{
char val;
int minor = MINOR(filp->f_dentry->d_inode->i_rdev);
copy_from_user(&val, buf, 1);
printk("/dev/led%d write the val = %d\n",minor,val);
return 0;
}
static struct file_operations mini2440_leds_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = mini2440_leds_open,
.write = mini2440_leds_write,
};
/*
* 执行insmod命令时就会调用这个函数
*/
static int mini2440_leds_init(void)
{
int result,i;
dev_t dev;
/*分配主设备号*/
if (scull_major) /*静态分配一个主设备号*/
{
dev = MKDEV(scull_major,0);
result = register_chrdev_region(dev,LED_GPIO_SIZE,DEVICE_NAME);
}
else /*动态分配一个主设备号*/
{
result = alloc_chrdev_region(&dev,0,LED_GPIO_SIZE,DEVICE_NAME);
scull_major = MAJOR(dev);
}
if(result <0)
{
printk("LED:can not get major:%d\n",scull_major);
return result;
}
led_class = class_create(THIS_MODULE,DEVICE_NAME);
if (IS_ERR(led_class)) {
return PTR_ERR(led_class);
}
for (i=0; i<LED_GPIO_SIZE;i++)
{
cdev_init(&led_gpio_cdev[i], &mini2440_leds_fops);
result = cdev_add(&led_gpio_cdev[i], (dev+i), 1);
if(result <0)
{
printk("LED:cdev_add error\n");
return result;
}
device_create(led_class, NULL, MKDEV(scull_major, i), NULL, "led%d",i);
}
return 0;
}
/*
* 执行rmmod命令时就会调用这个函数
*/
static void mini2440_leds_exit(void)
{
int i;
dev_t dev_id = MKDEV(scull_major, 0);
/*卸载主设备号*/
unregister_chrdev_region(dev_id, LED_GPIO_SIZE);
for(i=0;i<LED_GPIO_SIZE;i++)
{
device_destroy(led_class,MKDEV(scull_major, i));
cdev_del(&led_gpio_cdev[i]);
}
class_destroy(led_class);
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(mini2440_leds_init);
module_exit(mini2440_leds_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_LICENSE("GPL");
linux 测试代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/*
* ledtest <dev> <on|off>
*/
void print_usage(char *file)
{
printf("Usage:\n");
printf("%s <dev> <on|off>\n",file);
printf("eg. \n");
printf("%s /dev/led0 a\n", file);
printf("%s /dev/led1 b\n", file);
printf("%s /dev/led2 c\n", file);
printf("%s /dev/led3 d\n", file);
}
int main(int argc, char **argv)
{
int fd;
char* filename;
char val;
if (argc != 3)
{
print_usage(argv[0]);
return 0;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0)
{
printf("error, can't open %s\n", filename);
return 0;
}
if (!strcmp("a", argv[2]))
{
val = 10;
write(fd, &val, 1);
}
else if (!strcmp("b", argv[2]))
{
val = 11;
write(fd, &val, 1);
}
else if (!strcmp("c", argv[2]))
{
val = 12;
write(fd, &val, 1);
}
else if (!strcmp("d", argv[2]))
{
val = 13;
write(fd, &val, 1);
}
return 0;
}