135 lines · 2.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Generic push-switch framework * * Copyright (C) 2006  Paul Mundt */#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <asm/push-switch.h> #define DRV_NAME "push-switch"#define DRV_VERSION "0.1.1" static ssize_t switch_show(struct device *dev,			   struct device_attribute *attr,			   char *buf){	struct push_switch_platform_info *psw_info = dev->platform_data;	return sprintf(buf, "%s\n", psw_info->name);}static DEVICE_ATTR_RO(switch); static void switch_timer(struct timer_list *t){	struct push_switch *psw = timer_container_of(psw, t, debounce); 	schedule_work(&psw->work);} static void switch_work_handler(struct work_struct *work){	struct push_switch *psw = container_of(work, struct push_switch, work);	struct platform_device *pdev = psw->pdev; 	psw->state = 0; 	kobject_uevent(&pdev->dev.kobj, KOBJ_CHANGE);} static int switch_drv_probe(struct platform_device *pdev){	struct push_switch_platform_info *psw_info;	struct push_switch *psw;	int ret, irq; 	psw = kzalloc_obj(struct push_switch);	if (unlikely(!psw))		return -ENOMEM; 	irq = platform_get_irq(pdev, 0);	if (unlikely(irq < 0)) {		ret = -ENODEV;		goto err;	} 	psw_info = pdev->dev.platform_data;	BUG_ON(!psw_info); 	ret = request_irq(irq, psw_info->irq_handler,			  psw_info->irq_flags,			  psw_info->name ? psw_info->name : DRV_NAME, pdev);	if (unlikely(ret < 0))		goto err; 	if (psw_info->name) {		ret = device_create_file(&pdev->dev, &dev_attr_switch);		if (unlikely(ret)) {			dev_err(&pdev->dev, "Failed creating device attrs\n");			ret = -EINVAL;			goto err_irq;		}	} 	INIT_WORK(&psw->work, switch_work_handler);	timer_setup(&psw->debounce, switch_timer, 0); 	/* Workqueue API brain-damage */	psw->pdev = pdev; 	platform_set_drvdata(pdev, psw); 	return 0; err_irq:	free_irq(irq, pdev);err:	kfree(psw);	return ret;} static void switch_drv_remove(struct platform_device *pdev){	struct push_switch *psw = platform_get_drvdata(pdev);	struct push_switch_platform_info *psw_info = pdev->dev.platform_data;	int irq = platform_get_irq(pdev, 0); 	if (psw_info->name)		device_remove_file(&pdev->dev, &dev_attr_switch); 	platform_set_drvdata(pdev, NULL);	timer_shutdown_sync(&psw->debounce);	flush_work(&psw->work);	free_irq(irq, pdev); 	kfree(psw);} static struct platform_driver switch_driver = {	.probe		= switch_drv_probe,	.remove		= switch_drv_remove,	.driver		= {		.name	= DRV_NAME,	},}; static int __init switch_init(void){	printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);	return platform_driver_register(&switch_driver);} static void __exit switch_exit(void){	platform_driver_unregister(&switch_driver);}module_init(switch_init);module_exit(switch_exit); MODULE_VERSION(DRV_VERSION);MODULE_AUTHOR("Paul Mundt");MODULE_DESCRIPTION("Generic push-switch framework");MODULE_LICENSE("GPL v2");