https://blog.csdn.net/weixin_49551849/article/details/113190428
//liyuan-zlg7290.c
现在的问题是 probe函数无法被调用,
当我们调用i2c_add_driver时会去设备
链中寻找与id_tables中相同名字的i2c_client设备,
其中i2c_client设备的初始化应该于板级加载时就初始化,因此需要
https://blog.csdn.net/tanxjian/article/details/7479444
现在问题出在request_irq时会出错,可使用dmesg处理
input_dev是基于上报事件的,所以无需专门创建一个设备给他
可查看 cat /proc/bus/input/devices
struct i2c_board_info __initdata xxx_ls_cm3212 =
{
I2C_BOARD_INFO(“cm3212”,0x90),
// I2C_BOARD_INFO是个简单的宏:.type="cm3212",.addr=0x90.
};
然后,在板级系统初始化的时候注册:
i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
其中busnum表示你对应系统的那个 i2c 控制(adapter),len表示你注册的info的个数。
i2c设备 -》 对应的驱动会寻找该设备,因此现在的问题时在板级系统起来前注册一个设备;
思路:一开始的zImage包含 zlg7290驱动,然后手动卸载该驱动 rmmod zlg7290, 然后手动insmod 自己的zlg7290的驱动;
该思路无法实现,因为加进内核里的驱动不能简单地从用户态移除;
发现无法找到 s5p_register_gpio_interrupt, 找了下,内核里面有该方法,
解决方案是将自己的驱动替换系统的相应内核驱动,可以正常运行;
由于之前对 make menuconfg的一些奇怪配置,因此现在重新解压linux-3.0.15压缩包,环境清0,但初次编译花了较久时间,因此可考虑先复制一份
nohup /root/runoob.sh > runoob.log 2>&1 &
//地址是一样的
#ifdef CONFIG_FS4412_ZLG7290
{
I2C_BOARD_INFO(“zlg7290”, (0x70 >> 1)),
.irq = EXYNOS4_GPK2(2),
},
#endif
#ifdef CONFIG_FS4412_SEGDIS
{
I2C_BOARD_INFO(“FS4412_segdis”, 0x70 >> 1),
},
#endif
arm-none-linux-gnueabi-gcc -static -o hello.cgi hello.c
nohup /root/runoob.sh > runoob.log 2>&1 &
1c | 14 | c | 1 |
1b | 13 | b | 2 |
1a | 12 | a | 3 |
19 | 11 | 9 | 4 |
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#define led_major 800
#define led_minor 0
#define ZLG7290_NAME "zlg7290"
#define SET_VAL _IO('Z',0)
extern int s5p_register_gpio_interrupt(int pin);
static int zlg7290_probe(struct i2c_client *client,const struct i2c_device_id *id);
static int register_led();
static int unregister_zlg7290(void);
static int zlg7290_remove(struct i2c_client *client);
//static int zlg7290_hw_write(struct zlg7290 *ctr_zlg7290, int len, size_t *retlen, char *buf);
//static int zlg7290_hw_read(struct zlg7290 *ctr_zlg7290, int len,size_t *retlen, char *buf);
static void zlg7290_work(struct work_struct *work);
static long zlg7290_ioctl(struct file *file,unsigned int cmd,unsigned long arg);
static int zlg7290_probe(struct i2c_client *client,const struct i2c_device_id *id);
static int zlg7290_open(struct inode *inode, struct file *file);
static int zlg7290_release(struct inode *inode, struct file *file);
irqreturn_t zlg7290_interrupt(int irq,void *devid);
struct zlg7290{
struct i2c_client *client;
struct work_struct work;
struct input_dev *key;
struct cdev cdev;
struct workqueue_struct *queue;
uint32_t irq;
};
struct zlg7290 *zlg7290;
//static volatile int key_press = 0;
unsigned int key_value[65] = {
0,
1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62, 63, 64,
};
//I2C_BOARD_INFO("zlg7290", (0x70 >> 1)),
// .irq = EXYNOS4_GPK2(2),
//file_operations 相关操作
static struct file_operations zlg7290_led_fops={
.owner = THIS_MODULE,
.open = zlg7290_open,
.release = zlg7290_release,
.unlocked_ioctl = zlg7290_ioctl,
};
static int unregister_zlg7290(void){
dev_t devno = MKDEV(led_major,led_minor);
cdev_del(&zlg7290->cdev);
unregister_chrdev_region(devno,1);
return 0;
}
static int register_led(void){
printk("register_led\n");
int ret;
dev_t devno = MKDEV(led_major,led_minor);
ret = register_chrdev_region(devno,1,"FS4412_zlg7290");
if(ret<0){printk("Failed: register");return -1;}
cdev_init(&zlg7290->cdev,&zlg7290_led_fops);
zlg7290->cdev.owner = THIS_MODULE;
ret = cdev_add(&zlg7290->cdev,devno,1);
if(ret<0){
unregister_chrdev_region(devno,1);
printk("Failed: cedv_add\n");
return -1;
}
printk("register_led success!\n");
return 0;
}
static int zlg7290_remove(struct i2c_client *client){
printk("remove\n");
unregister_zlg7290();
free_irq(client->irq,NULL);
i2c_set_clientdata(client,NULL);
input_unregister_device(zlg7290->key);
input_free_device(zlg7290->key);
kfree(zlg7290);
printk("remove\n");
return 0;
}
static const struct i2c_device_id zlg7290_id[] ={
{ZLG7290_NAME,0},
{},
{}
};
MODULE_DEVICE_TABLE(i2c,zlg7290_id);
static struct i2c_driver zlg7290_driver = {
.probe = zlg7290_probe,
.remove = zlg7290_remove,
.id_table = zlg7290_id,
.driver = {
.name = ZLG7290_NAME,
.owner = THIS_MODULE,
}
};
static int __init zlg7290_init(void){
printk("init");
return i2c_add_driver(&zlg7290_driver);
}
static void __exit zlg7290_exit(void){
printk("exit\n");
i2c_del_driver(&zlg7290_driver);
}
static int zlg7290_hw_write(struct zlg7290 *ctr_zlg7290, int len, size_t *retlen, char *buf){
printk("write\n");
struct i2c_client *client = ctr_zlg7290->client;
struct i2c_msg msg[] = {
{client->addr,0,len,buf}, //buf中包含注册地址
};
int ret = i2c_transfer(client->adapter,msg,1);
if(ret<0){
printk("ret=%d, addr=%x\n",ret,client->addr);
dev_err(&client->dev,"i2c read error\n");
return -EIO;
}
*retlen = len;
return 0;
}
static int zlg7290_hw_read(struct zlg7290 *ctr_zlg7290, int len,size_t *retlen, char *buf){
printk("read len: %d; retlen: %d;buf: %c\n",len,retlen,buf);
struct i2c_client *client = ctr_zlg7290->client;
int ret;
buf = kmalloc(9,GFP_KERNEL);
buf[0] = 1;
struct i2c_msg msg[] = {
{.addr = client->addr,
.flags = 0,
.len = 1,
.buf = buf},//buf包含寄存器地址
{.addr = client->addr,
.flags = I2C_M_RD,
.len = 2,
.buf = buf},//buf包含寄存器地址
};
printk("read2 client->addr: %d; \n",client->addr);
ret= i2c_transfer(client->adapter,msg,2);
printk("read ret: %d\n",ret);
if(ret<0){
printk("ret=%d, addr=%x\n",ret,client->addr);
//dev_error(&client->dev,"i2c read error!\n");
return -EIO;
}
*retlen = len;
printk("read finished!");
return 0;
}
static void zlg7290_work(struct work_struct *work){
printk("work\n");
struct zlg7290 *ctr_zlg7290 = container_of(work,struct zlg7290,work);
size_t retlen = 0;
char *kbuf;
static char bfbuf[10];
struct i2c_client *client = zlg7290->client;
int ret,i,key;
kbuf = kmalloc(9,GFP_KERNEL);
kbuf[0] = 1;
struct i2c_msg msgr[] = {
{client->addr,0,1,kbuf},
{client->addr,I2C_M_RD,2,kbuf}
};
struct i2c_msg msgw [] = {
{client->addr,0,9,kbuf},
};
ret = i2c_transfer(client->adapter,msgr,2);
if(ret<0){printk("error!->i2c_transfer ret=%d; addr = %x;\n",ret,client->addr);}
key = kbuf[0];
printk("key = %x, repeat:%x \n",key,kbuf[1]);
kbuf[0] = 0x10;
for(i=1;i<8;i++){
kbuf[i+1] = kbuf[i];
}
if(key==0x1c){kbuf[1]=0x0c;}//1
if(key==0x1b){kbuf[1]=0xb6;}//5
if(key==0x1a){kbuf[1]=0xf6;}//9
if(key==0x19){kbuf[1]=0x7a;}//d
if(key==0x14){kbuf[1]=0xda;}//2
if(key==0x13){kbuf[1]=0xbe;}//6
if(key==0x12){kbuf[1]=0xef;}//A
if(key==0x11){kbuf[1]=0x9f;}//E
if(key==0xc){kbuf[1]=0xf2;}//3
if(key==0xb){kbuf[1]=0xe0;}//7
if(key==0xa){kbuf[1]=0x3e;}//b
if(key==0x9){kbuf[1]=0x8e;}//f
if(key==0x4){kbuf[1]=0x66;}//4
if(key==0x3){kbuf[1]=0xfe;}//8
if(key==0x2){kbuf[1]=0x9c;}//c
if(key==0x1){kbuf[1]=0xfc;}//0
for(i=1;i<8;i++){
kbuf[i+1] = bfbuf[i];
bfbuf[i] = kbuf[i];
}
ret = i2c_transfer(client->adapter,msgw,1);
if(ret<0){printk("ret= %d;, addr=%x; \n",ret,client->addr);}
//ret = i2c_transfer(client->adapter,msgw,1);
kfree(kbuf);
printk("endendend\n");
printk("input report key!\n");
//TODO
input_report_key(zlg7290->key,key_value[key],1);
//input_report_key(zlg7290->key,key_value[key],0);
//input_event(zlg7290->key,EV_KEY,KEY_DOWN,1);
input_sync(zlg7290->key);
}
static long zlg7290_ioctl(struct file *file,unsigned int cmd,unsigned long arg){
printk("ioctl\n");
unsigned char buf[8] = {0};
size_t len = 0;
unsigned char val[2] = {0};
unsigned char reg[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17};
int i = 0;
switch(cmd){
case SET_VAL:{
if(copy_from_user(buf,(void*)arg,8)){
return -EFAULT;
}
for(i=0;i<8;i++){
val[0] = reg[i];
switch(buf[i]){
case '0':{val[1] = 0xfc;break;}
case '1':{val[1] = 0x0c;break;}
case '2':{val[1] = 0xda;break;}
case '3':{val[1] = 0xf2;break;}
case '4':{val[1] = 0x66;break;}
case '5':{val[1] = 0xb6;break;}
case '6':{val[1] = 0xbe;break;}
case '7':{val[1] = 0xe0;break;}
case '8':{val[1] = 0xfe;break;}
case '9':{val[1] = 0xf6;break;}
case 'a':
case 'A':{val[1] = 0xee;break;}
case 'b':
case 'B':{val[1] = 0x3e;break;}
case 'c':
case 'C':{val[1] = 0x9c;break;}
case 'd':
case 'D':{val[1] = 0x7a;break;}
case 'e':
case 'E':{val[1] = 0x9e;break;}
case 'f':
case 'F':{val[1] = 0x8e;break;}
case ' ':{val[1] = 0x00;break;}
default:{val[1] = 0x00;break;}
}
msleep(10);
//数码管显示
zlg7290_hw_write(zlg7290,2,&len,val);
}
break;
}
}
return 0;
}
static int zlg7290_probe(struct i2c_client *client,const struct i2c_device_id *id){
printk("probe1\n");
struct input_dev *key_dev;
int ret;
int i=0;
int err = -EINVAL;
if(!(zlg7290 = kzalloc(sizeof(struct zlg7290),GFP_KERNEL))){
return -ENOMEM;
}
printk("irq = %d\n",client->irq);
zlg7290->key = input_allocate_device();
if(zlg7290->key==NULL){
kfree(zlg7290);
return -ENOMEM;
}
key_dev = zlg7290->key;
key_dev->name = "zlg7290";
key_dev->id.bustype = BUS_HOST;
key_dev->id.vendor = 0x0001;
key_dev->id.product = 0x0001;
key_dev->id.version = 0x0001;
key_dev->evbit[0] = BIT_MASK(EV_KEY);
for(i=1;i<=64;i++){
key_dev->keybit[BIT_WORD(key_value[i])] |=BIT_MASK(key_value[i]);
}
ret = input_register_device(key_dev);
if(ret<0){
printk("Failed to register input device\n");
goto err1;
}
printk("zlg7290 probe2\n");
if(!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)){
err = -ENODEV;
return err;
}
zlg7290->client = client;
i2c_set_clientdata(client,zlg7290);
INIT_WORK(&zlg7290->work,zlg7290_work);
zlg7290->queue = create_singlethread_workqueue("zlg7290-queue");
if(!zlg7290->queue){
printk("queue initialize error!\n");
err = -ESRCH;
return err;
}
//schedule_delayed_work(&zlg7290->work,HZ/5);
printk("request_irq\n");
zlg7290->irq = client->irq;
s5p_register_gpio_interrupt(zlg7290->irq);
zlg7290->irq= gpio_to_irq(zlg7290->irq);
// zlg7290->irq=386;
printk("zlg7290 irq: %d\n:",zlg7290->irq);
ret = request_irq(zlg7290->irq, zlg7290_interrupt,IRQ_TYPE_EDGE_FALLING , "zlg7290",key_dev);
if (ret < 0) {
printk("request_irq failed!\n");
goto err1;
}
ret = register_led();
if(ret<0){
printk("register Failed!");
goto err1;
}
return 0;
//free_irq_flag:
// input_unregister_device(key_dev);
err1:
printk("EBUSY:%d\n",EBUSY);
printk("err1->ret:%d\n",ret);
input_free_device(key_dev);
kfree(zlg7290);
return ret;
}
static int zlg7290_open(struct inode *inode, struct file *file){
printk("open\n");
return 0;
}
static int zlg7290_release(struct inode *inode, struct file *file){
printk("release\n");
return 0;
}
irqreturn_t zlg7290_interrupt(int irq,void *devid){
printk("irq = %d\n",irq);
schedule_work(&zlg7290->work);
return IRQ_HANDLED;
}
MODULE_AUTHOR("liyuan");
MODULE_DESCRIPTION("zlg7290 driver");
module_init(zlg7290_init);
module_exit(zlg7290_exit);
ifeq ($(KERNELRELEASE), )
KERNELDIR ?=/home/linux/liyuan/kernel/linux-3.0.15
PWD := $(shell pwd)
modules :
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install :
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean :
rm -f *.o *.mod* *.order *.symvers
.PHONY : modules modules_install clean
else
# obj-m +=FS4412_LED_drv.o
# obj-m +=FS4412_segdis_drv_2.o
obj-m +=liyuan-zlg7290.o
endif
//测试上报的事件
#include
#include
#include
#include
#include
#include
#include
int main(){
int fd;
int ret;
struct input_event event;
if((fd=open("/dev/input/event0",O_RDONLY))<0){
printf("error open!\n");
exit(1);
}
printf("open success!->%d",fd);
while(1){
// printf("event!!!\n");
ret = read(fd,&event,sizeof(event));
// printf("error read\n");
// break;
printf("event type: %d;value: %d;code: %d;\n",event.type,event.value,event.code);
}
close(fd);
}
上述实现由于是直接修改内核中驱动中代码,与题意不符合,题目是要求可以通过insmod xxx实现的,因此书写了下面这个版本,通过定时查询按键缓冲中是否有值从而读取按键的值;
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <plat/gpio-cfg.h>
static volatile int key_press =0;
#define ADDR_ZLG7290 0x70 /* ZLG7290 address 0x70*/
#define ZLG7290_NAME "zlg7290"
/* 为了方便起见,将需要操作的设备封装一个结构体 */
struct zlg7290
{
struct i2c_client *client;/* 由于该设备通过i2c总线通信,所以需要将它定义为一个i2c从设备 */
struct input_dev *key;/* 定义input device */
// struct cdev cdev;/* 定义字符设备结构体 */
struct device *class_dev;
struct delayed_work work;
//uint32_t irq;
}*zlg7290;
struct device *dev;
unsigned int key_value[65]={
0,
1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62, 63, 64,
};/* 定义linux内核标准键值 */
static void zlg7290_irq_work(struct work_struct *work) {
struct zlg7290 *ctr_zlg7290 = container_of(work,struct zlg7290,work);
size_t retlen = 0;
char *kbuf;
static char bfbuf[10];
struct i2c_client *client = zlg7290->client;
int ret, i, key;
kbuf = kmalloc(9, GFP_KERNEL);
kbuf[0] = 1;
struct i2c_msg msgr[] = {
{client->addr, 0, 1, kbuf},
{client->addr, I2C_M_RD, 2, kbuf}
};
struct i2c_msg msgw[] = {
{client->addr, 0, 9, kbuf},
};
ret = i2c_transfer(client->adapter, msgr, 2);
if (ret < 0) { printk("error!->i2c_transfer ret=%d; addr = %x;\n", ret, client->addr); }
key = kbuf[0];
if(key==0){
goto out;
}
printk("key1 = %x;key2 = %x;, repeat:%x \n", key,kbuf[1]);
kbuf[0] = 0x10;
for (i = 1; i < 8; i++) {
kbuf[i + 1] = kbuf[i];
}
if (key == 0x1c) { kbuf[1] = 0x0c; }//1
if (key == 0x1b) { kbuf[1] = 0xb6; }//5
if (key == 0x1a) { kbuf[1] = 0xf6; }//9
if (key == 0x19) { kbuf[1] = 0x7a; }//d
if (key == 0x14) { kbuf[1] = 0xda; }//2
if (key == 0x13) { kbuf[1] = 0xbe; }//6
if (key == 0x12) { kbuf[1] = 0xef; }//A
if (key == 0x11) { kbuf[1] = 0x9f; }//E
if (key == 0xc) { kbuf[1] = 0xf2; }//3
if (key == 0xb) { kbuf[1] = 0xe0; }//7
if (key == 0xa) { kbuf[1] = 0x3e; }//b
if (key == 0x9) { kbuf[1] = 0x8e; }//f
if (key == 0x4) { kbuf[1] = 0x66; }//4
if (key == 0x3) { kbuf[1] = 0xfe; }//8
if (key == 0x2) { kbuf[1] = 0x9c; }//c
if (key == 0x1) { kbuf[1] = 0xfc; }//0
for (i = 1; i < 8; i++) {
kbuf[i + 1] = bfbuf[i];
bfbuf[i] = kbuf[i];
}
ret = i2c_transfer(client->adapter, msgw, 1);
if (ret < 0) { printk("ret= %d;, addr=%x; \n", ret, client->addr); }
kfree(kbuf);
printk("liyuan->input report key!\n");
input_report_key(zlg7290->key, key_value[key], 1);
input_report_key(zlg7290->key, key_value[key], 0);
input_sync(zlg7290->key);
out:
schedule_delayed_work(&zlg7290->work,HZ/5);
}
/*设备与驱动匹配成功后会调用该函数,I2c_client 代表一个挂在i2c总线上的i2c从设备包含该设备所需的数据,I2c_device*/
static int zlg7290_probe(struct i2c_client *client, const struct i2c_device_id *id){
struct input_dev *key_dev;
int ret = 0 , i = 0 , err = -EINVAL;
for(i=0;i<=65;i++)
key_value[i]=i;
if (!(zlg7290 = kzalloc(sizeof(struct zlg7290), GFP_KERNEL)))/* 为zlg7290设备分配地址空间 */
{
printk("zlg7290 kzalloc fail!");
return -ENOMEM;
}
zlg7290->key = input_allocate_device();/* 为input设备分配地址空间 */
if (zlg7290->key == NULL)
{
printk("input device allocate fail!");
kfree(zlg7290);
return -ENOMEM;
}
key_dev = zlg7290->key;/* 将zlg7290设备的相关属性配置到input设备中 */
key_dev->name = "zlg7290";
key_dev->id.bustype = BUS_HOST;
key_dev->id.vendor = 0x0001;
key_dev->id.product = 0x0001;
key_dev->id.version = 0x0001;
key_dev->evbit[0] = BIT_MASK(EV_KEY);
for(i=1;i<=64;i++)
key_dev->keybit[BIT_WORD(key_value[i])] |= BIT_MASK(key_value[i]);
ret = input_register_device(key_dev);/* 注册input设备 */
if (ret < 0)
{
printk("Failed to register input device\n");
goto err1;
}
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) /* 检测i2c控制器是否支持我们的需求,如果返回1则支持,0则不支持 */
{
err = -ENODEV;
return err;
}
zlg7290->client = client;
i2c_set_clientdata(client, zlg7290);/* 将自定义的设备结构dev赋给设备驱动client的私有指针 */
INIT_DELAYED_WORK(&zlg7290->work, zlg7290_irq_work);
schedule_delayed_work(&zlg7290->work, HZ/5);
return 0;
err1:
input_free_device(key_dev);
kfree(zlg7290);
return ret;
}
/* 定义i2c设备信息 */
static const struct i2c_device_id zlg7290_id[] ={
{ZLG7290_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, zlg7290_id);/* 将zlg7290结构输出到用户空间,这样模块加载系统在加载模块时,就知道了什么模块对应什么硬件设备*/
static int __devexit zlg7290_remove(struct i2c_client *client) {
// free_irq(client->irq, NULL);
cancel_delayed_work_sync(&zlg7290->work);
i2c_set_clientdata(client, NULL);
kfree(zlg7290);
return 0;
}
/* 定义i2c设备驱动结构体 */
static struct i2c_driver zlg7290_driver =
{
.probe = zlg7290_probe,
.remove = zlg7290_remove,
.id_table = zlg7290_id,
.driver = {
.name = ZLG7290_NAME,
.owner = THIS_MODULE,
},
};
static int __init zlg7290_init(void)
{
return i2c_add_driver(&zlg7290_driver);
}
static void __exit zlg7290_exit(void)
{
i2c_del_driver(&zlg7290_driver);
}
MODULE_AUTHOR("liyuan");
MODULE_DESCRIPTION("zlg7290 driver");
MODULE_LICENSE("GPL");
module_init(zlg7290_init);
module_exit(zlg7290_exit);