guowenxue
2020-08-21 41cfbbd8eaef35a47b373bcd8288183ddac050a6
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
/*********************************************************************************
 *      Copyright:  (C) 2016 Guo Wenxue<guowenxue@aliyun.com>
 *                  All rights reserved.
 *
 *       Filename:  platdev_key.c
 *    Description:  This file is the keypad platform driver 
 *                 
 *        Version:  1.0.0(07/26/2016)
 *         Author:  Guo Wenxue <guowenxue@aliyun.com>
 *      ChangeLog:  1, Release initial version on "07/26/2016 05:01:25 PM"
 *                 
 ********************************************************************************/
 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/gpio.h>
#include <asm/irq.h>
#include <linux/slab.h>
#include <mach/regs-gpio.h>
 
/* 1HZ=100*jiffies 1*jiffies=10ms => 1HZ=100*10ms = 1s */
#define CANCEL_DITHERING_DELAY          (HZ/50)   /* Remove button push down dithering timer delay 20ms  */ 
 
 
/*+------------------------------------------------+
  |      keyboard platform device information      |
  +------------------------------------------------+*/
 
/* keyboard hardware informtation structure definition */
typedef struct s3c_kbd_info_s
{
    int                     code;      /* input device key code  */
    int                     nIRQ;      /* keyboard IRQ number*/
    unsigned int            setting;   /* keyboard IRQ Pin Setting*/
    unsigned int            gpio;      /* keyboard GPIO port */
} s3c_kbd_info_t;
 
/* keyboard platform device private data structure */
typedef struct s3c_kbd_platform_data_s
{
    s3c_kbd_info_t         *keys;
    int                    nkeys;
} s3c_kbd_platform_data_t;
 
 
static s3c_kbd_info_t  s3c_kbd_gpios[] = {
    [0] = {
        .code = KEY_1,
        .nIRQ = IRQ_EINT0,
        .gpio = S3C2410_GPF(0),
        .setting = S3C2410_GPF0_EINT0,
    },
    [1] = {
        .code = KEY_2,
        .nIRQ = IRQ_EINT2,
        .gpio = S3C2410_GPF(2),
        .setting = S3C2410_GPF2_EINT2,
    },
    [2] = {
        .code = KEY_3,
        .nIRQ = IRQ_EINT3,
        .gpio = S3C2410_GPF(3),
        .setting = S3C2410_GPF3_EINT3,
    },
    [3] = {
        .code = KEY_4,
        .nIRQ = IRQ_EINT4,
        .gpio = S3C2410_GPF(4),
        .setting = S3C2410_GPF4_EINT4,
    },
};
 
/* keyboard platform device private data */
static s3c_kbd_platform_data_t s3c_kbd_data = {
    .keys = s3c_kbd_gpios,
    .nkeys = ARRAY_SIZE(s3c_kbd_gpios),
};
 
static void platform_kbd_release(struct device * dev)
{
        return;
}
 
static struct platform_device s3c_keyboard_device = {
    .name    = "s3c_kbd",
    .id      = 1,
    .dev     =
    {
        .platform_data = &s3c_kbd_data,
        .release = platform_kbd_release,
    },
};
 
 
/*+------------------------------------------------+
  |      keyboard platform driver information      |
  +------------------------------------------------+*/
 
typedef struct s3c_kbd_s 
{
    struct timer_list           *timers; /* every key get a cancel dithering timer */
    struct input_dev            *input_dev;
    s3c_kbd_platform_data_t     *pdata;
} s3c_kbd_t;  /*---  end of struct s3c_kbd_s  ---*/
 
 
s3c_kbd_t                       *s3c_kbd = NULL;
 
static irqreturn_t s3c_kbd_intterupt(int irq, void *dev_id) 
{
    int                         i;
    int                         found = 0;
    struct platform_device      *pdev =  dev_id;
    s3c_kbd_t                   *s3c_kbd = NULL;
    
    s3c_kbd = platform_get_drvdata(pdev);
 
    for(i=0; i<s3c_kbd->pdata->nkeys; i++)
    {
        if(irq == s3c_kbd->pdata->keys[i].nIRQ)
        {
            found = 1;
            break;
        }
    }
 
    if(!found) /*  An ERROR interrupt */
        return IRQ_NONE;
 
    mod_timer(&s3c_kbd->timers[i], jiffies+CANCEL_DITHERING_DELAY); 
    return IRQ_HANDLED;  
}
 
static void  cancel_dithering_timer_handler(unsigned long data)
{
    int                      which =(int)data;
    unsigned int             pinval;  
 
    pinval = s3c2410_gpio_getpin(s3c_kbd->pdata->keys[which].gpio);
 
    if( pinval ) 
    {
        //printk("s3c_kbd key[%d] code[%d] released\n", which, s3c_kbd->pdata->keys[which].code);
        input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 0);  
    }
    else
    {
        //printk("s3c_kbd key[%d] code[%d] pressed\n", which, s3c_kbd->pdata->keys[which].code);
        input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 1);  
    }
    
    input_sync(s3c_kbd->input_dev);
}
 
static int s3c_kbd_probe(struct platform_device *pdev)
{
    int                         i = 0;
    int                         rv = -ENOMEM;
    struct input_dev            *input_dev = NULL;
    s3c_kbd_platform_data_t     *pdata = pdev->dev.platform_data;
 
    /* malloc s3c_kbd struct */
    s3c_kbd = kmalloc(sizeof(s3c_kbd_t), GFP_KERNEL);
    if( !s3c_kbd )
    {
        printk("error: s3c_kbd_probe kmalloc() for s3c_kbd failure\n");
        goto fail;
    }
    memset(s3c_kbd, 0, sizeof(s3c_kbd_t));
 
    /* malloc cancel dithering timer for every key */
    s3c_kbd->timers = (struct timer_list *) kmalloc(pdata->nkeys*sizeof(struct timer_list), GFP_KERNEL);
    if( !s3c_kbd->timers )
    {
        printk("error: s3c_kbd_probe kmalloc() for s3c_kbd timers failure\n");
        goto fail;
    }
    memset(s3c_kbd->timers, 0, pdata->nkeys*sizeof(struct timer_list));
 
    /* malloc input_dev for keyboard */
    input_dev=input_allocate_device();
    if( !input_dev )
    {
        printk("error: s3c_kbd_probe input_allocate_device() failure\n");
        goto fail;
    }
 
    /* setup input_dev  */
    input_dev->name = pdev->name;
    input_dev->dev.parent = &pdev->dev;
    input_dev->id.bustype = BUS_HOST;
    input_dev->id.vendor = 0x0001;
    input_dev->id.product = 0x0001;
    input_dev->id.version = 0x0100;
 
    set_bit(EV_KEY,input_dev->evbit);  
    set_bit(EV_REP,input_dev->evbit); 
 
    /* Initialize all the keys and interrupt */
    for(i=0; i<pdata->nkeys; i++)
    {
        set_bit(pdata->keys[i].code, input_dev->keybit);
        s3c2410_gpio_cfgpin(pdata->keys[i].gpio, pdata->keys[i].setting);
        irq_set_irq_type(pdata->keys[i].nIRQ, IRQ_TYPE_EDGE_BOTH);
        
        rv = request_irq(pdata->keys[i].nIRQ, s3c_kbd_intterupt, IRQF_DISABLED, pdev->name, pdev);
        if( rv )
        {
            printk("error: request IRQ[%d] for key<%d> failure\n", pdata->keys[i].nIRQ, i);
            rv = -EBUSY;
            goto fail;
        }
        
        //printk("s3c_kbd request IRQ[%d] for key<%d> ok\n", pdata->keys[i].nIRQ, i);
 
         /* Initialize all the keys cancel dithering timer */
        setup_timer(&s3c_kbd->timers[i], cancel_dithering_timer_handler, i);
    }
 
    /* register input device */
    rv = input_register_device(input_dev);
    if( rv )
    {
        printk("error: s3c_kbd_probe input_register_device error!\n");
        goto fail;
    }
 
    /* set s3c_kbd as private data in pdev */
    s3c_kbd->input_dev = input_dev;
    s3c_kbd->pdata = pdata;
    platform_set_drvdata(pdev, s3c_kbd);
 
    printk("s3c_kbd_probe ok\n");
    return 0;
 
fail:
    while(i--)
    {
        disable_irq(pdata->keys[i].nIRQ);
        free_irq(pdata->keys[i].nIRQ, pdev);
        del_timer( &s3c_kbd->timers[i] );
    }
 
    if(input_dev)
    {
        input_free_device(input_dev);
    }
 
    if(s3c_kbd && s3c_kbd->timers)
    {
        kfree(s3c_kbd->timers);
    }
 
    if(s3c_kbd)
    {
        kfree(s3c_kbd);
    }
    printk("s3c_kbd_probe failed\n");
 
    return -ENODEV;
}
 
static int s3c_kbd_remove(struct platform_device *pdev)
{
    int                         i = 0;
    s3c_kbd_t                   *s3c_kbd = platform_get_drvdata(pdev);
 
    for(i=0; i<s3c_kbd->pdata->nkeys; i++)
    {
        del_timer( &s3c_kbd->timers[i] );
        disable_irq(s3c_kbd->pdata->keys[i].nIRQ);
        free_irq(s3c_kbd->pdata->keys[i].nIRQ, pdev);
    }
 
    input_unregister_device(s3c_kbd->input_dev);
 
    kfree(s3c_kbd->timers);
    kfree(s3c_kbd);
 
    printk("s3c_kbd_remove ok\n");
 
    return 0;
}
 
static struct platform_driver s3c_keyboard_driver = {
     .probe      = s3c_kbd_probe,
     .remove     = s3c_kbd_remove,
     .driver     = {
         .name       = "s3c_kbd",
         .owner      = THIS_MODULE,
     },
};
 
static int __init s3c_keyboard_drv_init(void)
{
    int            rv;
 
    rv = platform_device_register(&s3c_keyboard_device);
    if(rv)
    {
        printk("S3C keyboard platform device register failure\n");
        return rv;
    }
 
    rv = platform_driver_register(&s3c_keyboard_driver);
    if(rv)
    {
        printk("s3c keyboard platform driver register failure\n");
        platform_device_unregister(&s3c_keyboard_device);
        return rv;
    }
 
    printk("s3c keyboard platform driver register ok\n");
    return 0;
}
 
static void __exit s3c_keyboard_drv_exit(void)
{
    printk("s3c keyboard driver exit\n");
 
    platform_device_unregister(&s3c_keyboard_device);
    platform_driver_unregister(&s3c_keyboard_driver);
 
    return ;
}
 
module_init(s3c_keyboard_drv_init);
module_exit(s3c_keyboard_drv_exit);
 
MODULE_DESCRIPTION("FL2440 board keyboard input driver platform_driver");
MODULE_AUTHOR("Guo Wenxue<guowenxue@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:FL2440 keyboard driver");