矩阵键盘驱动编写 - 嵌入式大作

1. 主动中断回调(失败)

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);
}

2. 定时查询(成功)

上述实现由于是直接修改内核中驱动中代码,与题意不符合,题目是要求可以通过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);

你可能感兴趣的:(嵌入式系统,矩阵,计算机外设,线性代数)