Adds Modem Access Framework, which allows for registering platform specific modem access mechanisms. The framework also exposes APIs for client drivers for getting and releasing access to modem, regardless of the underlying platform specific access mechanism. Signed-off-by: Arun Murthy <arun.murthy@xxxxxxxxxxxxxx> --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/modem_shm/Kconfig | 9 ++ drivers/modem_shm/Makefile | 1 + drivers/modem_shm/modem_access.c | 161 ++++++++++++++++++++++++++++++++++++++ include/linux/modem_shm/modem.h | 54 +++++++++++++ 6 files changed, 228 insertions(+), 0 deletions(-) create mode 100644 drivers/modem_shm/Kconfig create mode 100644 drivers/modem_shm/Makefile create mode 100644 drivers/modem_shm/modem_access.c create mode 100644 include/linux/modem_shm/modem.h diff --git a/drivers/Kconfig b/drivers/Kconfig index ece958d..dc7c14a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -152,4 +152,6 @@ source "drivers/vme/Kconfig" source "drivers/pwm/Kconfig" +source "drivers/modem_shm/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 5b42184..902dfec 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -139,3 +139,4 @@ obj-$(CONFIG_EXTCON) += extcon/ obj-$(CONFIG_MEMORY) += memory/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_VME_BUS) += vme/ +obj-$(CONFIG_MODEM_SHM) += modem_shm/ diff --git a/drivers/modem_shm/Kconfig b/drivers/modem_shm/Kconfig new file mode 100644 index 0000000..f4b7e54 --- /dev/null +++ b/drivers/modem_shm/Kconfig @@ -0,0 +1,9 @@ +config MODEM_SHM + bool "Modem Access Framework" + default n + help + Add support for Modem Access Framework. It allows different + platform specific drivers to register modem access mechanisms + and allows transparent access to modem to the client drivers. + + If unsure, say N. diff --git a/drivers/modem_shm/Makefile b/drivers/modem_shm/Makefile new file mode 100644 index 0000000..b77bcc0 --- /dev/null +++ b/drivers/modem_shm/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MODEM_SHM) := modem_access.o diff --git a/drivers/modem_shm/modem_access.c b/drivers/modem_shm/modem_access.c new file mode 100644 index 0000000..540234d --- /dev/null +++ b/drivers/modem_shm/modem_access.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Kumar Sanghvi + * Arun Murthy <arun.murthy@xxxxxxxxxxxxxx> + * + * Heavily adapted from Regulator framework. + * Provides mechanisms for registering platform specific access + * mechanisms for modem. + * Also, exposes APIs for gettng/releasing the access and even + * query the access status, and the modem usage status. + */ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/printk.h> +#include <linux/modem_shm/modem.h> + +static struct class *modem_class; + +static int __modem_is_requested(struct device *dev, void *data) +{ + struct modem_desc *mdesc = (struct modem_desc *)data; + + if (!mdesc->mclients) { + printk(KERN_ERR "modem_access: modem description is NULL\n"); + return 0; + } + return atomic_read(&mdesc->mclients->cnt); +} + +int modem_is_requested(struct modem_desc *mdesc) +{ + return class_for_each_device(modem_class, NULL, (void *)mdesc, __modem_is_requested); +} + +int modem_release(struct modem_desc *mdesc) +{ + if (!mdesc->release) + return -EFAULT; + + if (modem_is_requested(mdesc)) { + atomic_dec(&mdesc->mclients->cnt); + if (atomic_read(&mdesc->use_cnt) == 1) { + mdesc->release(mdesc); + atomic_dec(&mdesc->use_cnt); + } + } else + printk(KERN_WARNING + "modem_shm: client %s has not requested modem to release\n", + mdesc->mclients->name); + return 0; +} + +int modem_request(struct modem_desc *mdesc) +{ + if (!mdesc->request) + return -EFAULT; + + if (atomic_read(&mdesc->mclients->cnt) == 0) { + mdesc->request(mdesc); + atomic_inc(&mdesc->mclients->cnt); + atomic_inc(&mdesc->use_cnt); + if (atomic_read(&mdesc->use_cnt) > mdesc->no_clients) { + dev_warn(mdesc->dev, + "modem_shm: mismatch in the modem count\n"); + } + } else + dev_warn(mdesc->dev, + "modem_shm: client '%s' has already requested modem\n", + dev_name(mdesc->mclients->dev)); + return 0; +} + +int modem_put(struct modem_desc *mdesc) +{ + if (atomic_read(&mdesc->cli_cnt)) { + atomic_dec(&mdesc->cli_cnt); + kfree(mdesc->mclients); + return 0; + } else { + dev_err(mdesc->dev, "mismatch in the no of clients\n"); + return -EFAULT; + } +} + +static int modem_match_device_by_name(struct device *dev, void *data) +{ + const char *name = data; + struct modem_desc *mdesc = dev_get_drvdata(dev); + + return strcmp(mdesc->name, name) == 0; +} + +struct modem_desc *modem_get(struct device *pdev, const char *name) +{ + struct clients *mcli; + struct modem_desc *mdesc = NULL; + struct device *dev = class_find_device(modem_class, NULL, (void *)name, + modem_match_device_by_name); + mdesc = dev ? dev_get_drvdata(dev): NULL; + if (mdesc) { + if (atomic_read(&mdesc->cli_cnt) >= mdesc->no_clients) { + dev_err(pdev, "already %d clients have requested\n", + mdesc->no_clients); + return NULL; + } + mcli = kzalloc(sizeof(struct clients), GFP_KERNEL); + if (!mcli) { + dev_err(pdev, "uanable to allocate memory\n"); + return NULL; + } + mdesc->mclients = mcli; + mdesc->mclients->dev = pdev; + atomic_inc(&mdesc->cli_cnt); + atomic_set(&mdesc->mclients->cnt, 0); + } + return mdesc; +} + +int modem_register(struct device *parent, struct modem_desc *mdesc) +{ + mdesc->dev = device_create(modem_class, parent, 0, mdesc, "%s", mdesc->name); + if (IS_ERR(mdesc->dev)) { + dev_err(parent, "failed to create device\n"); + return PTR_ERR(mdesc->dev); + } + + atomic_set(&mdesc->use_cnt, 0); + atomic_set(&mdesc->cli_cnt, 0); + return 0; +} + +int modem_unregister(struct modem_desc *mdesc) +{ + device_unregister(mdesc->dev); + return 0; +} + +static int modem_init(void) +{ + modem_class = class_create(THIS_MODULE, "modem_access"); + if (IS_ERR(modem_class)) { + printk(KERN_ERR "modem_access: unable to create class\n"); + return PTR_ERR(modem_class); + } + + if (modem_class == NULL || IS_ERR(modem_class)) + printk(KERN_ERR "modem_access: MODEM ERR0R"); + + return 0; +} + +static void modem_exit(void) +{ + class_destroy(modem_class); +} + +arch_initcall(modem_init); +module_exit(modem_exit); diff --git a/include/linux/modem_shm/modem.h b/include/linux/modem_shm/modem.h new file mode 100644 index 0000000..0addf48 --- /dev/null +++ b/include/linux/modem_shm/modem.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * Author: Kumar Sanghvi + * Arun Murthy <arun.murthy@xxxxxxxxxxxxxx> + * + * Heavily adapted from Regulator framework + */ +#ifndef __MODEM_H__ +#define __MODEM_H__ + +#include <linux/device.h> + +struct clients { + struct device *dev; + const char *name; + atomic_t cnt; +}; + +struct modem_desc { + int (*request)(struct modem_desc *); + void (*release)(struct modem_desc *); + int (*is_requested)(struct modem_desc *); + struct clients *mclients; + struct device *dev; + char *name; + u8 no_clients; + atomic_t use_cnt; + atomic_t cli_cnt; +}; + +#ifdef CONFIG_MODEM_SHM +int modem_register(struct device *parent, struct modem_desc *mdesc); +int modem_unregister(struct modem_desc *mdesc); +struct modem_desc *modem_get(struct device *dev, const char *name); +int modem_put(struct modem_desc *mdesc); +int modem_release(struct modem_desc *mdesc); +int modem_is_requested(struct modem_desc *mdesc); +int modem_request(struct modem_desc *mdesc); + + +#else +int modem_register(struct device *parent, struct modem_desc *mdesc) +{ + return NULL; +} + +static inline int modem_unregister(struct modem_desc *mdesc) +{ + return NULL; +} +#endif +#endif /* __MODEM_H__ */ -- 1.7.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html