#include #include #include #include #include #include #include #include #include #include #include #include "fsled.h" #define FSLED_MAJOR 256 #define FSLED_DEV_NAME "fsled" struct fsled_dev { unsigned int __iomem *con; unsigned int __iomem *dat; unsigned int pin; atomic_t available; struct cdev cdev; struct device *dev; }; struct class *fsled_cls; static int fsled_open(struct inode *inode, struct file *filp) { struct fsled_dev *fsled = container_of(inode->i_cdev, struct fsled_dev, cdev); filp->private_data = fsled; if (atomic_dec_and_test(&fsled->available)) return 0; else { atomic_inc(&fsled->available); return -EBUSY; } } static int fsled_release(struct inode *inode, struct file *filp) { struct fsled_dev *fsled = filp->private_data; writel(readl(fsled->dat) & ~(0x1 << fsled->pin), fsled->dat); atomic_inc(&fsled->available); return 0; } static long fsled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct fsled_dev *fsled = filp->private_data; if (_IOC_TYPE(cmd) != FSLED_MAGIC) return -ENOTTY; switch (cmd) { case FSLED_ON: writel(readl(fsled->dat) | (0x1 << fsled->pin), fsled->dat); break; case FSLED_OFF: writel(readl(fsled->dat) & ~(0x1 << fsled->pin), fsled->dat); break; default: return -ENOTTY; } return 0; } static struct file_operations fsled_ops = { .owner = THIS_MODULE, .open = fsled_open, .release = fsled_release, .unlocked_ioctl = fsled_ioctl, }; static int fsled_probe(struct platform_device *pdev) { int ret; dev_t dev; struct fsled_dev *fsled; struct resource *res; unsigned int pin = *(unsigned int*)pdev->dev.platform_data; dev = MKDEV(FSLED_MAJOR, pdev->id); ret = register_chrdev_region(dev, 1, FSLED_DEV_NAME); if (ret) goto reg_err; fsled = kzalloc(sizeof(struct fsled_dev), GFP_KERNEL); if (!fsled) { ret = -ENOMEM; goto mem_err; } cdev_init(&fsled->cdev, &fsled_ops); fsled->cdev.owner = THIS_MODULE; ret = cdev_add(&fsled->cdev, dev, 1); if (ret) goto add_err; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENOENT; goto res_err; } fsled->con = ioremap(res->start, resource_size(res)); if (!fsled->con) { ret = -EBUSY; goto map_err; } fsled->dat = fsled->con + 1; fsled->pin = pin; atomic_set(&fsled->available, 1); writel((readl(fsled->con) & ~(0xF << 4 * fsled->pin)) | (0x1 << 4 * fsled->pin), fsled->con); writel(readl(fsled->dat) & ~(0x1 << fsled->pin), fsled->dat); platform_set_drvdata(pdev, fsled); fsled->dev = device_create(fsled_cls, NULL, dev, NULL, "led%d", pdev->id); if (IS_ERR(fsled->dev)) { ret = PTR_ERR(fsled->dev); goto dev_err; } return 0; dev_err: iounmap(fsled->con); map_err: res_err: cdev_del(&fsled->cdev); add_err: kfree(fsled); mem_err: unregister_chrdev_region(dev, 1); reg_err: return ret; } static int fsled_remove(struct platform_device *pdev) { dev_t dev; struct fsled_dev *fsled = platform_get_drvdata(pdev); dev = MKDEV(FSLED_MAJOR, pdev->id); device_destroy(fsled_cls, dev); iounmap(fsled->con); cdev_del(&fsled->cdev); kfree(fsled); unregister_chrdev_region(dev, 1); return 0; } struct platform_driver fsled_drv = { .driver = { .name = "fsled", .owner = THIS_MODULE, }, .probe = fsled_probe, .remove = fsled_remove, }; static int __init fsled_init(void) { int ret; fsled_cls = class_create(THIS_MODULE, "fsled"); if (IS_ERR(fsled_cls)) return PTR_ERR(fsled_cls); ret = platform_driver_register(&fsled_drv); if (ret) class_destroy(fsled_cls); return ret; } static void __exit fsled_exit(void) { platform_driver_unregister(&fsled_drv); class_destroy(fsled_cls); } module_init(fsled_init); module_exit(fsled_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kevin Jiang "); MODULE_DESCRIPTION("A simple character device driver for LEDs on FS4412 board");