现在的位置: 首页 > 综合 > 正文

S3C6410 矩阵键盘 驱动移植及分析

2014年01月11日 ⁄ 综合 ⁄ 共 6159字 ⁄ 字号 评论关闭

    前几天做矩阵键盘驱动移植,虽然步骤简单还是来记录下,顺便整理下思路。贵人多忘事  虽然咱不是贵人记性也不好啊。

    由于水平有限,此文档只供自己参考,但也欢迎扔砖。

----------------------------------------------------------------------------------------------------------------------------------------------------------

    矩阵键盘驱动主要包括平台设备及平台驱动两部分。

    平台设备定义在 dev-keypad.c 文件中。

struct platform_device samsung_device_keypad = {
	.name		= "samsung-keypad",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(samsung_keypad_resources),
	.resource	= samsung_keypad_resources,
};

    平台驱动定义在 samsung-keypad.c 文件中。

static struct platform_driver samsung_keypad_driver = {
	.probe		= samsung_keypad_probe,
	.remove		= __devexit_p(samsung_keypad_remove),
	.driver		= {
		.name	= "samsung-keypad",
		.owner	= THIS_MODULE,
#ifdef CONFIG_PM
		.pm	= &samsung_keypad_pm_ops,
#endif
	},
	.id_table	= samsung_keypad_driver_ids,
};

    设备和驱动名字必须相同,这样二者才能绑定。
    下面是设备和驱动是如何加到 platform bus 中去的。

    把平台设备 samsung_device_keypad 添加到系统板文件 mach-mini6410.c 的mini6410_devices[ ] 数组,这样开机 批量注册平台设备时就能把设备注册进platform bus。

    设备的资源包括两部分,[0]为使用的内存资源,[1]为使用的中断线。

static struct resource samsung_keypad_resources[] = {
	[0] = {
		.start	= SAMSUNG_PA_KEYPAD,
		.end	= SAMSUNG_PA_KEYPAD + 0x20 - 1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= IRQ_KEYPAD,
		.end	= IRQ_KEYPAD,
		.flags	= IORESOURCE_IRQ,
	},
};

 定义在文件 Dev-key.c

    mini6410_machine_init函数把 设备keypad设备加入系统后调了

samsung_keypad_set_platdata(&smdk6410_keypad_data);
 对设备参数进行了初始化 ,设备需要的参数如下。
static struct samsung_keypad_platdata smdk6410_keypad_data __initdata = {
.keymap_data = &smdk6410_keymap_data,//按键映射表
.rows = 2,//矩阵键盘行数
.cols = 2,//矩阵键盘列数
};
按键映射表
static uint32_t smdk6410_keymap[] __initdata = {
	// KEY(row, col, keycode) 
	KEY(0, 0, KEY_A), KEY(0, 1, KEY_B),KEY(0, 2, KEY_C), KEY(0, 3, KEY_D),
	KEY(1, 0, KEY_E), KEY(1, 1, KEY_F),KEY(1, 2, KEY_G), KEY(1, 3, KEY_H),
	KEY(2, 0, KEY_I), KEY(2, 1, KEY_J),KEY(2, 2, KEY_K), KEY(2, 3, KEY_L),
	KEY(3, 0, KEY_M), KEY(3, 1, KEY_N),KEY(3, 2, KEY_O), KEY(3, 3, KEY_P),
	KEY(4, 0, KEY_Q), KEY(4, 1, KEY_R),KEY(4, 2, KEY_S), KEY(4, 3, KEY_T)

};

static struct matrix_keymap_data smdk6410_keymap_data __initdata = {
	.keymap		= smdk6410_keymap,
	.keymap_size	= ARRAY_SIZE(smdk6410_keymap),
};

按键映射表是上传事件报告中用到的,具体后面再来讨论。
在 Dev-keypad.c中 
void __init samsung_keypad_set_platdata(struct samsung_keypad_platdata *pd)
{
struct samsung_keypad_platdata *npd;
//此函数就是把 smdk6410_keypad_data 复制了一份给了 samsung_device_keypad->dev.platform_data
npd = s3c_set_platdata(pd, sizeof(struct samsung_keypad_platdata),
&samsung_device_keypad);
//配置矩阵键盘接口用到的 GPIO
if (!npd->cfg_gpio)
npd->cfg_gpio = samsung_keypad_cfg_gpio;
}
在setup-keypad.c中 
void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols)
{
/* Set all the necessary GPK pins to special-function 3: KP_ROW[x] */
// s3c_gpio_cfgrange_nopull(S3C64XX_GPK(8), rows, S3C_GPIO_SFN(3));
//从GPN0开始的rows个端口设置为功能三 即键盘接口
s3c_gpio_cfgrange_nopull(S3C64XX_GPN(0), rows, S3C_GPIO_SFN(3));
/* Set all the necessary GPL pins to special-function 3: KP_COL[x] */
// s3c_gpio_cfgrange_nopull(S3C64XX_GPL(0), cols, S3C_GPIO_SFN(3));
s3c_gpio_cfgrange_nopull(S3C64XX_GPH(0), cols, S3C_GPIO_SFN(3));
}
6410 键盘接口可以用 K/L口或 N/H口
到此 divice 工作基本完成 。  休息下。

  linux 2.6设备驱动模型中,关心总线、设备、驱动这三个实体,总线将设备和驱动绑定。上面我们向内核添加了设备,下面我们来看是如何添加驱动及驱动是如何和设备绑定的。
我们来看 samsung-keypad.c  samsung_keypad_init中注册了平台设备驱动 samsung_keypad_driver
static int __init samsung_keypad_init(void)
{
return platform_driver_register(&samsung_keypad_driver);  //注册平台驱动
}
module_init(samsung_keypad_init);
 samsung_keypad_driver定义如下,
static struct platform_driver samsung_keypad_driver = {
.probe = samsung_keypad_probe,
.remove = __devexit_p(samsung_keypad_remove),
.driver = {
.name = "samsung-keypad",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &samsung_keypad_pm_ops,
#endif
},
.id_table = samsung_keypad_driver_ids,
};
其中 .name = "samsung-keypad",必须和设备
struct platform_device samsung_device_keypad = {
.name = "samsung-keypad",
一致,总线才能把设备和驱动绑定。
/**
 * platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 */
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;    //bus 类型 platform bus
if (drv->probe)
drv->driver.probe = platform_drv_probe; //探测函数
if (drv->remove)
drv->driver.remove = platform_drv_remove;  //移除函数
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;//关闭函数
return driver_register(&drv->driver);               //像总线注册驱动
}
转到了 driver_register(&drv->driver); 只能看看driver_register做了些啥东东
/**
 * 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);
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);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
下面来分析一下 bus_add_driver 
/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
//向总线添加设备驱动
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s/n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
    "%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed/n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed/n",
__func__, drv->name);
}
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
目前为止,设备和驱动都已经添加到了总线。如何互相找到对方并绑定是下一个问题。

抱歉!评论已关闭.