From 5c9ec517d075e052bc99eacc3d14382e70ddb184 Mon Sep 17 00:00:00 2001 From: James Wylder Date: Thu, 10 Feb 2011 11:03:22 -0600 Subject: [PATCH] misc: radio_ctrl: Create radio control interface Migrate the mdm6x00 sysfs interface to its own driver so that additional drivers can be added. Change-Id: Ia647f298adaba75ac296b2b33e632ba55e4dd55a Signed-off-by: James Wylder --- drivers/misc/Kconfig | 12 +- drivers/misc/Makefile | 2 +- drivers/misc/radio_ctrl/Kconfig | 15 ++ drivers/misc/radio_ctrl/Makefile | 6 + drivers/misc/{ => radio_ctrl}/mdm6600_ctrl.c | 129 +++--------- drivers/misc/radio_ctrl/radio_class.c | 189 ++++++++++++++++++ include/linux/{ => radio_ctrl}/mdm6600_ctrl.h | 0 include/linux/radio_ctrl/radio_class.h | 37 ++++ 8 files changed, 276 insertions(+), 114 deletions(-) create mode 100644 drivers/misc/radio_ctrl/Kconfig create mode 100644 drivers/misc/radio_ctrl/Makefile rename drivers/misc/{ => radio_ctrl}/mdm6600_ctrl.c (83%) create mode 100644 drivers/misc/radio_ctrl/radio_class.c rename include/linux/{ => radio_ctrl}/mdm6600_ctrl.h (100%) create mode 100644 include/linux/radio_ctrl/radio_class.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d9b6f3b5f9bf..9c4ef5015d6e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -210,17 +210,6 @@ config KERNEL_DEBUGGER_CORE Generic kernel debugging command processor used by low level (interrupt context) platform-specific debuggers. -config MDM6600_CTRL - bool "Motorola Modem Controller" - default n - ---help--- - Enables the device driver to control and interface with - the modem co-processor. This module is needed to monitor - modem panics, interact with the modem during factory resets, - and allow modem power up/down support. - - If unsure, say N. - config SGI_XP tristate "Support communication between SGI SSIs" depends on NET @@ -512,5 +501,6 @@ source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" source "drivers/misc/ts27010mux/Kconfig" source "drivers/misc/iwmc3200top/Kconfig" +source "drivers/misc/radio_ctrl/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 94e552057368..c7c058e406cf 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -47,7 +47,7 @@ obj-$(CONFIG_SENSORS_MAX9635) += max9635.o obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o obj-$(CONFIG_SENSORS_L3G4200D) += l3g4200d.o obj-$(CONFIG_GPS_GPIO_BRCM4750) += gps-gpio-brcm4750.o -obj-$(CONFIG_MDM6600_CTRL) += mdm6600_ctrl.o +obj-y += radio_ctrl/ obj-$(CONFIG_SENSORS_MOTO_BMP085) += moto_bmp085.o obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o obj-$(CONFIG_TS27010MUX) += ts27010mux/ diff --git a/drivers/misc/radio_ctrl/Kconfig b/drivers/misc/radio_ctrl/Kconfig new file mode 100644 index 000000000000..4a24f50d0864 --- /dev/null +++ b/drivers/misc/radio_ctrl/Kconfig @@ -0,0 +1,15 @@ +config RADIO_CTRL_CLASS + bool "Radio Modem Controller Class" + default n + +config MDM6600_CTRL + bool "Motorola MDM6X00 Modem Controller" + default n + select RADIO_CTRL_CLASS + ---help--- + Enables the device driver to control and interface with + the modem co-processor. This module is needed to monitor + modem panics, interact with the modem during factory resets, + and allow modem power up/down support. + + If unsure, say N. diff --git a/drivers/misc/radio_ctrl/Makefile b/drivers/misc/radio_ctrl/Makefile new file mode 100644 index 000000000000..68936c356a87 --- /dev/null +++ b/drivers/misc/radio_ctrl/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for modem (radio) control drivers +# + +obj-$(CONFIG_RADIO_CTRL_CLASS) += radio_class.o +obj-$(CONFIG_MDM6600_CTRL) += mdm6600_ctrl.o diff --git a/drivers/misc/mdm6600_ctrl.c b/drivers/misc/radio_ctrl/mdm6600_ctrl.c similarity index 83% rename from drivers/misc/mdm6600_ctrl.c rename to drivers/misc/radio_ctrl/mdm6600_ctrl.c index 7eb2a688a463..a6fa4fb902f8 100644 --- a/drivers/misc/mdm6600_ctrl.c +++ b/drivers/misc/radio_ctrl/mdm6600_ctrl.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +25,8 @@ #include #include #include +#include +#include #define AP_STATUS_BP_PANIC_ACK 0x00 #define AP_STATUS_DATA_ONLY_BYPASS 0x01 @@ -84,9 +85,8 @@ static DEFINE_MUTEX(mdm_ctrl_info_lock); struct workqueue_struct *working_queue = NULL; -static dev_t dev_number; struct class *radio_cls = NULL; -struct device *mdm_dev = NULL; +static struct radio_dev radio_cdev; static unsigned int bp_status_idx = BP_STATUS_UNDEFINED; static unsigned int bp_power_idx = 0; @@ -111,8 +111,7 @@ static const char *bp_power_state_string(unsigned int stat) return "status out of range"; } -static ssize_t mdm_status_show(struct device *dev, - struct device_attribute *attr, char *buff) +static ssize_t mdm_status_show(struct radio_dev *dev, char *buff) { ssize_t status = 0; status = snprintf(buff, BP_STATUS_MAX_LENGTH, "%s\n", @@ -121,8 +120,7 @@ static ssize_t mdm_status_show(struct device *dev, return status; } -static ssize_t mdm_power_show(struct device *dev, - struct device_attribute *attr, char *buff) +static ssize_t mdm_power_show(struct radio_dev *rdev, char *buff) { ssize_t status = 0; status = snprintf(buff, BP_STATUS_MAX_LENGTH, "%s\n", @@ -131,21 +129,8 @@ static ssize_t mdm_power_show(struct device *dev, return status; } -static ssize_t mdm_user_command(struct device *dev, - struct device_attribute *attr, const char *buff, - size_t size) +static ssize_t mdm_user_command(struct radio_dev *rdev, char *post_strip) { - char tmp[BP_COMMAND_MAX_LENGTH]; - char *post_strip = NULL; - - if (size > BP_COMMAND_MAX_LENGTH - 1) { - return size; - } - - /* strip whitespaces if any */ - memcpy(tmp, buff, size); - tmp[size] = '\0'; - post_strip = strim(tmp); pr_info("%s: user command = %s\n", mdmctrl, post_strip); @@ -157,15 +142,13 @@ static ssize_t mdm_user_command(struct device *dev, mdm_ctrl_set_bootmode(0); } else if (strcmp(post_strip,"bootmode_flash") == 0) { mdm_ctrl_set_bootmode(1); + } else { + return -EINVAL; } - return size; + return 0; } -static DEVICE_ATTR(status, 0444, mdm_status_show, NULL); -static DEVICE_ATTR(power_status, 0444, mdm_power_show, NULL); -static DEVICE_ATTR(command, 0200, NULL, mdm_user_command); - static unsigned int mdm_gpio_get_value(struct mdm_ctrl_gpio gpio) { return gpio_get_value(gpio.number); @@ -312,7 +295,7 @@ static void update_bp_status(void) { bp_status_string(bp_status_idx), bp_power_state_string(bp_power_idx)); - kobject_uevent(&mdm_dev->kobj, KOBJ_CHANGE); + kobject_uevent(&radio_cdev.dev->kobj, KOBJ_CHANGE); } static void mdm_ctrl_powerup(void) @@ -340,8 +323,6 @@ static void mdm_ctrl_powerup(void) static void mdm_ctrl_set_bootmode(int mode) { - unsigned int bp_status; - mutex_lock(&mdm_ctrl_info_lock); if (mdm_ctrl.pdata && ((mode == 0) || (mode == 1))) { gpio_request(mdm_ctrl.pdata->cmd_gpios.cmd1, @@ -425,6 +406,13 @@ static void mdm_gpio_cleanup_internal(void) mutex_unlock(&mdm_ctrl_info_lock); } +static struct radio_dev radio_cdev = { + .name = "mdm6600", + .power_status = mdm_power_show, + .status = mdm_status_show, + .command = mdm_user_command, +}; + static int __devinit mdm_ctrl_probe(struct platform_device *pdev) { int i; @@ -432,42 +420,7 @@ static int __devinit mdm_ctrl_probe(struct platform_device *pdev) dev_info(&pdev->dev, "mdm_ctrl_probe"); - if (alloc_chrdev_region(&dev_number, 0, 1, "mdm_ctrl") < 0) { - dev_err(&pdev->dev, "Can't register new device."); - return -1; - } - - /* /sys/class/radio */ - radio_cls = class_create(THIS_MODULE, "radio"); - if (IS_ERR(radio_cls)) { - dev_err(&pdev->dev, "Failed to create radio class."); - goto err_cls; - } - - /* /sys/class/radio/mdm6600 */ - mdm_dev = device_create(radio_cls, NULL, dev_number, NULL, "mdm6600"); - if (IS_ERR(mdm_dev)) { - dev_err(&pdev->dev, "Failed to create mdm_dev."); - goto err_mdm; - } - - /* /sys/class/radio/mdm6600/status */ - if (device_create_file(mdm_dev, &dev_attr_status) > 0) { - dev_err(&pdev->dev, "Failed to create status sysfile."); - goto err_status; - } - - /* /sys/class/radio/mdm6600/power_status */ - if (device_create_file(mdm_dev, &dev_attr_power_status) > 0) { - dev_err(&pdev->dev, "Failed to create power sysfile ."); - goto err_power; - } - - /* /sys/class/radio/mdm6600/command */ - if (device_create_file(mdm_dev, &dev_attr_command) > 0) { - dev_err(&pdev->dev, "Failed to create command sysfile."); - goto err_command; - } + pr_debug("%s: radio_cdev = %p\n", __func__, &radio_cdev); for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) { if (mdm_gpio_setup(&pdata->gpios[i])) { @@ -488,6 +441,11 @@ static int __devinit mdm_ctrl_probe(struct platform_device *pdev) goto err_setup; } + if (radio_dev_register(&radio_cdev)) { + pr_err("%s: failed to register mdm_ctr device\n", __func__); + goto err_setup; + } + update_bp_status(); return 0; @@ -502,27 +460,6 @@ probe_cleanup: for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) mdm_gpio_free(&pdata->gpios[i]); -err_command: - device_remove_file(mdm_dev, &dev_attr_command); - -err_power: - device_remove_file(mdm_dev, &dev_attr_power_status); - -err_status: - device_remove_file(mdm_dev, &dev_attr_status); - -err_mdm: - if (!IS_ERR_OR_NULL(mdm_dev)) { - device_destroy(radio_cls, dev_number); - mdm_dev = NULL; - } - -err_cls: - if (!IS_ERR_OR_NULL(radio_cls)) { - class_destroy(radio_cls); - radio_cls = NULL; - } - return -1; } @@ -533,6 +470,8 @@ static int __devexit mdm_ctrl_remove(struct platform_device *pdev) dev_info(&pdev->dev, "cleanup\n"); + radio_dev_unregister(&radio_cdev); + mdm_gpio_cleanup_internal(); if (working_queue) @@ -541,20 +480,6 @@ static int __devexit mdm_ctrl_remove(struct platform_device *pdev) for (i = 0; i < MDM_CTRL_NUM_GPIOS; i++) mdm_gpio_free(&pdata->gpios[i]); - device_remove_file(mdm_dev, &dev_attr_command); - device_remove_file(mdm_dev, &dev_attr_power_status); - device_remove_file(mdm_dev, &dev_attr_status); - - if (!IS_ERR_OR_NULL(mdm_dev)) { - device_destroy(radio_cls, dev_number); - mdm_dev = NULL; - } - - if (!IS_ERR_OR_NULL(radio_cls)) { - class_destroy(radio_cls); - radio_cls = NULL; - } - return 0; } @@ -651,6 +576,6 @@ module_init(mdm6600_ctrl_init); module_exit(mdm6600_ctrl_exit); MODULE_AUTHOR("Motorola"); -MODULE_DESCRIPTION("Modem Control Driver"); -MODULE_VERSION("1.1.3"); +MODULE_DESCRIPTION("MDM6X00 Control Driver"); +MODULE_VERSION("1.1.4"); MODULE_LICENSE("GPL"); diff --git a/drivers/misc/radio_ctrl/radio_class.c b/drivers/misc/radio_ctrl/radio_class.c new file mode 100644 index 000000000000..33a9adac8236 --- /dev/null +++ b/drivers/misc/radio_ctrl/radio_class.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2011 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +static struct class *radio_cls; + +/* show the device status */ +static ssize_t radio_status_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + struct radio_dev *rdev = dev_get_drvdata(dev); + + if (!rdev || !rdev->status) { + pr_err("%s: supporting function not found\n", __func__); + return -ENODEV; + } + + return rdev->status(rdev, buff); +} + +/* show the device power state */ +static ssize_t radio_power_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + struct radio_dev *rdev = dev_get_drvdata(dev); + + if (!rdev || !rdev->power_status) { + pr_err("%s: supporting function not found\n", __func__); + return -ENODEV; + } + return rdev->power_status(rdev, buff); +} + +/* control the radio device */ +static ssize_t radio_command(struct device *dev, + struct device_attribute *attr, const char *buff, size_t size) +{ + struct radio_dev *rdev = dev_get_drvdata(dev); + char tmp[RADIO_COMMAND_MAX_LENGTH]; + char *post_strip = NULL; + ssize_t ret; + + if (!rdev || !rdev->command) { + pr_err("%s: supporting function not found\n", __func__); + return -ENODEV; + } + pr_debug("%s: rdev.name = %s\n", __func__, rdev->name); + + if (size > RADIO_COMMAND_MAX_LENGTH - 1) { + pr_err("%s: command too long\n", __func__); + return -EINVAL; + } + + /* strip whitespaces if any */ + memcpy(tmp, buff, size); + tmp[size] = '\0'; + post_strip = strim(tmp); + + pr_debug("%s: command = %s size = %d\n", __func__, post_strip, size); + + ret = rdev->command(rdev, post_strip); + return ret >= 0 ? size : ret; +} + +static DEVICE_ATTR(status, S_IRUGO, radio_status_show, NULL); +static DEVICE_ATTR(power_status, S_IRUGO, radio_power_show, NULL); +static DEVICE_ATTR(command, S_IWUSR, NULL, radio_command); + +int radio_dev_register(struct radio_dev *rdev) +{ + int err = -1; + + rdev->dev = device_create(radio_cls, NULL, 0, rdev, + "%s", rdev->name); + if (IS_ERR(rdev->dev)) + return PTR_ERR(rdev->dev); + + pr_info("%s: register %s\n", __func__, rdev->name); + pr_debug("%s: dev = %p\n", __func__, rdev->dev); + + /* /sys/class/radio//status */ + if (rdev->status) { + err = device_create_file(rdev->dev, &dev_attr_status); + if (err < 0) { + pr_err("%s: status file create failed for %s\n", + __func__, rdev->name); + goto err_status; + } + } + + /* /sys/class/radio//power_status */ + if (rdev->power_status) { + err = device_create_file(rdev->dev, &dev_attr_power_status); + if (err < 0) { + pr_err("%s: power_status file create failed for %s\n", + __func__, rdev->name); + goto err_pwr_status; + } + } + + /* /sys/class/radio//command */ + if (rdev->command) { + err = device_create_file(rdev->dev, &dev_attr_command); + if (err < 0) { + pr_err("%s: command file create failed for %s\n", + __func__, rdev->name); + goto err_command; + } + } + + return 0; + +err_command: + device_remove_file(rdev->dev, &dev_attr_power_status); +err_pwr_status: + device_remove_file(rdev->dev, &dev_attr_status); +err_status: + put_device(rdev->dev); + device_unregister(rdev->dev); + dev_set_drvdata(rdev->dev, NULL); + + return err; +} +EXPORT_SYMBOL(radio_dev_register); + +void radio_dev_unregister(struct radio_dev *rdev) +{ + if (rdev) { + if (rdev->command) + device_remove_file(rdev->dev, &dev_attr_command); + if (rdev->power_status) + device_remove_file(rdev->dev, &dev_attr_power_status); + if (rdev->status) + device_remove_file(rdev->dev, &dev_attr_status); + + put_device(rdev->dev); + device_unregister(rdev->dev); + dev_set_drvdata(rdev->dev, NULL); + } +} +EXPORT_SYMBOL(radio_dev_unregister); + +static int __init radio_class_init(void) +{ + pr_info("%s: initialized radio_class\n", __func__); + + /* /sys/class/radio */ + radio_cls = class_create(THIS_MODULE, "radio"); + if (IS_ERR(radio_cls)) { + pr_err("%s: failed to initialize radio class\n", __func__); + return PTR_ERR(radio_cls); + } + + return 0; +} + +static void __exit radio_class_exit(void) +{ + pr_info("%s: destroy radio_class\n", __func__); + class_destroy(radio_cls); +} + +module_init(radio_class_init); +module_exit(radio_class_exit); + +MODULE_AUTHOR("Jim Wylder "); +MODULE_DESCRIPTION("Radio Control Class"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mdm6600_ctrl.h b/include/linux/radio_ctrl/mdm6600_ctrl.h similarity index 100% rename from include/linux/mdm6600_ctrl.h rename to include/linux/radio_ctrl/mdm6600_ctrl.h diff --git a/include/linux/radio_ctrl/radio_class.h b/include/linux/radio_ctrl/radio_class.h new file mode 100644 index 000000000000..d09b2dcb56c7 --- /dev/null +++ b/include/linux/radio_ctrl/radio_class.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ +#ifndef __RADIO_CLASS_H__ +#define __RADIO_CLASS_H__ + +struct radio_dev { + const char *name; + struct device *dev; + + ssize_t (*power_status)(struct radio_dev *rdev, char *buf); + ssize_t (*status)(struct radio_dev *rdev, char *buf); + ssize_t (*command)(struct radio_dev *rdev, char *buf); +}; + +extern void radio_dev_unregister(struct radio_dev *radio_cdev); +extern int radio_dev_register(struct radio_dev *radio_cdev); + +#define RADIO_STATUS_MAX_LENGTH 32 +#define RADIO_COMMAND_MAX_LENGTH 32 +#define RADIO_BOOTMODE_NORMAL 0 +#define RADIO_BOOTMODE_FLASH 1 +#endif /* __RADIO_CLASS_H__ */ -- 2.34.1