我本仁慈,奈何苍天不许
分类: LINUX
2013-12-26 16:55:49
【实验平台】
主机:Ubuntu 10.10
目标机:FS_S5PC100
目标机内核版本:2.6.35
交叉编译器版本:arm-none-linux-gnueabi-gcc-4.5.1
【实验步骤】
注意:在实验过程中"$"后的操作在主机上,"#"后的操作在开发板上
一、需要在下面目录下修改Kconfig文件,在里面加上红色字体这句话
config I2C_S3C2410
tristate "S3C2410 I2C Driver"
depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100
help
Say Y here to include support for I2C controller in the
Samsung S3C2410 based System-on-Chip devices.
/home/farsight/kernel/linux-2.6.35-farsight/drivers/i2c/busses
内核配置并重新编译内核
$ make menuconfig
Device Drivers --->
<*> I2C support --->
<*> I2C device interface
I2C Hardware Bus support --->
<*> S3C2410 I2C Driver
$ make zImage
二、使用内核i2c子系统构建驱动
1、 修改平台代码并重新编译内核
修改arch/arm/mach-s5pc100/mach-smdkc100.c
修改:
static struct i2c_board_info i2c_devs0[] __initdata = {
};
为:
static struct i2c_board_info i2c_devs0[] __initdata = {
{
I2C_BOARD_INFO("lm75", 0x48),
},
};
$ make zImage
2、 拷贝模块到linux下
将实验代码中的s5pc100_temp拷贝到/home/linux目录下并进入s5pc100_temp目录。
3、 编译模块
$ make
4、 编译应用程序
$ arm-none-linux-gnueabi-gcc test.c –o test
5、 拷贝驱动及应用程序到目标板上
$ cp s5pc100_temp.ko test /source/rootfs
6、 启动开发板后加载模块
# insmod s5pc100_temp.ko
7、 创建设备节点
# mknod /dev/lm75 c 250 0 //这里创建的设备文件名只需要跟测试程序里面打开的问价名一样即可,不必和加载进内核的驱动名相同
8、 测试
# ./test
这个值是16进制数,前两位代表度,后两位是小数点后的值,大家可以自己转换一下
驱动代码:
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_LICENSE ("GPL");
#define LM75_REG_CONF 0x01
static const u8 LM75_REG_TEMP[3] = {
0x00, /* input */
0x03, /* max */
0x02, /* hyst */
};
struct lm75_data
{
u16 temp[3]; /* Register values,
0 = input
1 = max
2 = hyst */
};
static int lm75_major = 250;
static int lm75_minor = 0;
static int number_of_devices = 1;
static dev_t devno = 0;
static struct cdev cdev;
static struct i2c_client *new_client;
struct lm75_data *data;
static int lm75_read_value(struct i2c_client *client)
{
struct i2c_msg msgs[2];
int status;
char buf1[2];
char buf2[2];
msgs[0].len = 1;
msgs[0].addr = client->addr; // lm75 设备地址
msgs[0].flags = 0;//write
msgs[0].buf = buf1;
msgs[0].buf[0] = LM75_REG_TEMP[0];
msgs[1].len = 2;//读出的数据
msgs[1].addr = client->addr;// lm75 设备地址
msgs[1].flags = I2C_M_RD;//read
msgs[1].buf = buf2;//存放返回值的地址。
status = i2c_transfer(client->adapter, msgs, 2);
if(status < 0)
return status;
// printk("1 = %2x %2x\n", buf2[0], buf2[1]);
return (buf2[0] << 8) | buf2[1];
}
static ssize_t lm75_read(struct file *file, char __user *buff, size_t count, loff_t *offset)
{
int status;
status = lm75_read_value(new_client);
if(status < 0)
{
return status;
}
printk("status = %x\n", status);
if(copy_to_user(buff, (char *)&status, sizeof(status)))
return -EFAULT;
return 0;
}
static int lm75_open(struct inode *inode, struct file *file)
{
return 0;
}
static int lm75_release(struct inode *inode, struct file *file)
{
return 0;
}
static struct file_operations lm75_fops = {
.owner = THIS_MODULE,
.read = lm75_read,
.open = lm75_open,
.release = lm75_release,
};
static int lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
new_client = client;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
return -EIO;
data = kzalloc(sizeof(struct lm75_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
devno = MKDEV(lm75_major, lm75_minor);
ret = register_chrdev_region(devno, number_of_devices, "lm75");
if(ret)
{
printk("failed to register device number\n");
goto err_register_chrdev_region;
}
cdev_init(&cdev, &lm75_fops);
cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev, devno, number_of_devices);
if(ret)
{
printk("failed to add device\n");
goto err_cdev_add;
}
return 0;
err_cdev_add:
unregister_chrdev_region(devno, number_of_devices);
err_register_chrdev_region:
kfree(data);
return ret;
}
static int lm75_remove(struct i2c_client *client)
{
cdev_del(&cdev);
unregister_chrdev_region(devno, number_of_devices);
return 0;
}
enum lm75_type { /* keep sorted in alphabetical order */
lm75,
lm75a,
};
static const struct i2c_device_id lm75_ids[] = {
{ "lm75", lm75, },
{ "lm75a", lm75a, },
{ /* LIST END */ }
};
static struct i2c_driver lm75_driver = {
.driver = {
.name = "lm75",
},
.probe = lm75_probe,
.remove = lm75_remove,
.id_table = lm75_ids,
};
static int __init s5pc100_lm75_init(void)
{
return i2c_add_driver(&lm75_driver);
}
static void __exit s5pc100_lm75_exit(void)
{
i2c_del_driver(&lm75_driver);
}
module_init(s5pc100_lm75_init);
module_exit(s5pc100_lm75_exit);
测试代码:
#include
#include
#include
#include
#include
int main (void)
{
int fd;
int data;
fd = open ("/dev/lm75",O_RDWR);
if (fd < 0) {
perror("open");
exit(0);
}
while(1)
{
read (fd, (char *)&data, sizeof(data));
sleep(1);
printf("%x\n", data);
}
close (fd);
printf ("/dev/temp closed :)\n");
return 0;
}
Makefile:
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /home/farsight/kernel/linux-2.6.35-farsight
CC = /opt/toolchain/arm-none-linux-gnueabi-gcc
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
test:test.c
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* module* test
.PHONY: modules modules_install clean
else
obj-m := s5pc100_temp.o
endif