在分析完DS1337的驱动之后,我们对I2C的驱动的移植有了个简单的认识,于是仿照DS1337的驱动,我们写了OFN的I2C部分:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/bcd.h>
#include <linux/list.h>
/*
* Functions declaration
*/
#define I2C_ADDR_OFN350 (0x66 >> 1)
static unsigned short normal_i2c[] = { I2C_ADDR_OFN350, I2C_CLIENT_END };
I2C_CLIENT_INSMOD_1(ofn350);
static int ofn350_attach_adapter(struct i2c_adapter *adapter);
static int ofn350_detect(struct i2c_adapter *adapter, int address, int kind);
static void ofn350_init_client(struct i2c_client *client);
static int ofn350_detach_client(struct i2c_client *client);
static int ofn350_command(struct i2c_client *client, unsigned int cmd,
void *arg);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver ofn350_driver = {
.driver = {
.name = "ofn350",
},
.attach_adapter = ofn350_attach_adapter, //注册时创建设备节点(调用适配器连接节点)
.detach_client = ofn350_detach_client, //让设备脱离适配器
.command = ofn350_command,
};
/*
* Client data (each client gets its own)
*/
struct ofn350_data {
struct i2c_client client;
struct list_head list;
};
/*
* Internal variables
*/
static LIST_HEAD(ofn350_clients);
static inline int ofn350_read(struct i2c_client *client, u8 reg, u8 *value)
{
s32 tmp = i2c_smbus_read_byte_data(client, reg);
if (tmp < 0)
return -EIO;
*value = tmp;
return 0;
}
/*
* Chip access functions
*/
static int ofn350_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
dev_dbg(&client->dev, "%s: cmd=%d\n", __FUNCTION__, cmd);
switch (cmd) {
default:
return -EINVAL;
}
}
/*
* Public API for access to specific device. Useful for low-level
* RTC access from kernel code.
*/
int ofn350_do_command(int bus, int cmd, void *arg)
{
struct list_head *walk;
struct list_head *tmp;
struct ofn350_data *data;
list_for_each_safe(walk, tmp, &ofn350_clients) {
data = list_entry(walk, struct ofn350_data, list);
if (data->client.adapter->nr == bus)
return ofn350_command(&data->client, cmd, arg);
}
return -ENODEV;
}
static int ofn350_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, ofn350_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int ofn350_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct ofn350_data *data;
int err = 0;
const char *name = "";
//
printk("<1>ofn350_detect\n");
//
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_I2C))
goto exit;
if (!(data = kzalloc(sizeof(struct ofn350_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
INIT_LIST_HEAD(&data->list);
/* The common I2C client data is placed right before the
* DS1337-specific data.
*/
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &ofn350_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*
* For detection, we read registers that are most likely to cause
* detection failure, i.e. those that have more bits with fixed
* or reserved values.
*/
/* Default to an DS1337 if forced */
if (kind == 0)
kind = ofn350;
if (kind < 0) { /* detection and identification */
u8 data;
kind = ofn350;
}
if (kind == ofn350)
name = "ofn350";
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the DS1337 chip */
ofn350_init_client(new_client);
/* Add client to local list */
list_add(&data->list, &ofn350_clients);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void ofn350_init_client(struct i2c_client *client)
{
//初始化ofn350代码
printk("<1>ofn350_init_client\n");
}
static int ofn350_detach_client(struct i2c_client *client)
{
int err;
struct ofn350_data *data = i2c_get_clientdata(client);
//
printk("<1>ofn350_detach_client\n");
//
if ((err = i2c_detach_client(client)))
return err;
list_del(&data->list);
kfree(data);
return 0;
}
static int __init ofn350_init(void)
{
printk("<1>ofn350_init\n");
return i2c_add_driver(&ofn350_driver);
}
static void __exit ofn350_exit(void)
{
printk("<1>ofn350_exit\n");
i2c_del_driver(&ofn350_driver);
}
MODULE_AUTHOR("Oscar Liu");
MODULE_DESCRIPTION("OFN350 driver");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL_GPL(ofn350_do_command);
module_init(ofn350_init);
module_exit(ofn350_exit);
------------------------------------------------------------------------------------------------------------
Makefile文件如下:
# Makefile
# Please modify here or set environments.
# KSOURCE should be pointed to the build directory of your kernel.
#
DEBUG ?= n
#KSOURCE ?= /home/azure/Utu-linux/s3c2440/Kernel/utu-linux_for_s3c2440_V1.5/
KSOURCE ?= /utu-Linux2.6.24/ #这个是我内核所在的路径
%.x:%.c
arm-linux-gcc -o $@ $<
KBUILD_VERBOSE:=1
obj-m := ofn.o
default:
make -C $(KSOURCE) LANG=C KBUILD_VERBOSE=${KBUILD_VERBOSE} SUBDIRS=`pwd` modules
.PHONY: cscope
cscope:
cscope -b -k -R
.PHONY: clean
clean:
make -C $(KSOURCE) LANG=C KBUILD_VERBOSE=${KBUILD_VERBOSE} SUBDIRS=`pwd` clean
rm -f *.x *~