linux驱动字符设备模板

linux字符驱动模板,非设备树模式

基于linux 4.4版本,板载6410

test_01.c


#include <linux/fs.h>       /* 包含file_operation结构体 */
#include <linux/init.h>     /* 包含module_init module_exit */
#include <linux/module.h>   /* 包含LICENSE的宏 */

#include <asm/uaccess.h>    /* 包含copy_to_user之类 */

#include <linux/gpio.h>
#include <linux/io.h>       /* 包含ioremap之类 */
#include <linux/device.h>

#include <mach/gpio-samsung.h>
#include <asm/gpio.h>
#include <linux/gfp.h>




static unsigned int major;
static struct class *leds_class;
static struct device *led_dev[4];



/* 要申请的gpio,默认初始化为输出高电平 */
static const struct gpio leds_gpio[] = {
    {S3C64XX_GPA(3), GPIOF_OUT_INIT_HIGH, "led1-gpj0-3"},
    {S3C64XX_GPA(4), GPIOF_OUT_INIT_HIGH, "led2-gpj0-4"},
    {S3C64XX_GPA(5), GPIOF_OUT_INIT_HIGH, "led3-gpj0-5"},
};

/* 定义一个open函数 */
static int first_drv_open(struct inode *inode, struct file *filep)
{
    /* 得到要操作的次设备号,进行对应的操作 */
    int minor = MINOR(inode->i_rdev);

    printk("first_drv_open\n");
    /* 因为在申请使用gpiolib时已经默认初始化为输出高电平了,这里只是展示有单独的操纵gpio配置的函数 */
    switch(minor)
    {   
        case 0:
        /* 初始化gpj0_3.4.5为输出 */
        /* 默认关闭led灯 */
        gpio_direction_output(S3C64XX_GPA(3), 1);
        gpio_direction_output(S3C64XX_GPA(4), 1);
        gpio_direction_output(S3C64XX_GPA(5), 1);
        break;
 
        case 1:
        /* 初始化gpj0_3为输出 */
        /* 默认关闭led灯 */
        gpio_direction_output(S3C64XX_GPA(3), 1);
        break;
 
        case 2:
        /* 初始化gpj0_4为输出 */
        /* 默认关闭led灯 */
        gpio_direction_output(S3C64XX_GPA(4), 1);
        break;
 
        case 3:
        /* 初始化gpj0_5为输出 */
        /* 默认关闭led灯 */
        gpio_direction_output(S3C64XX_GPA(5), 1);
        break;
 
        default:
        break;
    }


    return 0;
}
 
/* 定义一个write函数 */
static ssize_t first_drv_write(struct file *file, const char __user * buf, size_t len, loff_t *ppos)
{
    char temp[10];
    int ret,val;
    
    char *from = (char*)buf;
    /* 得到要操作的次设备号,进行对应的操作 */
    int minor = MINOR(file->f_inode->i_rdev);
    
    printk("first_drv_write\n");

    memset(temp, 0,10);
    /* 拷贝用户空间的数据到内核空间 */
    ret = copy_from_user(temp, from, 3);    /* 内核空间和用户空间使用的不同的地址,所以它们之间的指针参数都需要转换 */
    if(ret)        /* 错误校验 */
    {
        printk("copy_from_user fail\n");    
    }
 
    /* 比较用户空间传的参数,来控制io状态 */
    if(strcmp("on", buf))
    {
        val = 1;
    }
    else
    {
        val = 0;
    }


    /* 根据参数设置led的输出 */
    switch(minor)
    {
        case 0:
            if(val)
            {
                gpio_set_value(S3C64XX_GPA(3), val);
                gpio_set_value(S3C64XX_GPA(4), val);
                gpio_set_value(S3C64XX_GPA(5), val);
            }
            else
            {
                gpio_set_value(S3C64XX_GPA(3), val);
                gpio_set_value(S3C64XX_GPA(4), val);
                gpio_set_value(S3C64XX_GPA(5), val);
            }
        break;
 
        case 1:
            gpio_set_value(S3C64XX_GPA(3),val);
        break;
 
        case 2:
            gpio_set_value(S3C64XX_GPA(4),val);
        break;
 
        case 3:
            gpio_set_value(S3C64XX_GPA(5),val);
        break;
 
        default:
        break;
    }


    return 0;
}
 
/* 把自己定义的函数接口集合起来,方便系统使用 */
static const struct file_operations leds_drv_file_operation = { 
    .open  = first_drv_open,
    .write = first_drv_write, 
};
 
 
/* 把集合起来的函数接口告诉系统,同时使用111作为该设备的字符设备号 */
static int __init first_drv_init(void)
{
    int i=0;
    
    printk("first_drv_init\n");
    //其实很简单,主设备号为0,则系统就会为我们自动分配未使用的主设备号
    //register_chrdev(111,"first_drv",&first_drv_file_operation);
    
    /* 获取一个自动的主设备号 */
    major =  register_chrdev(0,"leds_drv",&leds_drv_file_operation);
    if(major < 0)
    {
        printk("register_chrdev leds_drv fail \n");
        goto err_register_chrdev;
    }
 
    /* 创建一个类 */
    leds_class = class_create(THIS_MODULE, "leds_class");
    if(!leds_class)
    {
        printk("class_create leds fail\n");
        goto err_class_create;
    }
 
    /* 创建从属这个类的设备 */
    led_dev[0] = device_create(leds_class,NULL,MKDEV(major, 0), NULL, "leds");
    if(!led_dev[0])
    {
        goto err_device_create_led0;
    }
 
 
 
    /* 创建其它三个设备 */
    for(i = 1; i < 4; i++)
    {
        led_dev[i] = device_create(leds_class,NULL,MKDEV(major, i), NULL, "led%d",i);
        if(!led_dev[i])
        {
            goto err_device_create_leds;
        }
    }
 
    /* 使用gpiolib申请gpio */
    if(gpio_request_array(leds_gpio, 3))
    {
        goto err_gpio_request_array;
    }
 
    return 0;
 
 
/* 倒影式错误处理机制 */
err_gpio_request_array:
    for(i-- ; i > 0; i--)
    {
        device_unregister(led_dev[i]);
    }
err_device_create_leds:
    device_unregister(led_dev[0]);
err_device_create_led0:
    class_destroy(leds_class);
err_class_create:
    unregister_chrdev(major,"leds_drv");
err_register_chrdev:
 
    return -EIO;
}
 
/* 从系统中卸载掉字符设备号为111的设备 */
static void __exit first_drv_exit(void)
{
    int i;
    printk("first_drv_exit\n");

    /* 注销类里面的设备 */
    for(i = 0; i < 4; i++)
    {
        device_unregister(led_dev[i]);
    }
    /* 注销字符设备 */
    unregister_chrdev(major,"leds_drv");
    /* 注销类 */
    class_destroy(leds_class);
    /* 释放申请的gpio */
    gpio_free_array(leds_gpio, 3);

}
 
/* 声明段属性 */
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");
、、、

Makefile

KERN_DIR = /home/eric/Documents/work/linux/linux-4.4

.PHONY:all
all:
	make -C $(KERN_DIR) M=`pwd` modules


.PHONY:clean
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean


obj-m += test_01.o

编译

eric@eric-PC:~/Documents/work/linux/driver/test01$ make
make -C /home/eric/Documents/work/linux/linux-4.4 M=`pwd` modules
make[1]: 进入目录“/home/eric/Documents/work/linux/linux-4.4”
  CC [M]  /home/eric/Documents/work/linux/driver/test01/test_01.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/eric/Documents/work/linux/driver/test01/test_01.mod.o
  LD [M]  /home/eric/Documents/work/linux/driver/test01/test_01.ko
make[1]: 离开目录“/home/eric/Documents/work/linux/linux-4.4”
eric@eric-PC:~/Documents/work/linux/driver/test01$ ls
Makefile  modules.order  Module.symvers  test_01.c  test_01.ko  test_01.mod.c  test_01.mod.o  test_01.o
eric@eric-PC:~/Documents/work/linux/driver/test01$ 

以下是一个简单的Linux字符设备驱动程序模板,供参考: ```c #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> #define DEVICE_NAME "mychardev" #define CLASS_NAME "mycharclass" static int major_num = 0; static struct class* mycharclass = NULL; static struct device* mychardev = NULL; static struct cdev mycdev; static int mychardev_open(struct inode*, struct file*); static int mychardev_release(struct inode*, struct file*); static ssize_t mychardev_read(struct file*, char*, size_t, loff_t*); static ssize_t mychardev_write(struct file*, const char*, size_t, loff_t*); static struct file_operations mychardev_fops = { .owner = THIS_MODULE, .open = mychardev_open, .release = mychardev_release, .read = mychardev_read, .write = mychardev_write, }; static int __init mychardev_init(void) { // allocate major number dynamically if (alloc_chrdev_region(&major_num, 0, 1, DEVICE_NAME) < 0) { return -1; } // create device class if ((mycharclass = class_create(THIS_MODULE, CLASS_NAME)) == NULL) { unregister_chrdev_region(major_num, 1); return -1; } // create device if ((mychardev = device_create(mycharclass, NULL, major_num, NULL, DEVICE_NAME)) == NULL) { class_destroy(mycharclass); unregister_chrdev_region(major_num, 1); return -1; } // initialize cdev cdev_init(&mycdev, &mychardev_fops); mycdev.owner = THIS_MODULE; // add cdev to kernel if (cdev_add(&mycdev, major_num, 1) < 0) { device_destroy(mycharclass, major_num); class_destroy(mycharclass); unregister_chrdev_region(major_num, 1); return -1; } printk(KERN_INFO "mychardev: registered\n"); return 0; } static void __exit mychardev_exit(void) { cdev_del(&mycdev); device_destroy(mycharclass, major_num); class_destroy(mycharclass); unregister_chrdev_region(major_num, 1); printk(KERN_INFO "mychardev: unregistered\n"); } static int mychardev_open(struct inode* inodep, struct file* filep) { printk(KERN_INFO "mychardev: opened\n"); return 0; } static int mychardev_release(struct inode* inodep, struct file* filep) { printk(KERN_INFO "mychardev: closed\n"); return 0; } static ssize_t mychardev_read(struct file* filep, char* buffer, size_t len, loff_t* offset) { printk(KERN_INFO "mychardev: read from device\n"); return 0; } static ssize_t mychardev_write(struct file* filep, const char* buffer, size_t len, loff_t* offset) { printk(KERN_INFO "mychardev: wrote to device\n"); return len; } module_init(mychardev_init); module_exit(mychardev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple character device driver"); ``` 需要注意的是,该模板仅提供了一个最基本的字符设备驱动程序框架,需要根据实际需求进行修改。此外,还需要在Makefile中添加相应的编译规则,将驱动程序编译成内核模块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值