好想哭,写了2个多小时的文章一下因为没保存一下弄没了,,,算了,主要是关于i2c驱动的,,把代码粘上来算了,,哭吧
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kdev_t.h>
#include <linux/notifier.h>
#include <linux/genhd.h>
#include <linux/kallsyms.h>
#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/errno.h>
/*1..分配i2c_driver */
/*2..设置i2c_driver */
#define at24cxx_EEPROM_SIZE 8
#define AT24C02_MAJOR 250 //设备的主设备号
#define DEBUG 1 //调试打印控制开关
static int at24c02_major = AT24C02_MAJOR;
/*声明at24c02结构体
*管理24c02的设备信息,,client信息等
*/
struct at24c02_dev
{
struct i2c_client *client;
unsigned int addr;
char name[30];
unsigned short current_pointer;//当前读写地址
struct cdev cdev;//cdev结构,,最后要注册它为字符设备
};
struct at24c02_dev *at24c02_devp;
static int at24c02_open(struct inode *inode,struct file *filp)
{
filp->private_data=at24c02_devp;//打开函数,这个函数可以写可以不写,不写的话系统会有默认的open函数
printk("at24c02_open is called\n");//这个函数的作用在于,当执行这个函数时
#if DEBUG // file *filp被传入属于本驱动的结构at24c02_devp
printk(KERN_NOTICE"open decvice is called\n"); //这个函数没有也可以的,,????
#endif
return 0;
}
static ssize_t at24c02_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos)
{
int transferred = 0; //读到的字节个数
unsigned char my_buff[512]={0}; //读缓冲区
unsigned char ret=0;
int i = 0;
/* 取出驱动结构,因为前面open函数里给filp赋值了*/
struct at24c02_dev *dev = (struct at24c02_dev *)filp->private_data;
copy_from_user(my_buff,buf,1);
//dev->current_pointer = *ppos; //属于当前的读地址位置
dev->current_pointer = my_buff[0];//这个地方根据程序需要改了下
/*检测adapter是否支持读写功能
*start inline int i2c_check_functionality(struct adapter*adp,u32 func)
*/
if(i2c_check_functionality(dev->client->adapter,I2C_FUNC_SMBUS_READ_BYTE_DATA))
{
while(transferred < size)
{
/*s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command);*/
/*将会调用i2c_smbus_xfer*/
/*smbus读函数,读的设备为dev->client设备地址,设备名字,设备采用的算法,属于
的适配器都在client结构中,读的地址为current_pointer +i,会随着i的增加而增加*/
ret = i2c_smbus_read_byte_data(dev->client,dev->current_pointer +i);
my_buff[i++] = (unsigned char) ret; //返回读到的值,赋给缓冲数组
transferred +=1;
#if DEBUG
printk(KERN_NOTICE"the my_buff[%d] is %x,transferred is %d\n",i,my_buff[i--],transferred);
#endif
}
/* 从内核拷贝数据到用户空间*/
copy_to_user(buf,(void *)my_buff,transferred);
dev->current_pointer += transferred; //记录数据读写位置
}
return transferred;
}
static ssize_t at24c02_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
int i = 0;
int transferred = 0; //成功写的字节个数
int ret;
unsigned char my_buff[512]; //写缓冲区
/* 取出驱动结构,因为前面open函数里给filp赋值了*/
struct at24c02_dev *dev = (struct at24c02_dev *)filp->private_data;
/*初始写位置*/
dev->current_pointer = *ppos;
/*检测adapter是否支持读写功能
*start inline int i2c_check_functionality(struct adapter*adp,u32 func)
*/
if(i2c_check_functionality(dev->client->adapter,I2C_FUNC_SMBUS_BYTE_DATA))
{
/*从用户空间拷贝数据到内核空间*/
copy_from_user(my_buff,buf,size);
dev->current_pointer=my_buff[0];
/*因为读的时候有一个是地址所以-1*/
while(transferred<(size-1))
{
/*s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command);*/
/*将会调用i2c_smbus_xfer*/
/*smbus写函数,写的设备为dev->client设备地址,设备名字,设备采用的算法,属于
的适配器都在client结构中,写的地址为current_pointer +i,会随着i的增加而增加*/
ret=i2c_smbus_write_byte_data(dev->client, dev->current_pointer+i, my_buff[i+1]);
#if DEBUG
printk(KERN_NOTICE"write data %d-----my_buff%x\n",i,my_buff[i+1]);
#endif
i+=1;
transferred +=1;
}
dev->current_pointer +=transferred;
}
return transferred;
}
/*io命令控制函数*/
static int at24c02_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg)
{
return 0;
}
/*设备释放函数,,在什么时候这个函数会执行?????*/
static int at24c02_release(struct inode*inodep,struct file *filp)
{
filp->private_data = NULL;
return 0;
}
/*驱动中很重要的结构,注册一个驱动时就是把这个结构体中包含的
函数指针赋给设备节点结构???*/
static const struct file_operations at24c02_fops=
{
.owner = THIS_MODULE,
.open=at24c02_open,
.read=at24c02_read,
.write=at24c02_write,
.ioctl=at24c02_ioctl,
.release=at24c02_release,
};
/*创建字符设备函数,,完成字符设备文件的注册*/
static void at24c02_setup_cdev(struct at24c02_dev *dev, int index)
{
/*MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
产生其主设备号*/
int err, devnum = MKDEV(at24c02_major, index);
#if DEBUG
printk(KERN_NOTICE"the devnum is %d\n",devnum);
#endif
/* 初始化一个cdev结构主要是,并添加操作函数集
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
*/
cdev_init(&dev->cdev, &at24c02_fops);
dev->cdev.owner = THIS_MODULE;
/*由上面的分析可知,这句话多余了
cdev_init函数里已经绑定了这个结构
*/
dev->cdev.ops = &at24c02_fops;
/* add a char device to the system
增加一个字符设备到系统
具体怎么加入的明天老师也许会讲
*/
err = cdev_add(&dev->cdev, devnum, 1);
if (err)
printk(KERN_NOTICE"Error %d adding at24c02 %d", err, index);
}
static struct class *at24cxx_class;
/*
* Called when a at24c02 device is matched with this driver
*/
static int __devinit at24cxx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
printk(KERN_NOTICE"at24c02 probe \n");
/*主设备号*/
dev_t devnum = MKDEV(at24c02_major,0);
if (at24c02_major)
{
/*如果定义了主设备号,那么就手动注册
register a range of device numbers
第二个参数代表几个设备号
从devnum开始的几个设备号
__register_chrdev_region
*/
ret = register_chrdev_region(devnum,1,"at24c02");
}
else
{
/*动态分配设备号到&devnum
从0号开始分配一个设备
*/
ret = alloc_chrdev_region(&devnum,0,1,"at24c02");
}
if(ret < 0)
return ret;
#if DEBUG
printk(KERN_NOTICE"device number is read\n");
#endif
/*动态分配内存
返回值为分配的内存的地址*/
at24c02_devp=kmalloc(sizeof(struct at24c02_dev),GFP_KERNEL);
if(!at24c02_devp)
{
ret=-ENOMEM;
goto fail_malloc;
}
#if DEBUG
printk(KERN_NOTICE"kmalloc is read\n");
#endif
/*设置这块内存值为0*/
memset(at24c02_devp,0,sizeof(struct at24c02_dev));
/*绑定设备到本模块的结构体*/
at24c02_devp->client=client;
/*创建字符设备*/
at24c02_setup_cdev(at24c02_devp,0);
/*create a struct class structure
This is used to create a struct class pointer that can then be used
in calls to device_create().
*/
at24cxx_class = class_create(THIS_MODULE, "at24c02");
/*判断返回的这个类结构是否是错误的*/
if (IS_ERR(at24cxx_class))
return -1;
/*creates a device and registers it with sysfs
*/
device_create(at24cxx_class, NULL, MKDEV(at24c02_major,0), NULL, "at24c02");
return 0;
fail_malloc:
/*This function will unregister a range of @count device numbers,*/
unregister_chrdev_region(devnum,1);
#if DEBUG
printk(KERN_NOTICE"fail_malloc\n");
#endif
return ret;
}
static int __devexit at24cxx_remove(struct i2c_client *client)
{
/*removes a device that was created with device_create()*/
device_destroy(at24cxx_class,MKDEV(at24c02_major,0));
/*destroys a struct class structure*/
class_destroy(at24cxx_class);
/*remove a cdev from the system*/
cdev_del(&at24c02_devp->cdev);
/*free previously allocated memory
Don't free memory not originally allocated by kmalloc()
or you will run into trouble.
*/
kfree(at24c02_devp);
/*This function will unregister a range of @count device numbers,
* starting with @from.
*/
unregister_chrdev_region(MKDEV(at24c02_major,0),1);
return 0;
}
/*由于一个i2c驱动可以用于多个设备,所以建一个设备表
驱动会根据这个表与适配器上挂载的设备名进行匹配
*/
static const struct i2c_device_id at24cxx_id[] = {
{ "at24c02", 0 },
{ }
};
/*一般在声明这种表时,都会加这一句
*/
MODULE_DEVICE_TABLE(i2c,at24cxx_id);
static struct i2c_driver at24c02_driver = {
.driver = {
.name = "at24cxx",
.owner =THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id,
/*.detect = at24cxx_detect,
.address_data = &addr_data, */
};
/*模块加载函数,,可能需要加__init标记为特殊函数
有待于研究*/
static int __init at24cxx_init(void)
{
#if DEBUG
printk(KERN_NOTICE"at24c02 is insmod\n");
#endif
/* There
* are two models for binding the driver to its device: "new style" drivers
* follow the standard Linux driver model and just respond to probe() calls
* issued if the driver core sees they match(); "legacy" drivers create device
* nodes themselves.*/
return i2c_add_driver(&at24c02_driver);
}
/*模块加载函数,,可能需要加__exit标记为特殊函数
有待于研究*/
static void __exit at24cxx_exit(void)
{
/*i2c_del_driver - unregister I2C driver*/
i2c_del_driver(&at24c02_driver);
#if DEBUG
printk(KERN_NOTICE"at24c02 is rmmod\n");
#endif
return ;
}
module_init(at24cxx_init);
module_exit(at24cxx_exit);
MODULE_LICENSE("GPL");
有了驱动程序后还需要注册设备,,,注册设备据说有两种方法,,第一种,,在板级文件中添加
static struct i2c_board_info i2c_devices[] __initdata={
{
I2C_BOARD_INFO("at24c02",0x50),
},
};
smdk2440_machine_init(void)
{
.......................................
i2c_register_board_info(0,i2c_devices,ARRAY_SIZE(i2c_devices));
}
}
我的不知道怎么回事这样做不行,,,现在还没弄懂为什么不行,,求教大师,,,,
第二种方法,,再写一个模块程序,注册设备到i2c总线,代码如下
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kdev_t.h>
#include <linux/notifier.h>
#include <linux/genhd.h>
#include <linux/kallsyms.h>
#include <linux/semaphore.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
static struct i2c_board_info at24cxx_info = {
I2C_BOARD_INFO("at24c02", 0x50),
};
static struct i2c_client *at24cxx_client;
static int at24cxx_dev_init(void)
{
struct i2c_adapter *i2c_adap;
printk(KERN_NOTICE"at24c02 device is insmod\n");
i2c_adap = i2c_get_adapter(0);
at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);
i2c_put_adapter(i2c_adap);
return 0;
}
static void at24cxx_dev_exit(void)
{
printk(KERN_NOTICE"at24c02 device is rmmod\n");
i2c_unregister_device(at24cxx_client);
}
module_init(at24cxx_dev_init);
module_exit(at24cxx_dev_exit);
MODULE_LICENSE("GPL");
编译后,,加载两个模块即可
测试程序
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
/*at24cxx_app r addr
*at24cxx_app w addr val
*/
void print_usage(char *file)
{
printf("%s r addr\n",file);
printf("%s w addr val \n",file);
}
int main(int argc,char **argv)
{
int fd;
unsigned char buf[2];
if((argc!=3)&&(argc!=4))
{
print_usage(argv[0]);
return -1;
}
fd =open("/dev/at24c02",O_RDWR);
if(fd<0)
{
printf("can't open /dev/at24cxx\n");
return -1;
}
if((strcmp(argv[1],"r"))==0)
{
buf[0]=strtoul(argv[2],NULL,0);
read(fd,buf,1);
printf("data:%c,%d,0x%2x\n",buf[0],buf[0],buf[0]);
}
else if((strcmp(argv[1],"w"))==0)
{
buf[0]=strtoul(argv[2],NULL,0);
buf[1]=strtoul(argv[3],NULL,0);
write(fd,buf,2);
}
else
{
printf("it is wrong");
}
return 0;
}