i2c驱动之普通设备驱动2(推荐设备驱动方法)

        与前面普通设备驱动1方法不同,这里的普通设备驱动利用i2c-core.c提供的i2c_transfer方法来实现设备驱动,而不是普通设备驱动1里面的通过操纵s3c2440的i2c寄存器来与设备通信


 一、采用友善之臂的2.6.32.2内核,需要修改/linux-2.6.32.2/arch/arm/mach-s3c2440/mach-mini2440.c文件

1)添加 #include<linux/i2c.h>

2)在static struct platform_device *mini2440_devices[]前添加struct i2c_board_info i2c_devices[]结构体

//add
static struct i2c_board_info i2c_devices[] __initdata={
	{I2C_BOARD_INFO("at24c08", 0x50), },
};
/* devices we initialise */
static struct platform_device *mini2440_devices[] __initdata = {
	&s3c_device_usb,
	&s3c_device_rtc,
	&s3c_device_lcd,
	&s3c_device_wdt,
	&s3c_device_i2c0,//没有修改
	&s3c_device_iis,
	&mini2440_device_eth,
	&s3c24xx_uda134x,
	&s3c_device_nand,
	&s3c_device_sdi,
	&s3c_device_usbgadget,
};

3)在mini2440_machine_init(void)函数内添加i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));函数

static void __init mini2440_machine_init(void)
{
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices)); //add

#if defined (LCD_WIDTH)
	s3c24xx_fb_set_platdata(&mini2440_fb_info);
#endif
	s3c_i2c0_set_platdata(NULL);

	s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);

	s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;
	s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;
	platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
	s3c_pm_init();
}

二、在linux2.6.32.2/drivers/i2c/chips/目录下添加at24c08.c设备驱动文件,并对应的修改Kconfig和Makefile文件

#include  <linux/init.h>
#include  <linux/module.h>
#include  <linux/i2c.h>
#include  <linux/fs.h>
#include  <linux/cdev.h>
#include  <linux/device.h>
#include  <linux/kdev_t.h>
#include  <asm/uaccess.h>

#define   major  155
static  struct  i2c_driver  at24c08_driver;
static  struct  i2c_adapter  *at24c08_adapter;
static  unsigned  short  addr;
static  struct  class  at24c08_cls  =  {
					.name =  "at24c08_cls",
				};

ssize_t  at24c08_read  (struct  file  *filp,  char  __user  *buf,  size_t  sz,  loff_t  *off)
{
	struct  i2c_msg  msg[2];
	unsigned  char  args,  data;

	if  (sz  !=  1)
		return  -EINVAL;
	copy_from_user((void  *)&args,  buf,  1);
	/* 先传读地址 */
	msg[0].addr =  addr;
	msg[0].buf =  &args;
	msg[0].len =  1;
	msg[0].flags =  0;

	/* 再 读 */
	msg[1].addr =  addr;
	msg[1].buf =  &data;
	msg[1].len =  1;
	msg[1].flags =  1; /* 读 */
	if  (2  ==  i2c_transfer(at24c08_adapter,  msg,  2))  {
		/* 读成功 */
		copy_to_user((void  *)buf,  &data,  1);
		return  1;
	}
	else
		return  -EIO;
}

ssize_t  at24c08_write  (struct  file  *filp,  const  char  __user  *buf,  size_t  sz,  loff_t  *off)
{
	struct  i2c_msg  msg;
	unsigned  char  args[2];

	copy_from_user((void  *)&args,  buf,  2);

	/* args[0] = addr, args[1] = val */
	msg.addr  =  addr;
	msg.buf =  args;
	msg.len =  2;
	msg.flags  =  0; /* 写 */
	if(1  ==  i2c_transfer(at24c08_adapter,&msg,  1))
		return  2;
	else
		return  -EIO;

}

static  struct  file_operations  at24c08_fops  =  {
	.owner  =  THIS_MODULE,
	.read    =  at24c08_read,
	.write  =  at24c08_write,
};

static  struct  cdev  cdev;
static  int  at24c08_probe(struct  i2c_client  *client,  const  struct  i2c_device_id  *dev_id)
{
	register_chrdev_region(major,  1,  "at24c08");
	cdev_init(&cdev,  &at24c08_fops);
	cdev_add(&cdev,  MKDEV(major,  0),  1);
	class_register(&at24c08_cls);
	device_create(&at24c08_cls,  NULL,  MKDEV(major,  0),  NULL,  "at24c08_dev");
	return    0;
}

static  int  at24c08_remove(struct  i2c_client  *client)
{
	device_destroy(&at24c08_cls,  MKDEV(major,  0));
	class_destroy(&at24c08_cls);
	cdev_del(&cdev);
	unregister_chrdev_region(MKDEV(major,  0),  1);

	return    0;
}

static  int  at24c08_detect(struct  i2c_client  *client,  int  kind,  struct  i2c_board_info  *bd_info)
{
	strcpy(bd_info->type,    "at24c08");
	addr  =  bd_info->addr  =  client->addr;
	at24c08_adapter  =  client->adapter;
	return    0;
}

static  const  struct  i2c_device_id  at24c08_id[]  =  {
	{"at24c08",  0},
	{}
};

static  unsigned  short  ignore[]            =  {  I2C_CLIENT_END  };

static  const  unsigned  short  normal_i2c[]  =  {0x50,  I2C_CLIENT_END};

static  const  struct  i2c_client_address_data  addr_data  =  {
	.normal_i2c =  normal_i2c,
	.probe =  ignore,
	.ignore =  ignore,
};
static  struct  i2c_driver  at24c08_driver=  {
	.probe =  at24c08_probe,
	.remove =  at24c08_remove,
	.driver  =  {
			.name  =  "at24c08",
			},
	.id_table =  at24c08_id,
	.detect =  at24c08_detect,
	.address_data =  &addr_data,
	.class      =  I2C_CLASS_HWMON  |  I2C_CLASS_SPD,
};

static  int  at24c08_init(void)
{
	i2c_add_driver(&at24c08_driver);
	return  0;
}

static  void  at24c08_exit(void)
{
	i2c_del_driver(&at24c08_driver);
	return  ;
}

module_init(at24c08_init);
module_exit(at24c08_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("100ask.net Young");

以上要说明的是为什么insmod at24c08.ko之后会在/dev/目录下自动创建/dev/at24c08_dev文件节点

主要归功于两个函数

static  struct  class  at24c08_cls  =  { .name =  "at24c08_cls", };

class_register(&at24c08_cls);
device_create(&at24c08_cls,  NULL,  MKDEV(major,  0),  NULL,  "at24c08_dev");

内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。

与class_create等价的函数是class_register,只是他们对应的注销函数不同class_unregister和class_destory。


三、在linux2.6.32.2目录下make menuconfig进行内核定制之后再make则会生成i2c-core.ko,i2c-s3c2440.ko,at24c08.ko等文件,将三个文件都insmod到内核中,在/dev/目录下会自动生成at24c08_dev文件节点,但是在重启之后会消失,因为insmod是动态加载的,如果在重启后还要有文件节点,则要将加载命令写到启动脚本中去。



你可能感兴趣的:(i2c驱动之普通设备驱动2(推荐设备驱动方法))