全志A20 keyadc驱动程序及toolbox测试代码
//TODO:文件分类, 功能
一,
//TODO:程序的分解
二,
//TODO:程序的调试和测试
三,
附录:
//TODO:keyadc驱动程序
/*
* linux/drivers/char/keyadc.c
*
* Copyright (C) 2013 Jack Chen, [email protected]
*
* - Base on Allwinner A20 K70 platform;
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include
#include
#include
#include
#include
#include
#include
#include
/****************************************************************/
#define MAGIC '8'
#define KEYADC_SET _IOW(MAGIC, 0, unsigned long)
#define KEYADC_GET _IOR(MAGIC, 1, unsigned long)
#define KEYADC_VOLUP (0x11)
#define KEYADC_VOLDOWN (0x12)
#define KEYADC_HOME (0x13)
#define KEYADC_BACK (0x14)
#define KEY_IRQNO (AW_IRQ_LRADC)
#define INPUTNAME "keyadc_input"
#define PLATFORMNAME "platform_keyadc"
#define DEVNAME "keyadc"
#define WORKQUEUENAME "keyadc_workqueue"
#define IRQNAME "keyadc_irq"
//#define DEBUGMASK
#ifdef DEBUGMASK
#define dprintk(fmt, arg...) printk(fmt, ## arg)
#else
#define dprintk(fmt, arg...)
#endif
/****************************************************************/
static struct sun7i_lradc_regs {
#define LRADC_BASE_ADDR (0xf1c22800)
#define LRADC_CTRL (0x00)
#define LRADC_INTC (0x04)
#define LRADC_INTS (0x08)
#define LRADC_DATA0 (0x0c)
#define LRADC_DATA1 (0x10)
//regs
/*LRADC_CTRL*/
u32 ctrl;
/*LRADC_INTC*/
u32 intc;
/*LRADC_INTS*/
u32 ints;
/*LRADC_DATA0*/
u32 data0;
/*LRADC_DATA1*/
u32 data1;
//bits
#define FIRST_CONCERT_DLY (2<<24)
#define CHAN (0x3)
#define ADC_CHAN_SELECT (CHAN<<22)
#define LRADC_KEY_MODE (0)
#define KEY_MODE_SELECT (LRADC_KEY_MODE<<12)
#define LEVELB_VOL (0<<4)
#define LRADC_HOLD_EN (1<<6)
#define LRADC_SAMPLE_32HZ (3<<2)
#define LRADC_SAMPLE_62HZ (2<<2)
#define LRADC_SAMPLE_125HZ (1<<2)
#define LRADC_SAMPLE_250HZ (0<<2)
#define LRADC_EN (1<<0)
#define LRADC_ADC1_UP_EN (1<<12)
#define LRADC_ADC1_DOWN_EN (1<<9)
#define LRADC_ADC1_DATA_EN (1<<8)
#define LRADC_ADC0_UP_EN (1<<4)
#define LRADC_ADC0_DOWN_EN (1<<1)
#define LRADC_ADC0_DATA_EN (1<<0)
#define LRADC_ADC1_UPPEND (1<<12)
#define LRADC_ADC1_DOWNPEND (1<<9)
#define LRADC_ADC1_DATAPEND (1<<8)
#define LRADC_ADC0_UPPEND (1<<4)
#define LRADC_ADC0_DOWNPEND (1<<1)
#define LRADC_ADC0_DATAPEND (1<<0)
} __attribute__((packed));
static struct keyadc_dev{
dev_t devid;
struct cdev chrdev;
struct class *class;
struct input_dev *input;
struct workqueue_struct *keyadc_workqueue;
struct work_struct keyadc_work;
unsigned int keycode;
volatile struct sun7i_lradc_regs *regs;
};
static struct keyadc_dev keyadc_pdata = {
.regs = NULL,
};
static struct platform_device keyadc_device = {
.name = PLATFORMNAME,
.id = -1,
.dev = {
.platform_data = &keyadc_pdata,
}
};
/****************************************************************/
static int keyadc_open(struct inode *inode, struct file *filp)
{
struct keyadc_dev *pdata;
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
pdata = container_of(inode->i_cdev, struct keyadc_dev, chrdev);
filp->private_data = pdata;
return 0;
}
static int keyadc_release(struct inode *inode, struct file *filp)
{
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
filp->private_data = NULL;
return 0;
}
static long keyadc_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
long ret;
void __user *uarg;
unsigned long karg;
struct keyadc_dev *pdata = filp->private_data;
dprintk("=====[%s(%d)]:cmd=%d\n", __FUNCTION__, __LINE__, cmd);
if (KEYADC_SET == cmd) {
uarg = (void __user *)arg;
copy_from_user(&karg, uarg, sizeof(unsigned long));
switch (karg) {
case KEYADC_VOLUP:
pdata->keycode = KEY_VOLUMEUP;
break;
case KEYADC_VOLDOWN:
pdata->keycode = KEY_VOLUMEDOWN;
break;
case KEYADC_HOME:
pdata->keycode = KEY_HOME;
break;
case KEYADC_BACK:
pdata->keycode = KEY_BACK;
break;
default:
dprintk("this is defalt\n");
break;
}
}
if (KEYADC_GET == cmd){
karg = pdata->keycode;
dprintk("send keycode to usr space: pdata->keycode=0x%x, karg=0x%x\n", pdata->keycode, karg);
ret = copy_to_user(arg, &karg, sizeof(unsigned long));
if(0 == ret){
dprintk("copy_to_user successed!\n");
}
}
return 0;
}
static struct file_operations keyadc_fops = {
.owner = THIS_MODULE,
.open = keyadc_open,
.release = keyadc_release,
.unlocked_ioctl = keyadc_unlocked_ioctl,
};
static void keyadc_do_work(struct work_struct *work)
{
struct keyadc_dev *pdata;
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
pdata = (struct keyadc_dev *)container_of(work, struct keyadc_dev, keyadc_work);
if ((KEY_VOLUMEUP == pdata->keycode) ||
(KEY_VOLUMEDOWN == pdata->keycode) ||
(KEY_HOME == pdata->keycode) ||
(KEY_BACK == pdata->keycode)) {
input_report_key(pdata->input, pdata->keycode, 1);
input_report_key(pdata->input, pdata->keycode, 0);
input_sync(pdata->input);
} else {
dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "No Keycode to Report!");
}
}
static irqreturn_t key_interrupt(int irq, void *pvoid)
{
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
unsigned int reg_val;
struct keyadc_dev *pdata = (struct keyadc_dev *)pvoid;
reg_val = readl(&pdata->regs->ints);
queue_work(pdata->keyadc_workqueue, &pdata->keyadc_work);
writel(reg_val, &pdata->regs->ints);
return IRQ_HANDLED;
}
static int keyadc_probe(struct platform_device *pdev)
{
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
int ret;
struct cdev *pchrdev;
struct keyadc_dev *pdata = pdev->dev.platform_data;
pdata->regs = (struct sun7i_lradc_regs *)LRADC_BASE_ADDR;
alloc_chrdev_region(&pdata->devid, 0, 1, DEVNAME);
pchrdev = cdev_alloc();
pdata->chrdev = *pchrdev;
cdev_init(&pdata->chrdev, &keyadc_fops);
pdata->chrdev.owner = THIS_MODULE;
ret = cdev_add(&pdata->chrdev, pdata->devid, 1);
if (ret) {
dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to cdev_add!");
goto fail0;
}
pdata->input = input_allocate_device();
if (!pdata->input) {
dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to input_allocate_device");
goto fail1;
}
pdata->input->name = INPUTNAME;
set_bit(EV_KEY, pdata->input->evbit);
set_bit(KEY_VOLUMEDOWN, pdata->input->keybit);
set_bit(KEY_VOLUMEUP, pdata->input->keybit);
set_bit(KEY_HOME, pdata->input->keybit);
set_bit(KEY_BACK, pdata->input->keybit);
ret = input_register_device(pdata->input);
if (ret) {
dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to input_register_device");
goto fail2;
}
pdata->class = class_create(THIS_MODULE, DEVNAME);
if (IS_ERR(pdata->class)) {
dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to class_create");
goto fail3;
}
device_create(pdata->class, NULL, pdata->devid, NULL, DEVNAME);
writel(LRADC_ADC0_DOWN_EN|LRADC_ADC0_UP_EN|LRADC_ADC0_DATA_EN, &pdata->regs->intc);
writel(FIRST_CONCERT_DLY|LEVELB_VOL|KEY_MODE_SELECT|LRADC_HOLD_EN|ADC_CHAN_SELECT|LRADC_SAMPLE_125HZ|LRADC_EN, &pdata->regs->ctrl);
if (request_irq(KEY_IRQNO, key_interrupt, 0, IRQNAME, pdata)) {
dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to request_irq");
goto fail3;
}
pdata->keyadc_workqueue = create_singlethread_workqueue(WORKQUEUENAME);
if (NULL == pdata->keyadc_workqueue) {
dprintk("=====[%s(%d)]:%s\n", __FUNCTION__, __LINE__, "fail to create_singlethread_workqueue");
goto fail4;
}
INIT_WORK(&pdata->keyadc_work, keyadc_do_work);
return 0;
fail4:
destroy_workqueue(pdata->keyadc_workqueue);
fail3:
free_irq(KEY_IRQNO, pdata);
device_destroy(pdata->class, pdata->devid);
class_destroy(pdata->class);
fail2:
input_unregister_device(pdata->input);
fail1:
input_free_device(pdata->input);
fail0:
cdev_del(&pdata->chrdev);
kfree(pdata);
return ret;
}
static int keyadc_remove(struct platform_device *pdev)
{
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
struct keyadc_dev *pdata = pdev->dev.platform_data;
destroy_workqueue(pdata->keyadc_workqueue);
device_destroy(pdata->class, pdata->devid);
class_destroy(pdata->class);
input_free_device(pdata->input);
cdev_del(&pdata->chrdev);
free_irq(KEY_IRQNO, pdata);
//kfree(pdata);
return 0;
}
static struct platform_driver keyadc_driver = {
.probe = keyadc_probe,
.remove = keyadc_remove,
.driver = {
.name = PLATFORMNAME,
.owner = THIS_MODULE,
}
};
static int __init keyadc_init(void)
{
int ret;
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
ret = platform_device_register(&keyadc_device);
if (ret < 0) {
dprintk("ERROR:Fail to platform_device_register!\n");
return ret;
}
ret = platform_driver_register(&keyadc_driver);
if (ret < 0) {
dprintk("ERROR:Fail to platform_driver_register!\n");
return ret;
}
return 0;
}
static void __exit keyadc_exit(void)
{
dprintk("=====[%s(%d)]\n", __FUNCTION__, __LINE__);
platform_device_unregister(&keyadc_device);
platform_driver_unregister(&keyadc_driver);
}
MODULE_AUTHOR("Jack Chen, [email protected]");
MODULE_LICENSE("GPL");
module_init(keyadc_init);
module_exit(keyadc_exit);