i2c驱动-建立于字符驱动之上

基于Linux3.10的设备驱动实例,

  首先是字符设备的框架搭建,该框架可以被很多场合稍加修改即可使用;在完成字符设备驱动之后,该字符设备并不依赖于真实物理硬件,调试起来很方便,LDD一书中scull例子比这里稍微要复杂,主要体现在其基于数据结构的读写方法上,其实字符驱动设备的框架和读写没有关系,所以下面的例子还是挺有用的

在字符设备完成的基础之上搭建一个i2c设备侧驱动,关于i2c驱动的client侧和adapter侧的关系见《编写i2c驱动-基于Linux3.10》,这里的驱动是让client能够正常工作的驱动,所以这依赖于adapter侧驱动必须先加载,由于SOC芯片厂商的SDK中都会提供其芯片自身的i2c控制器的驱动,所以这通常不是一个问题。但是这是依赖于具体硬件的,毕竟没有使用两条线模拟时钟线和数据线的读写时序。

在完成i2c驱动设备之后,由于示例较为简单,中断中处理了所有的接收字符的工作,通常在中断中处理函数中会使用tasklet或者work queue的方式,将任务推后执行以尽量提高中断可用的状态,所以在此基础之上使用work queue对接收i2c接收进行了优化。当然还有中断的共享,这里没有体现了。

字符设备

字符设备的驱动简单模板如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/proc_fs.h>
#include <linux/device.h> 
#include <linux/slab.h>
#include <asm/uaccess.h> 
#include <asm/io.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/stat.h>

#include <linux/workqueue.h>
#include <linux/spinlock_types.h>
#include <linux/clk.h>
#include <plat/timer.h>


#define DEV_NAME  "template_cdev"

static int dev_major = 0;
static int dev_minor = 0;
static int NUMBER_DEVICES = 1;

struct class *cdev_class = NULL;

dev_t cdev_devno;

struct cdev_dev {
	struct cdev cdev;
};

struct cdev_dev *cdev_device = NULL;

static volatile int cdev_interrupt_flag = 0;

static DEFINE_SEMAPHORE(cdev_lock);
static DEFINE_SEMAPHORE(cdev_write_lock);

static int cdev_open(struct inode * inode , struct file * filp)
{
	struct cdev_dev *dev = NULL;
	
	dev = container_of(inode->i_cdev, struct cdev_dev, cdev);
	
	if (dev == NULL){
		printk(KERN_EMERG "DEV == null\n");
		return  -ENXIO;
	}
		
	filp->private_data = dev;

	if(down_trylock(&cdev_lock)) {
		printk("template_cdev is busy,please try later\n");
		return -EBUSY;		
	}
}
static int cdev_release(struct inode * inode, struct file *filp)
{	

	struct cdev_dev *i_dev = filp->private_data;

	up(&cdev_lock);
	
 	return 0;
}

static ssize_t cdev_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{	
	int ret;

	struct cdev_dev *i_dev = filp->private_data;

	return ret;
}

static ssize_t cdev_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
		int ret = 0;
		
		struct cdev_dev *i_dev = filp->private_data;
		
		return ret;
}


struct file_operations cdev_fops = {
	.owner	= 	THIS_MODULE,
	.open	= 	cdev_open,
	.release	= 	cdev_release,
	.read	= 	cdev_read,
	.write	=	cdev_write,
};

static int  cdev_setup(struct class *dev_class, struct cdev_dev *dev, int index)
{
	int err = 0;
	struct device *class_dev = NULL;
	int devno = MKDEV(dev_major, index);

	if ((NULL == dev_class) || (NULL == dev)) {
		pr_err("The parameter is NULL!\n");
		return -EFAULT;
	}

	cdev_init(&dev->cdev, &cdev_ops);
	dev->cdev.owner = THIS_MODULE;

	err = cdev_add(&dev->cdev, devno, 1);
	 printk (KERN_WARNING " cdev_setup\n");
	if (err) {
		pr_err("Error %d adding %s%d", err, DEV_NAME, index);
		return err;
	}

	class_dev = device_create(dev_class, NULL, devno,"template cdev",NULL)
	if(IS_ERR(class_dev)) {
		pr_err("class_device_create() failure!\n");
		err = -EFAULT;
	}

	return err;
}

static __init int cdev_init(void)
{
	int ret;

	   	if (dev_major) {
			cdev_devno = MKDEV(dev_major, dev_minor);
			ret = register_chrdev_region(cdev_devno, NUMBER_DEVICES, DEV_NAME);
		} else {
			ret = alloc_chrdev_region(&cdev_devno, dev_minor, NUMBER_DEVICES, DEV_NAME);
			dev_major = MAJOR(cdev_devno);
		} 
		if (ret<0) {
	           printk (KERN_WARNING "can't get major number %d\n", dev_major);
	           return ret;
		}
		
		cdev_device = kzalloc(NUMBER_DEVICES*sizeof(struct cdev_dev), GFP_KERNEL);
		if (NULL == cdev_device){
			ret = -ENOMEM;
			goto fail_request_region;
		}	

		cdev_class = class_create(THIS_MODULE, DEV_NAME);  
		if (IS_ERR(cdev_class)) {
		 	printk("Err: failed in creating template cdev class.\n");
			ret = -PTR_ERR(cdev_class);
		      	goto fail_alloc;
		}

		ret = cdev_setup(cdev_class, cdev_device, dev_minor);
		if (ret != 0) {
			goto fail_setup;
		}			

		return 0;
		
fail_setup:
	class_destroy(cdev_class);
	
fail_alloc:
	if (cdev_device != NULL) {
		kfree(cdev_device);
		cdev_device = NULL;
	}

fail_request_region:
	unregister_chrdev_region(cdev_devno, NUMBER_DEVICES);
		
	return ret;
}

static void  cdev_exit(void)
{
	dev_t devno = MKDEV(dev_major, dev_minor);

	device_destroy(cdev_class, devno);
	cdev_del(&cdev_device->cdev);
	class_destroy(cdev_class);
	kfree(cdev_device);
	cdev_device = NULL;
	unregister_chrdev_region(devno, NUMBER_DEVICES);
}

module_init(cdev_init);
module_exit(cdev_exit);
MODULE_LICENSE("GPL");

字符设备之上的i2c设备

设备树如下:

/ {
	mode = “ABCD corp Board”;
	compatible = “ABCD,exp”;
	chosen = {
		bootargs = “console=ttySA0 init=/linuxrc root=…”
	};
	apb@80000000{
		i2c0:i2c80006000{
		ak4951:code@5{
			compatible=”A, ak4951”;
			reg =<0x12>;
		};
		i2c2: i2c@e8007000 {
			compatible = "A,i2c";
			status = "ok";
			
			Template: Template@48 {
				compatible = "Template_i2c";
				reg = <0x48>;
			};
	
	Template {		
		Template_i2c= <&Template>;
		irq_pin = <13>;
	};

};


设备树信息查看,uboot下工具

# fdt getnode /
/
/chosen
/aliases
/memory
/cpus
/cpus/cpu@0
/apb@e8000000
/apb@e8000000/timer@e800b000
/apb@e8000000/i2c@e8007000/Template@90
/ahb@e0000000
/ahb@e0000000/dma@e0005000
/ahb@e0000000/nand@e0001000
/ahb@e0000000/uart@e0032000
...

i2c外设驱动

如果不是基于设备树方法,则会走register_driver路线,在这个函数中prob额方法会被调用,该函数完成扫描设备链表,查找和驱动是否匹配,如果匹则会进行绑定。基于设备树方法,设备信息的获取是通过解析设备树实现的。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/proc_fs.h>
#include <linux/device.h> 
#include <linux/slab.h>
#include <asm/uaccess.h> 
#include <asm/io.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/of_i2c.h>
#include <linux/gpio.h> 
#include <linux/io.h>
#include <linux/stat.h>

#include <linux/workqueue.h>
#include <linux/spinlock_types.h>
#include <linux/clk.h>
#include <plat/timer.h>

#define template_i2c_DEV_NAME  "template_i2c"

static int dev_major = 0;
static int dev_minor = 0;
static int NUMBER_DEVICES = 1;

struct class *template_i2c_class = NULL;

dev_t template_i2c_devno;

struct template_i2c_dev {
	int irq;
	unsigned int irq_pin;
	struct cdev cdev;
	struct i2c_client *template_i2c_client;
};

struct template_i2c_dev *template_i2c_device = NULL;

static int i2c_write_reg(struct i2c_client *client, u8 reg, u8 val);


static int i2c_write_reg(struct i2c_client *client, u8 reg, u8 val)
{
	int err = 0;

	struct i2c_msg msg[1];
	u8 data[2];

	if (!client->adapter) {
		err = -ENODEV;
		goto out;
	} else {
		msg[0].addr = client->addr;
		msg[0].flags = 0;
		msg[0].len = 2;
		msg[0].buf = data;
		data[0] = reg;
		data[1] = val;
		err = i2c_transfer(client->adapter, msg, 1);
		if(err < 0) {
			err = -EIO;
			goto out;
		}
	}
	return 0;
out:
	return err;
}

static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
	int err = 0;
	struct i2c_msg msg[2] = {
		{
			.addr = client->addr,
			.flags = 0,
			.len = 1,
			.buf = &reg,
		}, {
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = 1,
			.buf = val,
		}
	};
	
	if (!client->adapter) {
		err = -ENODEV;
		goto out;
	} else {
		err = i2c_transfer(client->adapter, msg, 2);
		if(err < 0) {
			err = -EIO;
			goto out;
	       }
     }
out:
	return err;
}


static int template_i2c_open(struct inode * inode , struct file * filp)
{
	struct template_i2c_dev *dev = NULL;

	dev = container_of(inode->i_cdev, struct template_i2c_dev, cdev);

	if (dev == NULL){
		printk(KERN_EMERG "DEV == null\n");
		return  -ENXIO;
	}
		
	filp->private_data = dev;

	if(down_trylock(&template_i2c_lock)) {
		printk("template_i2c_dev is busy,please try later\n");
		return -EBUSY;		
	}

	if (gpio_request(dev->irq_pin, "irq_line") < 0) {
		printk(KERN_ERR "fail to requet gpio%d.\r\n", dev->irq_pin);
		return -EIO;
	}

	if(gpio_direction_input(dev->irq_pin) < 0) {
		printk(KERN_ERR "set GPIO%d to input failed.\r\n", dev->irq_pin);
		return -EIO;
	}
	
	printk(KERN_EMERG "template_i2c_open!request_irq\n");
	if (request_irq(dev->irq, &template_i2c_irq, IRQ_TYPE_EDGE_FALLING, "template_i2c", template_i2c_device)) {	
		printk("request_irq failed!\n");
		free_irq(dev->irq, NULL);
		return -EBUSY;
    	}
	
	return 0;
}

static int template_i2c_release(struct inode * inode, struct file *filp)
{	

	struct template_i2c_dev *i_dev = filp->private_data;

	gpio_free(i_dev->irq_pin);

	free_irq(i_dev->irq, i_dev);

	up(&template_i2c_lock);
	
 	return 0;
}

static ssize_t template_i2c_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{	
	int err;

	return err;
}

static ssize_t template_i2c_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	int i = 0;
	
	return i;
}


struct file_operations template_i2c_fops = {
	.owner	= 	THIS_MODULE,
	.open	= 	template_i2c_open,
	.release	= 	template_i2c_release,
	.read	= 	template_i2c_read,
	.write	=	template_i2c_write,
};


static int of_template_i2c_parse(struct template_i2c_dev *i2cdev)
{
	int rval = 0;
	struct device_node *of_node, *i2c_node;

	if (NULL == i2cdev) {
		return -EINVAL;
	}
	
	of_node = of_find_node_by_path("/template_i2c");
	if (NULL == of_node) {
		pr_err("get template_i2c device-tree error!\n");
		return -ENXIO;
	} else {
			i2c_node = of_parse_phandle(of_node, "template_i2c_i2c", 0);
			if (NULL == i2c_node) {
				pr_err("get i2c_client device-tree property error!\n");
				return -ENXIO;
			}
	}

	i2cdev->template_i2c_client = of_find_i2c_device_by_node(i2c_node);
	if (NULL == i2cdev->template_i2c_client) {
		pr_err("get i2c_client device error!\n");
		return -ENXIO;
	}

	rval= of_property_read_u32(of_node, "irq_pin", &i2cdev->irq_pin);
	
	printk(KERN_EMERG "The PIn is :%d", i2cdev->irq_pin);

	i2cdev->irq = gpio_to_irq(i2cdev->irq_pin);	

	return rval;
}

static int  template_cdev_setup(struct class *dev_class, struct template_i2c_dev *dev, int index)
{
	int err = 0;
	struct device *class_dev = NULL;
	int devno = MKDEV(dev_major, index);

	if ((NULL == dev_class) || (NULL == dev)) {
		pr_err("The parameter is NULL!\n");
		return -EFAULT;
	}

	cdev_init(&dev->cdev, &template_i2c_fops);
	dev->cdev.owner = THIS_MODULE;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err) {
		pr_err("Error %d adding %s%d", err, template_i2c_DEV_NAME, index);
		return err;
	}

	class_dev = device_create(dev_class, NULL, devno, "template cdev", NULL);
	if(IS_ERR(class_dev)) {
		pr_err("class_device_create() failure!\n");
		err = -EFAULT;
	}

	return err;
}

static __init int template_i2c_init(void)
{
	int ret;

	   	if (dev_major) {
			template_i2c_devno = MKDEV(dev_major, dev_minor);
			ret = register_chrdev_region(template_i2c_devno, NUMBER_DEVICES, template_i2c_DEV_NAME);
		} else {
			ret = alloc_chrdev_region(&template_i2c_devno, dev_minor, NUMBER_DEVICES, template_i2c_DEV_NAME);
			dev_major = MAJOR(template_i2c_devno);
		} 
		if (ret<0) {
	           printk (KERN_WARNING "can't get major number %d\n", dev_major);
	           return ret;
		}
		
		template_i2c_device = kzalloc(NUMBER_DEVICES*sizeof(struct template_i2c_dev), GFP_KERNEL);
		if (NULL == template_i2c_device){
			ret = -ENOMEM;
			goto fail_request_region;
		}	

		if ((ret = of_template_i2c_parse(template_i2c_device)) < 0) {
			printk("Err: parse template_i2c_device node failed!\n");
			goto fail_alloc;
		}

		template_i2c_class = class_create(THIS_MODULE, template_i2c_DEV_NAME);  //类名为template_i2c_class
		if (IS_ERR(template_i2c_class)) {
			ret = -PTR_ERR(template_i2c_class);
		      	goto fail_alloc;
		}

		ret = template_cdev_setup(template_i2c_class, template_i2c_device, dev_minor);
		if (ret != 0) {
			goto fail_setup;
		}			

		return 0;
		
fail_setup:
		class_destroy(template_i2c_class);
fail_alloc:
	if (template_i2c_device != NULL){
		kfree(template_i2c_device);
		template_i2c_device = NULL;
	}

fail_request_region:
	unregister_chrdev_region(template_i2c_devno, NUMBER_DEVICES);
		
	return ret;
}

static void  template_i2c_exit(void)
{
	dev_t devno = MKDEV(dev_major, dev_minor);

	device_destroy(template_i2c_class, devno);
	cdev_del(&template_i2c_device->cdev);
	class_destroy(template_i2c_class);
	kfree(template_i2c_device);
	template_i2c_device = NULL;
	unregister_chrdev_region(devno, NUMBER_DEVICES);
}

module_init(template_i2c_init);
module_exit(template_i2c_exit);
MODULE_LICENSE("GPL");

中断使用工作队列推迟执行

包括workqueue、poll以及semaphore使用。
<pre name="code" class="cpp">#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/proc_fs.h>
#include <linux/device.h> 
#include <linux/slab.h>
#include <asm/uaccess.h> 
#include <asm/io.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/of_i2c.h>
#include <linux/gpio.h> 
#include <linux/io.h>
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#incldue <linux/semaphore.h>

#include <linux/workqueue.h>
#include <linux/spinlock_types.h>
#include <linux/clk.h>
#include <plat/timer.h>

#define template_i2c_DEV_NAME  "template_i2c"

static int dev_major = 0;
static int dev_minor = 0;
static int NUMBER_DEVICES = 1;

struct class *template_i2c_class = NULL;

dev_t template_i2c_devno;

struct template_i2c_dev {
	int irq;
	unsigned int irq_pin;
	struct cdev cdev;
	struct i2c_client *template_i2c_client;
	struct semaphore sem;
	wait_queue_head_t recv_wq;
	struct workqueue_struct *recv_work;
	struct work_struct restart;
};

struct template_i2c_dev *template_i2c_device = NULL;

static DECLARE_WAIT_QUEUE_HEAD(waitq);
static void template_delay_workqueue(struct work_struct *data)
{
	stuct template_i2c_device *i_dev = container_of(data, struct template_i2c_dev, restart);
	if (down_interruptible(&i_dev->sem) != 0)
	printk(" semaphore get failed~!\n");
	....
	wake_up_interruptible(&waitq);
	up(&i_dev->sem)
}
static int i2c_write_reg(struct i2c_client *client, u8 reg, u8 val);

static irqreturn_t template_i2c_irq(int irq, void *dev_id)
{
queue_work(template_i2c_device->recv_work, & template_i2c_device->restart);
...
return IRQ_RETVAL(IRQ_HANDLED); 
}
static int i2c_write_reg(struct i2c_client *client, u8 reg, u8 val)
{	int err = 0;
	struct i2c_msg msg[1];
	u8 data[2];
	if (!client->adapter) {
		err = -ENODEV;
		goto out;
	} else {
		msg[0].addr = client->addr;
		msg[0].flags = 0;
		msg[0].len = 2;
		msg[0].buf = data;
		data[0] = reg;
		data[1] = val;
		err = i2c_transfer(client->adapter, msg, 1);
		if(err < 0) {
			err = -EIO;goto out;}
		}
		return 0;
out:
	return err;
}

static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
	int err = 0;
	struct i2c_msg msg[2] = {
		{.addr = client->addr,.flags = 0,.len = 1,.buf = &reg,}, 
		{.addr = client->addr,.flags = I2C_M_RD,.len = 1,.buf = val,}
	};
	if (!client->adapter) {
		err = -ENODEV;
		goto out;
	} else {
		err = i2c_transfer(client->adapter, msg, 2);
		if(err < 0) {
			err = -EIO;
			goto out; } 
	}
out:
	return err;
}
static int template_i2c_open(struct inode * inode , struct file * filp)
{
	struct template_i2c_dev *dev = NULL;
	dev = container_of(inode->i_cdev, struct template_i2c_dev, cdev);
	if (dev == NULL){
		printk(KERN_EMERG "DEV == null\n");
		return -ENXIO;
	}
	filp->private_data = dev;
	if(down_trylock(&template_i2c_lock)) {
		printk("template_i2c_dev is busy,please try later\n");return -EBUSY;
	}
	if (gpio_request(dev->irq_pin, "irq_line") < 0) {
		printk(KERN_ERR "fail to requet gpio%d.\r\n", dev->irq_pin);
		return -EIO;
	}
	if(gpio_direction_input(dev->irq_pin) < 0) {
		printk(KERN_ERR "set GPIO%d to input failed.\r\n", dev->irq_pin);
		return -EIO;
	}
	printk(KERN_EMERG "template_i2c_open!request_irq\n");
	if (request_irq(dev->irq, &template_i2c_irq, IRQ_TYPE_EDGE_FALLING, "template_i2c", template_i2c_device)) {
		printk("request_irq failed!\n");free_irq(dev->irq, NULL);
		return -EBUSY; 
	}return 0;
}

static int template_i2c_release(struct inode * inode, struct file *filp)
{
	struct template_i2c_dev *i_dev = filp->private_data;
	gpio_free(i_dev->irq_pin);
	free_irq(i_dev->irq, i_dev);
	up(&template_i2c_lock); return 0;
}
static ssize_t template_i2c_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	int err;
	return err;
	
}
static ssize_t template_i2c_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{	int i = 0;
	return i;
}

unsigned int template_i2c_poll(struct file *file, struct poll_table_struct *wait)
{
poll_wait(file, &waitq, wait);
...
mask |= POLLIN;
return mask;
}

struct file_operations template_i2c_fops = {
	.owner	= 	THIS_MODULE,
	.open	= 	template_i2c_open,
	.release	= 	template_i2c_release,
	.read	= 	template_i2c_read,
	.write	=	template_i2c_write,
	.poll = template_i2c_poll;
};


static int of_template_i2c_parse(struct template_i2c_dev *i2cdev)
{
	int rval = 0;
	struct device_node *of_node, *i2c_node;

	if (NULL == i2cdev) {
		return -EINVAL;
	}
	
	of_node = of_find_node_by_path("/template_i2c");
	if (NULL == of_node) {
		pr_err("get template_i2c device-tree error!\n");
		return -ENXIO;
	} else {
			i2c_node = of_parse_phandle(of_node, "template_i2c_i2c", 0);
			if (NULL == i2c_node) {
				pr_err("get i2c_client device-tree property error!\n");
				return -ENXIO;
			}
	}

	i2cdev->template_i2c_client = of_find_i2c_device_by_node(i2c_node);
	if (NULL == i2cdev->template_i2c_client) {
		pr_err("get i2c_client device error!\n");
		return -ENXIO;
	}

	rval= of_property_read_u32(of_node, "irq_pin", &i2cdev->irq_pin);
	
	printk(KERN_EMERG "The PIn is :%d", i2cdev->irq_pin);

	i2cdev->irq = gpio_to_irq(i2cdev->irq_pin);	

	return rval;
}

static int  template_cdev_setup(struct class *dev_class, struct template_i2c_dev *dev, int index)
{
	int err = 0;
	struct device *class_dev = NULL;
	int devno = MKDEV(dev_major, index);

	if ((NULL == dev_class) || (NULL == dev)) {
		pr_err("The parameter is NULL!\n");
		return -EFAULT;
	}

	cdev_init(&dev->cdev, &template_i2c_fops);
	dev->cdev.owner = THIS_MODULE;

	err = cdev_add(&dev->cdev, devno, 1);

	if (err) {
		pr_err("Error %d adding %s%d", err, template_i2c_DEV_NAME, index);
		return err;
	}

	class_dev = device_create(dev_class, NULL, devno, "template cdev", NULL);
	if(IS_ERR(class_dev)) {
		pr_err("class_device_create() failure!\n");
		err = -EFAULT;
	}

	return err;
}

static __init int template_i2c_init(void)
{
	int ret;

	   	if (dev_major) {
			template_i2c_devno = MKDEV(dev_major, dev_minor);
			ret = register_chrdev_region(template_i2c_devno, NUMBER_DEVICES, template_i2c_DEV_NAME);
		} else {
			ret = alloc_chrdev_region(&template_i2c_devno, dev_minor, NUMBER_DEVICES, template_i2c_DEV_NAME);
			dev_major = MAJOR(template_i2c_devno);
		} 
		if (ret<0) {
	           printk (KERN_WARNING "can't get major number %d\n", dev_major);
	           return ret;
		}
		
		template_i2c_device = kzalloc(NUMBER_DEVICES*sizeof(struct template_i2c_dev), GFP_KERNEL);
		if (NULL == template_i2c_device){
			ret = -ENOMEM;
			goto fail_request_region;
		}	

		if ((ret = of_template_i2c_parse(template_i2c_device)) < 0) {
			printk("Err: parse template_i2c_device node failed!\n");
			goto fail_alloc;
		}

		template_i2c_class = class_create(THIS_MODULE, template_i2c_DEV_NAME);  //类名为template_i2c_class
		if (IS_ERR(template_i2c_class)) {
			ret = -PTR_ERR(template_i2c_class);
		      	goto fail_alloc;
		}

		ret = template_cdev_setup(template_i2c_class, template_i2c_device, dev_minor);
		if (ret != 0) {
			goto fail_setup;
		}			

	INIT_WORK(&template_i2c_device->restart,template_delay_workqueue );
	init_waitqueue_head(&template_i2c_device->recv_wq);
	sema_init(&template_i2c_device->sem,1);
	return 0;
		
fail_setup:
		class_destroy(template_i2c_class);
fail_alloc:
	if (template_i2c_device != NULL){
		kfree(template_i2c_device);
		template_i2c_device = NULL;
	}

fail_request_region:
	unregister_chrdev_region(template_i2c_devno, NUMBER_DEVICES);
		
	return ret;
}

static void  template_i2c_exit(void)
{
	dev_t devno = MKDEV(dev_major, dev_minor);

	device_destroy(template_i2c_class, devno);
	cdev_del(&template_i2c_device->cdev);
	class_destroy(template_i2c_class);
	kfree(template_i2c_device);
	template_i2c_device = NULL;
	unregister_chrdev_region(devno, NUMBER_DEVICES);
}

module_init(template_i2c_init);
module_exit(template_i2c_exit);
MODULE_LICENSE("GPL");


 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shichaog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值