linux驱动摸索 --驱动框架初始化(结合韦东山视频教程)

一.驱动框架

初始化: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;
}



你可能感兴趣的:(c,linux,linux驱动)