2011.12.1 星期四
本周二,完成了 通过migor_ts.c (经过修改)创建的驱动程序来将C48的报点数据传输到QT界面上,但是总是分辨率不匹配,不知道是不是由于是使用input_report_abs这个函数对QT进行报点所造成的,是不是必须像电阻屏一样进行开机校准,在电阻屏驱动s3c2410_ts.c文件中看到其X,Y设置的限制坐标为1024*1024,另外2440的数据手册上写出了X和Y方向的坐标输出最大也都是1024。将C48配置成1024*1024后,还是不行。
将I2C传输数据到QT的驱动过程记录如下:
使用的驱动程序为migor_ts.c,结合i2c_s3c2410.c文件
添加i2c驱动
static struct i2c_driver i2c_ts_driver = {
.driver = {
.name = "i2c-ts",
},
.probe = i2c_ts_probe, //自动执行
.remove = i2c_ts_remove,
.suspend = i2c_ts_suspend,
.resume = i2c_ts_resume,
.id_table = i2c_ts_id,// i2c_ts_id= ‘i2c-ts’
};
static int __init i2c_ts_init(void)
{
return i2c_add_driver(&i2c_ts_driver);
}
往下挖i2c_add_driver,
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
继续挖,需要详细分析i2c_register_driver这个函数,之前一直没有屡清楚,现在试试
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
//驱动检查,因为新旧两种风格只能指定一种
//新风格只需要指定probe和remove,前面指定了probe和remove所以是新风格
if (is_newstyle_driver(driver)) {
if (driver->attach_adapter || driver->detach_adapter
|| driver->detach_client) {
printk(KERN_WARNING"i2c-core: driver %s is confused\n"
driver->driver.name);
return -EINVAL;
}
}
/* add the driver to the list of i2c drivers in the driver core */
//这部分代码是给新风格的驱动注册用的
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type; //i2c_bus_type关联了I2C总线支持的操作函数
//注册驱动
res = driver_register(&driver->driver);
if (res)
return res;
mutex_lock(&core_lists);
list_add_tail(&driver->list, &drivers); //将驱动挂载到drivers驱动链表上
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
//这个操作是旧风格的驱动注册
if (driver->attach_adapter) { //如果适配器的探测函数存在
struct i2c_adapter *adapter;
//从适配器链表adapters中取出每一个适配器
list_for_each_entry(adapter, &adapters, list) {
//调用探测函数
//从这里可以看出,这个函数的作用是探测驱动属于哪一个适配器的
driver->attach_adapter(adapter);
}
}
mutex_unlock(&core_lists);
return 0;
}
static struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
在这里又关联了一个总线操作函数i2c_bus_type,先不管,先看注册路径driver_register(&driver->driver);
/**
* driver_register - register driver with bus
* @drv: driver to register
*
* We pass off most of the work to the bus_add_driver() call,
* since most of the things we have to do deal with the bus
* structures.
*/
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);//这个函数的意思是看看设备驱动里有没有函数,如果有就是需要更新,因为我们现在需要用的是bus里的函数。
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);//将驱动添加到bus上
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
从注释可以看到,这个注册函数是注册带有bus的驱动的。
其中driver的结构体为:
struct device_driver {
const char *name;
struct bus_type *bus;//这个成员关联的就是i2c_bus_type
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
来挖driver_find(drv->name, drv->bus);
/**
* driver_find - locate driver on a bus by its name.
* @name: name of the driver.
* @bus: bus to scan for the driver.
*
* Call kset_find_obj() to iterate over list of drivers on
* a bus to find driver by name. Return driver if found.
*
* Note that kset_find_obj increments driver's reference count.
*/
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
struct driver_private *priv;
if (k) {
priv = to_driver(k);
return priv->driver;
}
return NULL;
}
从注释中可以看出这个函数是从bus中寻找符合name(也就是之前在i2c_ts_driver定义的“i2c-ts”)的设备驱动,然后返回这个设备驱动。寻找的过程应该与Mach_mini2440.c中的设置有关系。应该是从这个文件中传给驱动设备地址以及其他的一些信息,但目前分析不出。再往下不挖了。
下面回到migor_ts.c文件中的i2c_ts_probe函数,这个函数应该会在 i2c_add_driver(&i2c_ts_driver)注册后自动被调用,至于为什么会这样,目前还不清楚。他的参数也不知道是怎么传来的
static int i2c_ts_probe(struct i2c_client *client,
const struct i2c_device_id *idp)
{
struct i2c_ts_priv *priv;/*触摸屏的数据结构/* touch screen data structure */
struct i2c_ts_priv {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work work;
int irq;
};*/
struct input_dev *input;//input设备结构体
int error;
char buf[2];
priv = kzalloc(sizeof(*priv), GFP_KERNEL);//给i2c数据结构分配内核空间
if (!priv) {
dev_err(&client->dev, "failed to allocate driver data\n");
error = -ENOMEM;
goto err0;
}
dev_set_drvdata(&client->dev, priv);/*static inline unsigned int dev_set_drvdata(struct device *dev, void *data)
{
dev->driver_data = data;;
}*/将i2c数据空间付给dev
input = input_allocate_device();//request input_dev device获得input结构体空间
if (!input) {
dev_err(&client->dev, "Failed to allocate input device.\n");
error = -ENOMEM;
goto err1;
}
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input, ABS_X, MIN_X_COORDINATE, MAX_X_COORDINATE, 0, 0);
input_set_abs_params(input, ABS_Y, MIN_Y_COORDINATE, MAX_Y_COORDINATE, 0, 0);
input->name = client->name;
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;//关联上已经分配了i2c数据空间的dev数据结构
input->open = i2c_ts_open;
input->close = i2c_ts_close;
input_set_drvdata(input, priv);//将priv中的数据付给input中的成员
priv->client = client;
priv->input = input;
INIT_DELAYED_WORK(&priv->work, i2c_ts_poscheck);//初始化等待队列,用于中断函数中
priv->irq = client->irq;
error = input_register_device(input);//注册输入设备,前面已将input构造成输入设备了
if (error)
goto err1;
error = request_irq(priv->irq, i2c_ts_isr, IRQF_TRIGGER_FALLING,
client->name, priv);//注册中断,这里参数, IRQF_TRIGGER_FALLING的意思是下降沿触发中断
if (error) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
goto err2;
}
device_init_wakeup(&client->dev,1);//唤醒设备,这里是i2c设备
return 0;
err2:
input_unregister_device(input);
input = NULL; /* so we dont try to free it below */
err1:
input_free_device(input);
kfree(priv);
err0:
dev_set_drvdata(&client->dev, NULL);
return error;
}
这个函数起到的是一个初始化的作用构造了一个输入设备结构体,然后进行了注册,然后还注册了一个中断。Priv的数据和input中的成员互相指向,有点迷糊。另外这里注册了一个等待队列,这里有两个函数是一对:本函数中的INIT_DELAYED_WORK(&priv->work, i2c_ts_poscheck),另外还有中断函数i2c_ts_isr(int irq, void *dev_id)中的schedule_delayed_work(&priv->work, HZ/100)表示等待10us再执行真正的中断函数i2c_ts_poscheck。
下面来看中断函数,这个函数在TP中断下降沿到来10us后执行。
static void i2c_ts_poscheck(struct work_struct *work)
{
struct i2c_ts_priv *priv = container_of(work, struct i2c_ts_priv, work.work);//得到包含work的结构体指针,其实也就是在probe中构造的priv结构体指针。
/* buffer for storing data */
char buf[6];
int number;
int xpos, ypos;
memset(buf, 0, sizeof(buf));
/* Now do Page Read */
if (i2c_master_recv(priv->client, buf,6) != 6) {//读取设备数据
dev_err(&priv->client->dev, "Unable to read i2c-pixcir page\n");
goto out;
}
/* convert coordinate */
number = buf[0]&0x07;
xpos = ((buf[3] << 8) | buf[2]);
ypos = ((buf[5] << 8) | buf[4]);
/* report input event */
if ((number != 0) && (xpos != 0) && (ypos != 0)) {
input_report_key(priv->input, BTN_TOUCH, 1);//传往QT
input_report_abs(priv->input, ABS_X, xpos);
input_report_abs(priv->input, ABS_Y, ypos);
input_sync(priv->input);
} else if (number == 0) {
input_report_key(priv->input, BTN_TOUCH, 0);
input_sync(priv->input);
}
out:
enable_irq(priv->irq);
}
这里的参数传递过程也不是很明白,不知道形参对应的实参在哪里。此函数实现了从i2c设备读取数据并传递到QT的过程,往QT的传输不是很清楚。
现在还有一个问题是不知道设备地址以及中断号什么的是怎么传过来的,定义应该是在mach-s3c2440.c这个文件中,也就是后面需要修改的内容。
总体的调用路径是:migor_ts.c?i2c-core.c?i2c-s3c2410.c. i2c最终的实际操作都是通过i2c-s3c2410.c.完成的。
目前的问题是使用input_report_abs报上去的坐标不知道怎么同QT的显示相匹配。
驱动修改过程为:
1、 按TP驱动分析(十一)中所讲修改migor_ts.c的内容,其中不一样的地方为在i2c_ts_poscheck函数中需要添加
if(i2c_master_send(priv->client,&send,1)!=1)
{
dev_err(&priv->client->dev, "Unable to write i2c-pixcir page\n");
goto out;
}
这样一段,因为触摸屏读操作是需要先发送要读的地址的。
2、 按TP驱动分析(十一)中所讲修改mach-mini2440.c,但是需要在头文件处添加i2c.h。