/*++ Some descriptions of such software. Copyright (c) 2010 WonderMedia Technologies, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . WonderMedia Technologies, Inc. 2010-2-26, HangYan, ShenZhen --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wmt_vibrate.h" #include "timed_output.h" /* Constant Macro */ #define WMT_VIBRATE_DEVICE_NAME "wmt_vibrate" #define WMT_VIBRATE_MAJOR 37 //#define WMT_VIBRATE_MINOR 0 //#define WMT_VIBRATE_DELAY 250 //ms static int WMT_VIBRATE_DELAY = 90; /*************************Function declare************************************/ static void enable_timedoutput(struct timed_output_dev *sdev, int timeout); static int gettime_timedoutput(struct timed_output_dev *sdev); //#define DEBUG #ifdef DEBUG #define vibrate_dbg(fmt, args...) printk(KERN_ALERT "[%s]: " fmt, __FUNCTION__ , ## args) #else #define vibrate_dbg(fmt, args...) #endif static struct timer_list vibrate_timer; /************************Data type and local variable****************/ struct vibrate_device { int vibratile; // 0: shouldn't vibrate, 1:should vibrate struct mutex mlock; struct class* dev_class; struct device *device; unsigned int vibtimeout; struct work_struct work; struct timed_output_dev vibtimedev; /* setting attributy */ int name; int active; unsigned int bmp; unsigned int ctraddr; unsigned int ocaddr; unsigned int odaddr; unsigned int bitx; }; static struct vibrate_device l_vibratedev = { .vibratile = 0, .dev_class = NULL, .device = NULL, .vibtimeout = 0, .vibtimedev = { .name = "vibrator", .enable = enable_timedoutput, .get_time = gettime_timedoutput, }, /* default reg setting: Use GPIO1 to control motor */ .name = 1, .active = 1, .bmp = 0x2, .ctraddr = GPIO_BASE_ADDR + 0x40, .ocaddr = GPIO_BASE_ADDR + 0x80, .odaddr = GPIO_BASE_ADDR + 0xc0, }; /************************Function implement*******************/ static void wmt_disable_vibrator(unsigned long unused) { if (l_vibratedev.active == 0) REG32_VAL(l_vibratedev.odaddr) |= l_vibratedev.bmp; else REG32_VAL(l_vibratedev.odaddr) &= ~l_vibratedev.bmp; } static void enable_timedoutput(struct timed_output_dev *sdev, int timeout) { /* mutex_lock(&l_vibratedev.mlock); l_vibratedev.vibtimeout = timeout; mutex_unlock(&l_vibratedev.mlock); //wakeup work to vibrate if (l_vibratedev.vibtimeout) schedule_work(&l_vibratedev.work); */ if (0 == timeout) { // Don't vibrate return; } del_timer_sync(&vibrate_timer); if (l_vibratedev.active == 0) REG32_VAL(l_vibratedev.odaddr) &= ~l_vibratedev.bmp; /* low active */ else REG32_VAL(l_vibratedev.odaddr) |= l_vibratedev.bmp; /* high active */ if (timeout < WMT_VIBRATE_DELAY) { timeout = WMT_VIBRATE_DELAY; } mod_timer(&vibrate_timer, jiffies + msecs_to_jiffies(timeout)); } static int gettime_timedoutput(struct timed_output_dev *sdev) { return l_vibratedev.vibtimeout; } void wmt_do_vibrate(void) { unsigned int vibtime = 0; REG32_VAL(l_vibratedev.ctraddr) |= l_vibratedev.bmp; REG32_VAL(l_vibratedev.ocaddr) |= l_vibratedev.bmp; if (l_vibratedev.active == 0) REG32_VAL(l_vibratedev.odaddr) &= ~l_vibratedev.bmp; /* low active */ else REG32_VAL(l_vibratedev.odaddr) |= l_vibratedev.bmp; /* high active */ // delay if (l_vibratedev.vibtimeout < WMT_VIBRATE_DELAY) { vibtime = WMT_VIBRATE_DELAY; } else { vibtime = l_vibratedev.vibtimeout; } msleep(vibtime); if (l_vibratedev.active == 0) REG32_VAL(l_vibratedev.odaddr) |= l_vibratedev.bmp; else REG32_VAL(l_vibratedev.odaddr) &= ~l_vibratedev.bmp; } // return 0: unvibratile, 1: vibratile int wmt_is_vibratile(void) { return l_vibratedev.vibratile; } static int wmt_vibrate_release(struct inode *inode, struct file *file) { return 0; } static int wmt_vibrate_open(struct inode *inode, struct file *file) { return 0; } static long wmt_vibrate_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int ret = 0; void __user *argp = (void __user *)arg; u8 val8; mutex_lock(&l_vibratedev.mlock); switch (cmd) { case WMT_VIBRATE_ENABLE: if (copy_from_user(&val8, argp, sizeof(val8))) { ret = -EFAULT; break; } l_vibratedev.vibratile= (val8 != 0)? 1:0; /* if (l_vibratedev.vibratile != 0){ wmt_do_vibrate(); } */ break; default: ret = -EINVAL; } mutex_unlock(&l_vibratedev.mlock); return ret; } static const struct file_operations vibrate_fops = { .owner = THIS_MODULE, .open = wmt_vibrate_open, .release = wmt_vibrate_release, .unlocked_ioctl = wmt_vibrate_ioctl, }; /* static int __devinit wmt_vibrate_probe(struct platform_device *dev) { } static int __devexit wmt_vibrate_remove(struct platform_device *dev) { return 0; } static void wmt_vibrate_shutdown(struct platform_device *dev) { } */ static void vibrate_gpio_init(void) { REG32_VAL(l_vibratedev.ctraddr) |= l_vibratedev.bmp; REG32_VAL(l_vibratedev.ocaddr) |= l_vibratedev.bmp; if (l_vibratedev.active == 0) REG32_VAL(l_vibratedev.odaddr) |= l_vibratedev.bmp; else REG32_VAL(l_vibratedev.odaddr) &= ~l_vibratedev.bmp; } #ifdef CONFIG_PM static int wmt_vibrate_suspend(struct platform_device *dev, pm_message_t state) { return 0; } static int wmt_vibrate_resume(struct platform_device *dev) { vibrate_gpio_init(); return 0; } #else #define wmt_vibrate_suspend NULL #define wmt_vibrate_resume NULL #endif static struct platform_driver vibrate_driver = { .driver = { .name = WMT_VIBRATE_DEVICE_NAME, .owner = THIS_MODULE, }, //.probe = wmt_vibrate_probe, //.remove = __devexit_p(wmt_vibrate_remove), //.shutdown = wmt_vibrate_shutdown, .suspend = wmt_vibrate_suspend, .resume = wmt_vibrate_resume, }; static struct platform_device *vibrate_platform_device; /* static ssize_t show_classname(struct class *class, char *buf) { return sprintf(buf, "%s\n", WMT_VIBRATE_DEVICE_NAME); } static CLASS_ATTR(vibrate, 0777, show_classname, NULL); */ //static struct class_attribute cls_attrs = __ATTR(WMT_VIBRATE_DEVICE_NAME, 0777, NULL, NULL); // dev_attr_wmt_vibrate /* static ssize_t show_vibtime(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%dms\n", l_vibratedev.vibtimeout); } static ssize_t set_vibtime(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long val; if (strict_strtoul(buf, 10, &val) != 0) { return count; } return count; } static DEVICE_ATTR(vibtimeout, 0666, show_devname, NULL); */ static void vib_work_handler(struct work_struct *work) { wmt_do_vibrate(); } extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); static int get_vibratorset(void* param) { char buf[128]; unsigned int ubootval[6]; int ret = 0; int varlen = 127; /* read the paraqm from */ memset(buf ,0, sizeof(buf)); memset(ubootval, 0, sizeof(ubootval)); if (wmt_getsyspara("wmt.vibrate.time", buf, &varlen)) { //printk(KERN_WARNING "wmt.gpo.vibrator isn't set in u-boot env! -> Use default\n"); //return -1; WMT_VIBRATE_DELAY = 90; } else { sscanf(buf, "%d", &WMT_VIBRATE_DELAY); } memset(buf ,0, sizeof(buf)); if (wmt_getsyspara("wmt.gpo.vibrator", buf, &varlen)) { printk(KERN_WARNING "wmt.gpo.vibrator isn't set in u-boot env! -> Use default\n"); return -1; } ret = sscanf(buf, "%x:%x:%x:%x:%x:%x", &ubootval[0], &ubootval[1], &ubootval[2], &ubootval[3], &ubootval[4], &ubootval[5]); if (ret != 6) { printk(KERN_ERR "wmt.gpo.vibrator format is incorrect in u-boot\n"); return -2; } vibrate_dbg("wmt.gpo.vibrator = %x:%x:%x:%x:%x:%x\n", ubootval[0], ubootval[1], ubootval[2], ubootval[3], ubootval[4], ubootval[5]); l_vibratedev.name = ubootval[0]; l_vibratedev.active = ubootval[1]; l_vibratedev.bmp = ubootval[2]; l_vibratedev.ctraddr = ubootval[3] + WMT_MMAP_OFFSET; l_vibratedev.ocaddr = ubootval[4] + WMT_MMAP_OFFSET; l_vibratedev.odaddr = ubootval[5] + WMT_MMAP_OFFSET; return 0; } static int __init wmt_vibrate_init(void) { int error = 0; /* get vibrator setting */ if (get_vibratorset(&l_vibratedev) < 0) { printk(KERN_ERR "Wrong vabrating u-boot parameter!\n"); return -EINVAL; } vibrate_gpio_init(); /* other initial */ mutex_init(&l_vibratedev.mlock); //INIT_WORK(&l_vibratedev.work, vib_work_handler); setup_timer(&vibrate_timer, wmt_disable_vibrator, 0); /* register char device */ if (register_chrdev (WMT_VIBRATE_MAJOR, WMT_VIBRATE_DEVICE_NAME, &vibrate_fops)) { printk (KERN_ERR "wmt vibrate: unable to get major %d\n", WMT_VIBRATE_MAJOR); error = -EIO; goto initend; } /* (mknod /dev/wmt_vibrate c 37 0) */ l_vibratedev.dev_class = class_create(THIS_MODULE, WMT_VIBRATE_DEVICE_NAME); if (IS_ERR(l_vibratedev.dev_class)) { error = PTR_ERR(l_vibratedev.dev_class); printk(KERN_ERR "Can't class_create vibrate device !!\n"); goto initend1; } /* if (class_create_file(l_vibratedev.dev_class, &class_attr_vibrate) < 0) { printk(KERN_ERR "Can't add class attr !\n"); return -1; } */ l_vibratedev.device = device_create(l_vibratedev.dev_class, NULL, MKDEV(WMT_VIBRATE_MAJOR, 0), NULL, WMT_VIBRATE_DEVICE_NAME); if (IS_ERR(l_vibratedev.device)) { error = PTR_ERR(l_vibratedev.device); printk(KERN_ERR "Failed to create device %s !!!",WMT_VIBRATE_DEVICE_NAME); goto initend2; } /* if (device_create_file(l_vibratedev.device, &dev_attr_vibrate)) { printk(KERN_ERR "Can't add device attr!!!\n"); return -1; } */ /* create '/sys/class/timed_output/vibrator/enable' */ if ((error = timed_output_dev_register(&l_vibratedev.vibtimedev)) != 0) { goto initend3; } error = platform_driver_register(&vibrate_driver); if (error) goto exit_timed_unregsiter; vibrate_platform_device = platform_device_alloc(WMT_VIBRATE_DEVICE_NAME, -1); if (!vibrate_platform_device) { error = -ENOMEM; printk(KERN_ERR "Can't alloc vibrate_platform_device!!!\n"); goto initend4; } error = platform_device_add(vibrate_platform_device); if (error) goto initend5; printk(KERN_ALERT"WMT vibrater driver load successfully!\n"); return 0; initend5: platform_device_put(vibrate_platform_device); initend4: platform_driver_unregister(&vibrate_driver); exit_timed_unregsiter: timed_output_dev_unregister(&l_vibratedev.vibtimedev); initend3: device_destroy(l_vibratedev.dev_class, MKDEV(WMT_VIBRATE_MAJOR, 0)); initend2: class_destroy(l_vibratedev.dev_class); initend1: unregister_chrdev(WMT_VIBRATE_MAJOR, WMT_VIBRATE_DEVICE_NAME); initend: return error; } static void __exit wmt_vibrate_exit(void) { platform_device_unregister(vibrate_platform_device); platform_driver_unregister(&vibrate_driver); timed_output_dev_unregister(&l_vibratedev.vibtimedev); device_destroy(l_vibratedev.dev_class, MKDEV(WMT_VIBRATE_MAJOR, 0)); class_destroy(l_vibratedev.dev_class); unregister_chrdev(WMT_VIBRATE_MAJOR, WMT_VIBRATE_DEVICE_NAME); printk(KERN_ALERT "WMT vibrate driver is removed.\n"); } module_init(wmt_vibrate_init); module_exit(wmt_vibrate_exit); EXPORT_SYMBOL(wmt_do_vibrate); EXPORT_SYMBOL(wmt_is_vibratile); MODULE_DESCRIPTION("Vibrate Device driver"); MODULE_LICENSE("GPL");