linux字符设备驱动开发之新字符设备驱动开发

本文章参考正点原子相关教程,仅学习使用

考虑到老版本使用register_chrdev需要事先确定设备号,驱动加载需要手动创建节点(mknod).新版本linux驱动采用动态分配设备号,可自动创建节点.

1. 新字符设备注册方法

// 没有指定设备号,申请设备号
// dev : 设备号
// baseminor : 次设备号 , 通常为0
// count : 设备申请数量
// name : 设备名
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			const char *name)

// 指定设备号,申请设备号
// from : 给定的设备号
// count : 申请设备数量
// name : 设备名称
int register_chrdev_region(dev_t from, unsigned count, const char *name)

// 注销设备
void unregister_chrdev_region(dev_t from, unsigned count)
// 字符设备结构体
struct cdev {
	struct kobject kobj;
	struct module *owner;
	const struct file_operations *ops;
	struct list_head list;
	dev_t dev;
	unsigned int count;
};

// 初始化字符设备
void cdev_init(struct cdev *, const struct file_operations *);

// 添加字符设备
int cdev_add(struct cdev *, dev_t, unsigned);

// 删除字符设备
void cdev_del(struct cdev *);

2.自动创建设备节点

linux通过udev机制自动创建节点,udev通过访问硬件状态,根据硬件状态申请节点.

自动创建设备节点通过创建类和设备两部组成.

// 创建类
// owner : THIS_MODULE
// name : class name
extern struct class * __must_check __class_create(struct module *owner,
						  const char *name,
						  struct lock_class_key *key);
						  
// 销毁类
extern void class_destroy(struct class *cls);

// 创建设备
struct device *device_create(struct class *cls, struct device *parent,
			     dev_t devt, void *drvdata,
			     const char *fmt, ...);
			     
// 销毁设备
extern void device_destroy(struct class *cls, dev_t devt);

3.自定义设备结构体

通常设备包含很多属性,如设备号,类,设备等等,采用结构体定义代替变量定义.一般地,在open函数里面将结构作为私有设备添加到设备中.

struct newchrled_dev
{
    dev_t devid;    /*device number */
    struct cdev cdev;   /*cdev */
    struct class *class;    /*class */
    struct device *device; /*device  */
    int major;  /*major device  number*/
    int minor;  /*minor device number */
};

4. 完整例子

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define LED_NAME 'led'
#define LED_CNT 1

#define LEDOFF 0
#define LEDON 1

// physical address
#define CCM_CCGR1_BASE          (0x020C406C)
#define SW_MUX_GPIO1_IO03_BASE  (0x020E0068)
#define SW_PAD_GPIO1_IO03_BASE  (0x020E02F4)
#define GPIO1_DR_BASE           (0x0209C000)
#define GPIO1_GDIR_BASE         (0x0209C004)


// virtual address
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;


struct newchrled_dev
{
    dev_t devid;    /*device number */
    struct cdev cdev;   /*cdev */
    struct class *class;    /*class */
    struct device *device; /*device  */
    int major;  /*major device  number*/
    int minor;  /*minor device number */

};

struct newchrled_dev newchrled;



// open or close led
void led_switch(uint8_t sta){
    uint32_t val = 0;

    if(sta == LEDON){
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);
    }else if (sta == LEDOFF)
    {
        val = readl(GPIO1_DR);
        val |= (1 << 3);
        writel(val, GPIO1_DR);
    }
    
}


// open device 
static int led_open(struct inode *inode , struct file *flip){
    
    // set private data
    flip->private_data = &newchrled;
    return 0;
}

// release device 
static int led_release(struct inode *inode, struct file *filp){
    return 0;
}


// read data
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt){
    return 0;
}


// write data
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt){
    int ret;
    unsigned char databuf[1];
    unsigned char ledstat;

    ret = copy_from_user(databuf,buf,cnt);
    if (ret < 0 ){
        printk("kernel write failed!\n");
        return -1;
    }

    ledstat = databuf[0];

    if (ledstat == LEDON){
        led_switch(LEDON);
    }else if(ledstat == LEDOFF){
        led_switch(LEDOFF);
    }

    return 0;
}

// device operation
static struct file_operation led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_read,
};

// device init 
static int __init led_init(void){
    int ret = 0;
    uint32_t val = 0;

    // address mapping
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);

    // init gpio1 time
    val = readl(IMX6U_CCM_CCGR1);
    val &= ~(3 << 26);
    val |= (3 << 26);
    writel(val, IMX6U_CCM_CCGR1);

    // set reuse
    writel(5, SW_MUX_GPIO1_IO03);

    // set io
    writel(0x10B0,SW_PAD_GPIO1_IO03);

    // set gpio1 3 as output
    val = readl(GPIO1_GDIR);

    val &= ~(1 << 3);
    val |= (1 << 3);
    writel(val, GPIO1_GDIR);

    // close led
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);

    // register device
    if(newchrled.major)  /*if defined major */
    {   
        newchrled.devid = MKDEV(newchrled.major,0);    /* */
        register_chrdev_region(newchrled.devid, LED_CNT, LED_NAME);

    }else
    {
        alloc_chrdev_region(&newchrled.devid, LED_CNT, LED_NAME);
        newchrled.major = MAJOR(newchrled.devid);
        newchrled.minor = MAJOR(newchrled.devid);
    }

    printk("newchrled major = %d, minor = %d.\n",newchrled.major, newchrled.minor);

    // cdev
    newchrled.cdev.owner = THIS_MODULE;
    cdev_init(&newchrled.cdev, &led_fops);

    cdev_add(&newchrled.cdev, newchrled.devid, LED_CNT);

    // class
    newchrled.class = class_creat(THIS_MODULE, LED_NAME);
    if(IS_ERR(newchrled.class)){
        return PTR_ERR(newchrled.class);
    }

    // device
    newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, LED_NAME);
    if(IS_ERR(newchrled.device)){
        return PTR_ERR(newchrled.device);
    }  

    return 0;
}

// device exit
static void __exit led_exit(void){

    // cancel mapping
    iounmap(CCM_CCGR1_BASE);
    iounmap(SW_MUX_GPIO1_IO03_BASE);
    iounmap(SW_PAD_GPIO1_IO03_BASE);
    iounmap(GPIO1_DR_BASE);
    iounmap(GPIO1_GDIR_BASE);

    cdev_del(&newchrled.cdev);
    unregister_chrdev_region(newchrled.devid, LED_CNT);
    device_destory(newchrled.device, LED_CNT);
    class_destory(newchrled.class);
    
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TAN");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值