From 689ae231afbac8979f96100b372a5a73458baaa9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 27 Jul 2012 22:14:59 +0200 Subject: platform: Add support for automatic device IDs Right now we have support for explicit platform device IDs, as well as ID-less platform devices when a given device type can only have one instance. However there are cases where multiple instances of a device type can exist, and their IDs aren't (and can't be) known in advance and do not matter. In that case we need automatic device IDs to avoid device name collisions. I am using magic ID value -2 (PLATFORM_DEVID_AUTO) for this, similar to -1 for ID-less devices. The automatically allocated device IDs are global (to avoid an additional per-driver cost.) We keep note that the ID was automatically allocated so that it can be freed later. Note that we also restore the ID to PLATFORM_DEVID_AUTO on error and device deletion, to avoid avoid unexpected behavior on retry. I don't really expect retries on platform device addition, but better safe than sorry. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_device.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 60e9994ef40..5711e9525a2 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -14,11 +14,15 @@ #include #include +#define PLATFORM_DEVID_NONE (-1) +#define PLATFORM_DEVID_AUTO (-2) + struct mfd_cell; struct platform_device { const char * name; int id; + bool id_auto; struct device dev; u32 num_resources; struct resource * resource; -- cgit v1.2.3-70-g09d2 From 980d7929816236476967218645c30acc022042eb Mon Sep 17 00:00:00 2001 From: anish kumar Date: Fri, 10 Aug 2012 00:29:43 -0700 Subject: Extcon: adc_jack: adc-jack driver to support 3.5 pi or simliar devices External connector devices that decides connection information based on ADC values may use adc-jack device driver. The user simply needs to provide a table of adc range and connection states. Then, extcon framework will automatically notify others. Changes in V1: added Lars-Peter Clausen suggested changes: Using macros to get rid of boiler plate code such as devm_kzalloc and module_platform_driver.Other changes suggested are related to coding guidelines. Changes in V2: Removed some unnecessary checks and changed the way we are un-regitering extcon and freeing the irq while removing. Changes in V3: Renamed the files to comply with extcon naming. Changes in V4: Added the cancel_work_sync during removing of driver. Changes in V5: Added the dependency of IIO in Kconfig. Changes in V6: Some nitpicks related to naming. Reviewed-by: Lars-Peter Clausen Signed-off-by: anish kumar Signed-off-by: MyungJoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 6 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-adc-jack.c | 195 +++++++++++++++++++++++++++++++++ include/linux/extcon/extcon-adc-jack.h | 73 ++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 drivers/extcon/extcon-adc-jack.c create mode 100644 include/linux/extcon/extcon-adc-jack.h (limited to 'include/linux') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index e175c8ed4ec..dd5b01b957a 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -21,6 +21,12 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. +config EXTCON_ADC_JACK + tristate "ADC Jack extcon support" + depends on IIO + help + Say Y here to enable extcon device driver based on ADC values. + config EXTCON_MAX77693 tristate "MAX77693 EXTCON Support" depends on MFD_MAX77693 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 88961b33234..53d88c1cce3 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_EXTCON) += extcon_class.o obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o +obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c new file mode 100644 index 00000000000..da058f7033e --- /dev/null +++ b/drivers/extcon/extcon-adc-jack.c @@ -0,0 +1,195 @@ +/* + * drivers/extcon/extcon-adc-jack.c + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * Modified for calling to IIO to get adc by + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct adc_jack_data - internal data for adc_jack device driver + * @edev - extcon device. + * @cable_names - list of supported cables. + * @num_cables - size of cable_names. + * @adc_conditions - list of adc value conditions. + * @num_conditions - size of adc_conditions. + * @irq - irq number of attach/detach event (0 if not exist). + * @handling_delay - interrupt handler will schedule extcon event + * handling at handling_delay jiffies. + * @handler - extcon event handler called by interrupt handler. + * @chan - iio channel being queried. + */ +struct adc_jack_data { + struct extcon_dev edev; + + const char **cable_names; + int num_cables; + struct adc_jack_cond *adc_conditions; + int num_conditions; + + int irq; + unsigned long handling_delay; /* in jiffies */ + struct delayed_work handler; + + struct iio_channel *chan; +}; + +static void adc_jack_handler(struct work_struct *work) +{ + struct adc_jack_data *data = container_of(to_delayed_work(work), + struct adc_jack_data, + handler); + u32 state = 0; + int ret, adc_val; + int i; + + ret = iio_read_channel_raw(data->chan, &adc_val); + if (ret < 0) { + dev_err(data->edev.dev, "read channel() error: %d\n", ret); + return; + } + + /* Get state from adc value with adc_conditions */ + for (i = 0; i < data->num_conditions; i++) { + struct adc_jack_cond *def = &data->adc_conditions[i]; + if (!def->state) + break; + if (def->min_adc <= adc_val && def->max_adc >= adc_val) { + state = def->state; + break; + } + } + /* if no def has met, it means state = 0 (no cables attached) */ + + extcon_set_state(&data->edev, state); +} + +static irqreturn_t adc_jack_irq_thread(int irq, void *_data) +{ + struct adc_jack_data *data = _data; + + schedule_delayed_work(&data->handler, data->handling_delay); + return IRQ_HANDLED; +} + +static int __devinit adc_jack_probe(struct platform_device *pdev) +{ + struct adc_jack_data *data; + struct adc_jack_pdata *pdata = pdev->dev.platform_data; + int i, err = 0; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->edev.name = pdata->name; + + if (pdata->cable_names) + data->edev.supported_cable = pdata->cable_names; + else + data->edev.supported_cable = extcon_cable_name; + + /* Check the length of array and set num_cables */ + for (i = 0; data->edev.supported_cable[i]; i++) + ; + if (i == 0 || i > SUPPORTED_CABLE_MAX) { + err = -EINVAL; + dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", + i - 1); + goto out; + } + data->num_cables = i; + + if (!pdata->adc_conditions || + !pdata->adc_conditions[0].state) { + err = -EINVAL; + dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); + goto out; + } + data->adc_conditions = pdata->adc_conditions; + + /* Check the length of array and set num_conditions */ + for (i = 0; data->adc_conditions[i].state; i++) + ; + data->num_conditions = i; + + data->chan = iio_channel_get(dev_name(&pdev->dev), + pdata->consumer_channel); + if (IS_ERR(data->chan)) { + err = PTR_ERR(data->chan); + goto out; + } + + data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); + + INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler); + + platform_set_drvdata(pdev, data); + + err = extcon_dev_register(&data->edev, &pdev->dev); + if (err) + goto out; + + data->irq = platform_get_irq(pdev, 0); + if (!data->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + err = -ENODEV; + goto err_irq; + } + + err = request_any_context_irq(data->irq, adc_jack_irq_thread, + pdata->irq_flags, pdata->name, data); + + if (err) { + dev_err(&pdev->dev, "error: irq %d\n", data->irq); + err = -EINVAL; + goto err_irq; + } + + goto out; + +err_irq: + extcon_dev_unregister(&data->edev); +out: + return err; +} + +static int __devexit adc_jack_remove(struct platform_device *pdev) +{ + struct adc_jack_data *data = platform_get_drvdata(pdev); + + free_irq(data->irq, data); + cancel_work_sync(&data->handler.work); + extcon_dev_unregister(&data->edev); + + return 0; +} + +static struct platform_driver adc_jack_driver = { + .probe = adc_jack_probe, + .remove = __devexit_p(adc_jack_remove), + .driver = { + .name = "adc-jack", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(adc_jack_driver); diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h new file mode 100644 index 00000000000..a1603cfc504 --- /dev/null +++ b/include/linux/extcon/extcon-adc-jack.h @@ -0,0 +1,73 @@ +/* + * include/linux/extcon/extcon-adc-jack.h + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * 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. + * + */ + +#ifndef _EXTCON_ADC_JACK_H_ +#define _EXTCON_ADC_JACK_H_ __FILE__ + +#include +#include + +/** + * struct adc_jack_cond - condition to use an extcon state + * @state - the corresponding extcon state (if 0, this struct denotes + * the last adc_jack_cond element among the array) + * @min_adc - min adc value for this condition + * @max_adc - max adc value for this condition + * + * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means + * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and + * 1 are attached (1<<0 | 1<<1 == 0x3) + * + * Note that you don't need to describe condition for "no cable attached" + * because when no adc_jack_cond is met, state = 0 is automatically chosen. + */ +struct adc_jack_cond { + u32 state; /* extcon state value. 0 if invalid */ + u32 min_adc; + u32 max_adc; +}; + +/** + * struct adc_jack_pdata - platform data for adc jack device. + * @name - name of the extcon device. If null, "adc-jack" is used. + * @consumer_channel - Unique name to identify the channel on the consumer + * side. This typically describes the channels used within + * the consumer. E.g. 'battery_voltage' + * @cable_names - array of cable names ending with null. If the array itself + * if null, extcon standard cable names are chosen. + * @adc_contitions - array of struct adc_jack_cond conditions ending + * with .state = 0 entry. This describes how to decode + * adc values into extcon state. + * @irq_flags - irq flags used for the @irq + * @handling_delay_ms - in some devices, we need to read ADC value some + * milli-seconds after the interrupt occurs. You may + * describe such delays with @handling_delay_ms, which + * is rounded-off by jiffies. + */ +struct adc_jack_pdata { + const char *name; + const char *consumer_channel; + /* + * NULL if standard extcon names are used. + * The last entry should be NULL + */ + const char **cable_names; + /* The last entry's state should be 0 */ + struct adc_jack_cond *adc_conditions; + + unsigned long irq_flags; + unsigned long handling_delay_ms; /* in ms */ +}; + +#endif /* _EXTCON_ADC_JACK_H */ -- cgit v1.2.3-70-g09d2 From 3afebf577d4cb21215abb388e80c46c9c387ed0f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 16 Aug 2012 10:40:32 -0700 Subject: Revert "Extcon: adc_jack: adc-jack driver to support 3.5 pi or simliar devices" This reverts commit 980d7929816236476967218645c30acc022042eb as it breaks the build with the error: ERROR: "extcon_cable_name" [drivers/extcon/extcon-adc-jack.ko] undefined! Cc: Lars-Peter Clausen Cc: anish kumar Cc: MyungJoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 6 - drivers/extcon/Makefile | 1 - drivers/extcon/extcon-adc-jack.c | 195 --------------------------------- include/linux/extcon/extcon-adc-jack.h | 73 ------------ 4 files changed, 275 deletions(-) delete mode 100644 drivers/extcon/extcon-adc-jack.c delete mode 100644 include/linux/extcon/extcon-adc-jack.h (limited to 'include/linux') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index dd5b01b957a..e175c8ed4ec 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -21,12 +21,6 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. -config EXTCON_ADC_JACK - tristate "ADC Jack extcon support" - depends on IIO - help - Say Y here to enable extcon device driver based on ADC values. - config EXTCON_MAX77693 tristate "MAX77693 EXTCON Support" depends on MFD_MAX77693 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 53d88c1cce3..88961b33234 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -4,7 +4,6 @@ obj-$(CONFIG_EXTCON) += extcon_class.o obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o -obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c deleted file mode 100644 index da058f7033e..00000000000 --- a/drivers/extcon/extcon-adc-jack.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * drivers/extcon/extcon-adc-jack.c - * - * Analog Jack extcon driver with ADC-based detection capability. - * - * Copyright (C) 2012 Samsung Electronics - * MyungJoo Ham - * - * Modified for calling to IIO to get adc by - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * struct adc_jack_data - internal data for adc_jack device driver - * @edev - extcon device. - * @cable_names - list of supported cables. - * @num_cables - size of cable_names. - * @adc_conditions - list of adc value conditions. - * @num_conditions - size of adc_conditions. - * @irq - irq number of attach/detach event (0 if not exist). - * @handling_delay - interrupt handler will schedule extcon event - * handling at handling_delay jiffies. - * @handler - extcon event handler called by interrupt handler. - * @chan - iio channel being queried. - */ -struct adc_jack_data { - struct extcon_dev edev; - - const char **cable_names; - int num_cables; - struct adc_jack_cond *adc_conditions; - int num_conditions; - - int irq; - unsigned long handling_delay; /* in jiffies */ - struct delayed_work handler; - - struct iio_channel *chan; -}; - -static void adc_jack_handler(struct work_struct *work) -{ - struct adc_jack_data *data = container_of(to_delayed_work(work), - struct adc_jack_data, - handler); - u32 state = 0; - int ret, adc_val; - int i; - - ret = iio_read_channel_raw(data->chan, &adc_val); - if (ret < 0) { - dev_err(data->edev.dev, "read channel() error: %d\n", ret); - return; - } - - /* Get state from adc value with adc_conditions */ - for (i = 0; i < data->num_conditions; i++) { - struct adc_jack_cond *def = &data->adc_conditions[i]; - if (!def->state) - break; - if (def->min_adc <= adc_val && def->max_adc >= adc_val) { - state = def->state; - break; - } - } - /* if no def has met, it means state = 0 (no cables attached) */ - - extcon_set_state(&data->edev, state); -} - -static irqreturn_t adc_jack_irq_thread(int irq, void *_data) -{ - struct adc_jack_data *data = _data; - - schedule_delayed_work(&data->handler, data->handling_delay); - return IRQ_HANDLED; -} - -static int __devinit adc_jack_probe(struct platform_device *pdev) -{ - struct adc_jack_data *data; - struct adc_jack_pdata *pdata = pdev->dev.platform_data; - int i, err = 0; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->edev.name = pdata->name; - - if (pdata->cable_names) - data->edev.supported_cable = pdata->cable_names; - else - data->edev.supported_cable = extcon_cable_name; - - /* Check the length of array and set num_cables */ - for (i = 0; data->edev.supported_cable[i]; i++) - ; - if (i == 0 || i > SUPPORTED_CABLE_MAX) { - err = -EINVAL; - dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", - i - 1); - goto out; - } - data->num_cables = i; - - if (!pdata->adc_conditions || - !pdata->adc_conditions[0].state) { - err = -EINVAL; - dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); - goto out; - } - data->adc_conditions = pdata->adc_conditions; - - /* Check the length of array and set num_conditions */ - for (i = 0; data->adc_conditions[i].state; i++) - ; - data->num_conditions = i; - - data->chan = iio_channel_get(dev_name(&pdev->dev), - pdata->consumer_channel); - if (IS_ERR(data->chan)) { - err = PTR_ERR(data->chan); - goto out; - } - - data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); - - INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler); - - platform_set_drvdata(pdev, data); - - err = extcon_dev_register(&data->edev, &pdev->dev); - if (err) - goto out; - - data->irq = platform_get_irq(pdev, 0); - if (!data->irq) { - dev_err(&pdev->dev, "platform_get_irq failed\n"); - err = -ENODEV; - goto err_irq; - } - - err = request_any_context_irq(data->irq, adc_jack_irq_thread, - pdata->irq_flags, pdata->name, data); - - if (err) { - dev_err(&pdev->dev, "error: irq %d\n", data->irq); - err = -EINVAL; - goto err_irq; - } - - goto out; - -err_irq: - extcon_dev_unregister(&data->edev); -out: - return err; -} - -static int __devexit adc_jack_remove(struct platform_device *pdev) -{ - struct adc_jack_data *data = platform_get_drvdata(pdev); - - free_irq(data->irq, data); - cancel_work_sync(&data->handler.work); - extcon_dev_unregister(&data->edev); - - return 0; -} - -static struct platform_driver adc_jack_driver = { - .probe = adc_jack_probe, - .remove = __devexit_p(adc_jack_remove), - .driver = { - .name = "adc-jack", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(adc_jack_driver); diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h deleted file mode 100644 index a1603cfc504..00000000000 --- a/include/linux/extcon/extcon-adc-jack.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * include/linux/extcon/extcon-adc-jack.h - * - * Analog Jack extcon driver with ADC-based detection capability. - * - * Copyright (C) 2012 Samsung Electronics - * MyungJoo Ham - * - * 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. - * - */ - -#ifndef _EXTCON_ADC_JACK_H_ -#define _EXTCON_ADC_JACK_H_ __FILE__ - -#include -#include - -/** - * struct adc_jack_cond - condition to use an extcon state - * @state - the corresponding extcon state (if 0, this struct denotes - * the last adc_jack_cond element among the array) - * @min_adc - min adc value for this condition - * @max_adc - max adc value for this condition - * - * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means - * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and - * 1 are attached (1<<0 | 1<<1 == 0x3) - * - * Note that you don't need to describe condition for "no cable attached" - * because when no adc_jack_cond is met, state = 0 is automatically chosen. - */ -struct adc_jack_cond { - u32 state; /* extcon state value. 0 if invalid */ - u32 min_adc; - u32 max_adc; -}; - -/** - * struct adc_jack_pdata - platform data for adc jack device. - * @name - name of the extcon device. If null, "adc-jack" is used. - * @consumer_channel - Unique name to identify the channel on the consumer - * side. This typically describes the channels used within - * the consumer. E.g. 'battery_voltage' - * @cable_names - array of cable names ending with null. If the array itself - * if null, extcon standard cable names are chosen. - * @adc_contitions - array of struct adc_jack_cond conditions ending - * with .state = 0 entry. This describes how to decode - * adc values into extcon state. - * @irq_flags - irq flags used for the @irq - * @handling_delay_ms - in some devices, we need to read ADC value some - * milli-seconds after the interrupt occurs. You may - * describe such delays with @handling_delay_ms, which - * is rounded-off by jiffies. - */ -struct adc_jack_pdata { - const char *name; - const char *consumer_channel; - /* - * NULL if standard extcon names are used. - * The last entry should be NULL - */ - const char **cable_names; - /* The last entry's state should be 0 */ - struct adc_jack_cond *adc_conditions; - - unsigned long irq_flags; - unsigned long handling_delay_ms; /* in ms */ -}; - -#endif /* _EXTCON_ADC_JACK_H */ -- cgit v1.2.3-70-g09d2 From 1f2b79599ee8f5fc82cc73c6c090eb6cdff881d6 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 4 Aug 2012 12:01:21 +0800 Subject: firmware loader: always let firmware_buf own the pages buffer This patch always let firmware_buf own the pages buffer allocated inside firmware_data_write, and add all instances of firmware_buf into the firmware cache global list. Also introduce one private field in 'struct firmware', so release_firmware will see the instance of firmware_buf associated with the current firmware instance, then just 'free' the instance of firmware_buf. The firmware_buf instance represents one pages buffer for one firmware image, so lots of firmware loading requests can share the same firmware_buf instance if they request the same firmware image file. This patch will make implementation of the following cache_firmware/ uncache_firmware very easy and simple. In fact, the patch improves request_formware/release_firmware: - only request userspace to write firmware image once if several devices share one same firmware image and its drivers call request_firmware concurrently. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 240 ++++++++++++++++++++++++++++++------------ include/linux/firmware.h | 3 + 2 files changed, 174 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 5f2076e5d5b..848ad97e8d7 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -21,6 +21,7 @@ #include #include #include +#include MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); @@ -85,13 +86,17 @@ static inline long firmware_loading_timeout(void) return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; } -/* fw_lock could be moved to 'struct firmware_priv' but since it is just - * guarding for corner cases a global lock should be OK */ -static DEFINE_MUTEX(fw_lock); +struct firmware_cache { + /* firmware_buf instance will be added into the below list */ + spinlock_t lock; + struct list_head head; +}; struct firmware_buf { + struct kref ref; + struct list_head list; struct completion completion; - struct firmware *fw; + struct firmware_cache *fwc; unsigned long status; void *data; size_t size; @@ -106,8 +111,94 @@ struct firmware_priv { bool nowait; struct device dev; struct firmware_buf *buf; + struct firmware *fw; }; +#define to_fwbuf(d) container_of(d, struct firmware_buf, ref) + +/* fw_lock could be moved to 'struct firmware_priv' but since it is just + * guarding for corner cases a global lock should be OK */ +static DEFINE_MUTEX(fw_lock); + +static struct firmware_cache fw_cache; + +static struct firmware_buf *__allocate_fw_buf(const char *fw_name, + struct firmware_cache *fwc) +{ + struct firmware_buf *buf; + + buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC); + + if (!buf) + return buf; + + kref_init(&buf->ref); + strcpy(buf->fw_id, fw_name); + buf->fwc = fwc; + init_completion(&buf->completion); + + pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf); + + return buf; +} + +static int fw_lookup_and_allocate_buf(const char *fw_name, + struct firmware_cache *fwc, + struct firmware_buf **buf) +{ + struct firmware_buf *tmp; + + spin_lock(&fwc->lock); + list_for_each_entry(tmp, &fwc->head, list) + if (!strcmp(tmp->fw_id, fw_name)) { + kref_get(&tmp->ref); + spin_unlock(&fwc->lock); + *buf = tmp; + return 1; + } + + tmp = __allocate_fw_buf(fw_name, fwc); + if (tmp) + list_add(&tmp->list, &fwc->head); + spin_unlock(&fwc->lock); + + *buf = tmp; + + return tmp ? 0 : -ENOMEM; +} + +static void __fw_free_buf(struct kref *ref) +{ + struct firmware_buf *buf = to_fwbuf(ref); + struct firmware_cache *fwc = buf->fwc; + int i; + + pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", + __func__, buf->fw_id, buf, buf->data, + (unsigned int)buf->size); + + spin_lock(&fwc->lock); + list_del(&buf->list); + spin_unlock(&fwc->lock); + + vunmap(buf->data); + for (i = 0; i < buf->nr_pages; i++) + __free_page(buf->pages[i]); + kfree(buf->pages); + kfree(buf); +} + +static void fw_free_buf(struct firmware_buf *buf) +{ + kref_put(&buf->ref, __fw_free_buf); +} + +static void __init fw_cache_init(void) +{ + spin_lock_init(&fw_cache.lock); + INIT_LIST_HEAD(&fw_cache.head); +} + static struct firmware_priv *to_firmware_priv(struct device *dev) { return container_of(dev, struct firmware_priv, dev); @@ -118,7 +209,7 @@ static void fw_load_abort(struct firmware_priv *fw_priv) struct firmware_buf *buf = fw_priv->buf; set_bit(FW_STATUS_ABORT, &buf->status); - complete(&buf->completion); + complete_all(&buf->completion); } static ssize_t firmware_timeout_show(struct class *class, @@ -158,18 +249,6 @@ static struct class_attribute firmware_class_attrs[] = { __ATTR_NULL }; -static void fw_free_buf(struct firmware_buf *buf) -{ - int i; - - if (!buf) - return; - - for (i = 0; i < buf->nr_pages; i++) - __free_page(buf->pages[i]); - kfree(buf->pages); -} - static void fw_dev_release(struct device *dev) { struct firmware_priv *fw_priv = to_firmware_priv(dev); @@ -212,13 +291,8 @@ static ssize_t firmware_loading_show(struct device *dev, /* firmware holds the ownership of pages */ static void firmware_free_data(const struct firmware *fw) { - int i; - vunmap(fw->data); - if (fw->pages) { - for (i = 0; i < PFN_UP(fw->size); i++) - __free_page(fw->pages[i]); - kfree(fw->pages); - } + WARN_ON(!fw->priv); + fw_free_buf(fw->priv); } /* Some architectures don't have PAGE_KERNEL_RO */ @@ -269,7 +343,7 @@ static ssize_t firmware_loading_store(struct device *dev, if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) { set_bit(FW_STATUS_DONE, &fw_buf->status); clear_bit(FW_STATUS_LOADING, &fw_buf->status); - complete(&fw_buf->completion); + complete_all(&fw_buf->completion); break; } /* fallthrough */ @@ -448,7 +522,6 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, struct device *device, bool uevent, bool nowait) { struct firmware_priv *fw_priv; - struct firmware_buf *buf; struct device *f_dev; fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); @@ -458,21 +531,10 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, goto exit; } - buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1, GFP_KERNEL); - if (!buf) { - dev_err(device, "%s: kmalloc failed\n", __func__); - kfree(fw_priv); - fw_priv = ERR_PTR(-ENOMEM); - goto exit; - } - - buf->fw = firmware; - fw_priv->buf = buf; fw_priv->nowait = nowait; + fw_priv->fw = firmware; setup_timer(&fw_priv->timeout, firmware_class_timeout, (u_long) fw_priv); - strcpy(buf->fw_id, fw_name); - init_completion(&buf->completion); f_dev = &fw_priv->dev; @@ -484,12 +546,42 @@ exit: return fw_priv; } +/* one pages buffer is mapped/unmapped only once */ +static int fw_map_pages_buf(struct firmware_buf *buf) +{ + buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); + if (!buf->data) + return -ENOMEM; + return 0; +} + +/* store the pages buffer info firmware from buf */ +static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw) +{ + fw->priv = buf; + fw->pages = buf->pages; + fw->size = buf->size; + fw->data = buf->data; + + pr_debug("%s: fw-%s buf=%p data=%p size=%u\n", + __func__, buf->fw_id, buf, buf->data, + (unsigned int)buf->size); +} + +static void _request_firmware_cleanup(const struct firmware **firmware_p) +{ + release_firmware(*firmware_p); + *firmware_p = NULL; +} + static struct firmware_priv * _request_firmware_prepare(const struct firmware **firmware_p, const char *name, struct device *device, bool uevent, bool nowait) { struct firmware *firmware; - struct firmware_priv *fw_priv; + struct firmware_priv *fw_priv = NULL; + struct firmware_buf *buf; + int ret; if (!firmware_p) return ERR_PTR(-EINVAL); @@ -506,35 +598,45 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name, return NULL; } - fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); - if (IS_ERR(fw_priv)) { - release_firmware(firmware); + ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); + if (!ret) + fw_priv = fw_create_instance(firmware, name, device, + uevent, nowait); + + if (IS_ERR(fw_priv) || ret < 0) { + kfree(firmware); *firmware_p = NULL; + return ERR_PTR(-ENOMEM); + } else if (fw_priv) { + fw_priv->buf = buf; + + /* + * bind with 'buf' now to avoid warning in failure path + * of requesting firmware. + */ + firmware->priv = buf; + return fw_priv; } - return fw_priv; -} - -static void _request_firmware_cleanup(const struct firmware **firmware_p) -{ - release_firmware(*firmware_p); - *firmware_p = NULL; -} -/* transfer the ownership of pages to firmware */ -static int fw_set_page_data(struct firmware_buf *buf) -{ - struct firmware *fw = buf->fw; - - buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO); - if (!buf->data) - return -ENOMEM; - - fw->data = buf->data; - fw->pages = buf->pages; - fw->size = buf->size; - WARN_ON(PFN_UP(fw->size) != buf->nr_pages); + /* share the cached buf, which is inprogessing or completed */ + check_status: + mutex_lock(&fw_lock); + if (test_bit(FW_STATUS_ABORT, &buf->status)) { + fw_priv = ERR_PTR(-ENOENT); + _request_firmware_cleanup(firmware_p); + goto exit; + } else if (test_bit(FW_STATUS_DONE, &buf->status)) { + fw_priv = NULL; + fw_set_page_data(buf, firmware); + goto exit; + } + mutex_unlock(&fw_lock); + wait_for_completion(&buf->completion); + goto check_status; - return 0; +exit: + mutex_unlock(&fw_lock); + return fw_priv; } static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, @@ -585,13 +687,12 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status)) retval = -ENOENT; - /* transfer pages ownership at the last minute */ if (!retval) - retval = fw_set_page_data(buf); - if (retval) - fw_free_buf(buf); /* free untransfered pages buffer */ + retval = fw_map_pages_buf(buf); + + /* pass the pages buffer to driver at the last minute */ + fw_set_page_data(buf, fw_priv->fw); - kfree(buf); fw_priv->buf = NULL; mutex_unlock(&fw_lock); @@ -753,6 +854,7 @@ request_firmware_nowait( static int __init firmware_class_init(void) { + fw_cache_init(); return class_register(&firmware_class); } diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 1e7c01189fa..e85b771f3d8 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -12,6 +12,9 @@ struct firmware { size_t size; const u8 *data; struct page **pages; + + /* firmware loader private fields */ + void *priv; }; struct module; -- cgit v1.2.3-70-g09d2 From 2887b3959c8b2f6ed1f62ce95c0888aedb1ea84b Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 4 Aug 2012 12:01:22 +0800 Subject: firmware loader: introduce cache_firmware and uncache_firmware This patches introduce two kernel APIs of cache_firmware and uncache_firmware, both of which take the firmware file name as the only parameter. So any drivers can call cache_firmware to cache the specified firmware file into kernel memory, and can use the cached firmware in situations which can't request firmware from user space. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 100 ++++++++++++++++++++++++++++++++++++++---- include/linux/firmware.h | 12 +++++ 2 files changed, 104 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 848ad97e8d7..fc119ce6fdb 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -142,6 +142,17 @@ static struct firmware_buf *__allocate_fw_buf(const char *fw_name, return buf; } +static struct firmware_buf *__fw_lookup_buf(const char *fw_name) +{ + struct firmware_buf *tmp; + struct firmware_cache *fwc = &fw_cache; + + list_for_each_entry(tmp, &fwc->head, list) + if (!strcmp(tmp->fw_id, fw_name)) + return tmp; + return NULL; +} + static int fw_lookup_and_allocate_buf(const char *fw_name, struct firmware_cache *fwc, struct firmware_buf **buf) @@ -149,14 +160,13 @@ static int fw_lookup_and_allocate_buf(const char *fw_name, struct firmware_buf *tmp; spin_lock(&fwc->lock); - list_for_each_entry(tmp, &fwc->head, list) - if (!strcmp(tmp->fw_id, fw_name)) { - kref_get(&tmp->ref); - spin_unlock(&fwc->lock); - *buf = tmp; - return 1; - } - + tmp = __fw_lookup_buf(fw_name); + if (tmp) { + kref_get(&tmp->ref); + spin_unlock(&fwc->lock); + *buf = tmp; + return 1; + } tmp = __allocate_fw_buf(fw_name, fwc); if (tmp) list_add(&tmp->list, &fwc->head); @@ -167,6 +177,18 @@ static int fw_lookup_and_allocate_buf(const char *fw_name, return tmp ? 0 : -ENOMEM; } +static struct firmware_buf *fw_lookup_buf(const char *fw_name) +{ + struct firmware_buf *tmp; + struct firmware_cache *fwc = &fw_cache; + + spin_lock(&fwc->lock); + tmp = __fw_lookup_buf(fw_name); + spin_unlock(&fwc->lock); + + return tmp; +} + static void __fw_free_buf(struct kref *ref) { struct firmware_buf *buf = to_fwbuf(ref); @@ -852,6 +874,66 @@ request_firmware_nowait( return 0; } +/** + * cache_firmware - cache one firmware image in kernel memory space + * @fw_name: the firmware image name + * + * Cache firmware in kernel memory so that drivers can use it when + * system isn't ready for them to request firmware image from userspace. + * Once it returns successfully, driver can use request_firmware or its + * nowait version to get the cached firmware without any interacting + * with userspace + * + * Return 0 if the firmware image has been cached successfully + * Return !0 otherwise + * + */ +int cache_firmware(const char *fw_name) +{ + int ret; + const struct firmware *fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + ret = request_firmware(&fw, fw_name, NULL); + if (!ret) + kfree(fw); + + pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret); + + return ret; +} + +/** + * uncache_firmware - remove one cached firmware image + * @fw_name: the firmware image name + * + * Uncache one firmware image which has been cached successfully + * before. + * + * Return 0 if the firmware cache has been removed successfully + * Return !0 otherwise + * + */ +int uncache_firmware(const char *fw_name) +{ + struct firmware_buf *buf; + struct firmware fw; + + pr_debug("%s: %s\n", __func__, fw_name); + + if (fw_get_builtin_firmware(&fw, fw_name)) + return 0; + + buf = fw_lookup_buf(fw_name); + if (buf) { + fw_free_buf(buf); + return 0; + } + + return -EINVAL; +} + static int __init firmware_class_init(void) { fw_cache_init(); @@ -869,3 +951,5 @@ module_exit(firmware_class_exit); EXPORT_SYMBOL(release_firmware); EXPORT_SYMBOL(request_firmware); EXPORT_SYMBOL(request_firmware_nowait); +EXPORT_SYMBOL_GPL(cache_firmware); +EXPORT_SYMBOL_GPL(uncache_firmware); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index e85b771f3d8..e4279fedb93 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -47,6 +47,8 @@ int request_firmware_nowait( void (*cont)(const struct firmware *fw, void *context)); void release_firmware(const struct firmware *fw); +int cache_firmware(const char *name); +int uncache_firmware(const char *name); #else static inline int request_firmware(const struct firmware **fw, const char *name, @@ -65,6 +67,16 @@ static inline int request_firmware_nowait( static inline void release_firmware(const struct firmware *fw) { } + +static inline int cache_firmware(const char *name) +{ + return -ENOENT; +} + +static inline int uncache_firmware(const char *name) +{ + return -EINVAL; +} #endif #endif -- cgit v1.2.3-70-g09d2 From bddb1b9078505bb0e430c87c5015c0963f7a594f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 4 Aug 2012 12:01:26 +0800 Subject: driver core: devres: introduce devres_for_each_res This patch introduces one devres API of devres_for_each_res so that the device's driver can iterate each resource it has interest in. The firmware loader will use the API to get each firmware name from the device instance. Signed-off-by: Ming Lei Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/base/devres.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 4 ++++ 2 files changed, 46 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 2360adb7a58..8731979d668 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -143,6 +143,48 @@ void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp) EXPORT_SYMBOL_GPL(devres_alloc); #endif +/** + * devres_for_each_res - Resource iterator + * @dev: Device to iterate resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * @fn: Function to be called for each matched resource. + * @data: Data for @fn, the 3rd parameter of @fn + * + * Call @fn for each devres of @dev which is associated with @release + * and for which @match returns 1. + * + * RETURNS: + * void + */ +void devres_for_each_res(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data, + void (*fn)(struct device *, void *, void *), + void *data) +{ + struct devres_node *node; + struct devres_node *tmp; + unsigned long flags; + + if (!fn) + return; + + spin_lock_irqsave(&dev->devres_lock, flags); + list_for_each_entry_safe_reverse(node, tmp, + &dev->devres_head, entry) { + struct devres *dr = container_of(node, struct devres, node); + + if (node->release != release) + continue; + if (match && !match(dev, dr->data, match_data)) + continue; + fn(dev, dr->data, data); + } + spin_unlock_irqrestore(&dev->devres_lock, flags); +} +EXPORT_SYMBOL_GPL(devres_for_each_res); + /** * devres_free - Free device resource data * @res: Pointer to devres data to free diff --git a/include/linux/device.h b/include/linux/device.h index 52a5f15a222..ecd90066372 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -536,6 +536,10 @@ extern void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, #else extern void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp); #endif +extern void devres_for_each_res(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data, + void (*fn)(struct device *, void *, void *), + void *data); extern void devres_free(void *res); extern void devres_add(struct device *dev, void *res); extern void *devres_find(struct device *dev, dr_release_t release, -- cgit v1.2.3-70-g09d2 From 9b5957803cb444a99275355eb2309b6fecc63c5f Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Mon, 13 Aug 2012 10:06:51 -0700 Subject: Drivers: hv: Add KVP definitions for IP address injection Add the necessary definitions for supporting the IP injection functionality. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Olaf Hering Reviewed-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_util.c | 4 +-- include/linux/hyperv.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++-- tools/hv/hv_kvp_daemon.c | 2 +- 3 files changed, 77 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index d3ac6a40118..a0667de7a04 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -263,7 +263,7 @@ static int util_probe(struct hv_device *dev, (struct hv_util_service *)dev_id->driver_data; int ret; - srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + srv->recv_buffer = kmalloc(PAGE_SIZE * 2, GFP_KERNEL); if (!srv->recv_buffer) return -ENOMEM; if (srv->util_init) { @@ -274,7 +274,7 @@ static int util_probe(struct hv_device *dev, } } - ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0, + ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, srv->util_cb, dev->channel); if (ret) goto error; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 68ed7f7e1fc..11afc4e0849 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -122,12 +122,53 @@ #define REG_U32 4 #define REG_U64 8 +/* + * As we look at expanding the KVP functionality to include + * IP injection functionality, we need to maintain binary + * compatibility with older daemons. + * + * The KVP opcodes are defined by the host and it was unfortunate + * that I chose to treat the registration operation as part of the + * KVP operations defined by the host. + * Here is the level of compatibility + * (between the user level daemon and the kernel KVP driver) that we + * will implement: + * + * An older daemon will always be supported on a newer driver. + * A given user level daemon will require a minimal version of the + * kernel driver. + * If we cannot handle the version differences, we will fail gracefully + * (this can happen when we have a user level daemon that is more + * advanced than the KVP driver. + * + * We will use values used in this handshake for determining if we have + * workable user level daemon and the kernel driver. We begin by taking the + * registration opcode out of the KVP opcode namespace. We will however, + * maintain compatibility with the existing user-level daemon code. + */ + +/* + * Daemon code not supporting IP injection (legacy daemon). + */ + +#define KVP_OP_REGISTER 4 + +/* + * Daemon code supporting IP injection. + * The KVP opcode field is used to communicate the + * registration information; so define a namespace that + * will be distinct from the host defined KVP opcode. + */ + +#define KVP_OP_REGISTER1 100 + enum hv_kvp_exchg_op { KVP_OP_GET = 0, KVP_OP_SET, KVP_OP_DELETE, KVP_OP_ENUMERATE, - KVP_OP_REGISTER, + KVP_OP_GET_IP_INFO, + KVP_OP_SET_IP_INFO, KVP_OP_COUNT /* Number of operations, must be last. */ }; @@ -140,6 +181,26 @@ enum hv_kvp_exchg_pool { KVP_POOL_COUNT /* Number of pools, must be last. */ }; +#define ADDR_FAMILY_NONE 0x00 +#define ADDR_FAMILY_IPV4 0x01 +#define ADDR_FAMILY_IPV6 0x02 + +#define MAX_ADAPTER_ID_SIZE 128 +#define MAX_IP_ADDR_SIZE 1024 +#define MAX_GATEWAY_SIZE 512 + + +struct hv_kvp_ipaddr_value { + __u16 adapter_id[MAX_ADAPTER_ID_SIZE]; + __u8 addr_family; + __u8 dhcp_enabled; + __u16 ip_addr[MAX_IP_ADDR_SIZE]; + __u16 sub_net[MAX_IP_ADDR_SIZE]; + __u16 gate_way[MAX_GATEWAY_SIZE]; + __u16 dns_addr[MAX_IP_ADDR_SIZE]; +} __attribute__((packed)); + + struct hv_kvp_hdr { __u8 operation; __u8 pool; @@ -181,16 +242,26 @@ struct hv_kvp_register { }; struct hv_kvp_msg { - struct hv_kvp_hdr kvp_hdr; + union { + struct hv_kvp_hdr kvp_hdr; + int error; + }; union { struct hv_kvp_msg_get kvp_get; struct hv_kvp_msg_set kvp_set; struct hv_kvp_msg_delete kvp_delete; struct hv_kvp_msg_enumerate kvp_enum_data; + struct hv_kvp_ipaddr_value kvp_ip_val; struct hv_kvp_register kvp_register; } body; } __attribute__((packed)); +struct hv_kvp_ip_msg { + __u8 operation; + __u8 pool; + struct hv_kvp_ipaddr_value kvp_ip_val; +} __attribute__((packed)); + #ifdef __KERNEL__ #include #include @@ -982,6 +1053,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); #define HV_S_CONT 0x80070103 #define HV_ERROR_NOT_SUPPORTED 0x80070032 #define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F /* * While we want to handle util services as regular devices, diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index d9834b36294..8fbcf7b3c69 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -69,7 +69,7 @@ enum key_index { }; static char kvp_send_buffer[4096]; -static char kvp_recv_buffer[4096]; +static char kvp_recv_buffer[4096 * 2]; static struct sockaddr_nl addr; static char *os_name = ""; -- cgit v1.2.3-70-g09d2 From b47a81dcc5a806efb6d970608299129771588289 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Mon, 13 Aug 2012 10:06:52 -0700 Subject: Drivers: hv: kvp: Cleanup error handling in KVP In preparation to implementing IP injection, cleanup the way we propagate and handle errors both in the driver as well as in the user level daemon. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Olaf Hering Reviewed-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 114 +++++++++++++++++++++++++++++++++++++---------- include/linux/hyperv.h | 17 ++++--- tools/hv/hv_kvp_daemon.c | 66 +++++++++++++-------------- 3 files changed, 135 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0012eed6d87..eb4d0730f44 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -48,13 +48,24 @@ static struct { void *kvp_context; /* for the channel callback */ } kvp_transaction; +/* + * Before we can accept KVP messages from the host, we need + * to handshake with the user level daemon. This state tracks + * if we are in the handshake phase. + */ +static bool in_hand_shake = true; + +/* + * This state maintains the version number registered by the daemon. + */ +static int dm_reg_value; + static void kvp_send_key(struct work_struct *dummy); -#define TIMEOUT_FIRED 1 static void kvp_respond_to_host(char *key, char *value, int error); static void kvp_work_func(struct work_struct *dummy); -static void kvp_register(void); +static void kvp_register(int); static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func); static DECLARE_WORK(kvp_sendkey_work, kvp_send_key); @@ -68,7 +79,7 @@ static u8 *recv_buffer; */ static void -kvp_register(void) +kvp_register(int reg_value) { struct cn_msg *msg; @@ -83,7 +94,7 @@ kvp_register(void) msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; - kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER; + kvp_msg->kvp_hdr.operation = reg_value; strcpy(version, HV_DRV_VERSION); msg->len = sizeof(struct hv_kvp_msg); cn_netlink_send(msg, 0, GFP_ATOMIC); @@ -97,9 +108,43 @@ kvp_work_func(struct work_struct *dummy) * If the timer fires, the user-mode component has not responded; * process the pending transaction. */ - kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED); + kvp_respond_to_host("Unknown key", "Guest timed out", HV_E_FAIL); +} + +static int kvp_handle_handshake(struct hv_kvp_msg *msg) +{ + int ret = 1; + + switch (msg->kvp_hdr.operation) { + case KVP_OP_REGISTER: + dm_reg_value = KVP_OP_REGISTER; + pr_info("KVP: IP injection functionality not available\n"); + pr_info("KVP: Upgrade the KVP daemon\n"); + break; + case KVP_OP_REGISTER1: + dm_reg_value = KVP_OP_REGISTER1; + break; + default: + pr_info("KVP: incompatible daemon\n"); + pr_info("KVP: KVP version: %d, Daemon version: %d\n", + KVP_OP_REGISTER1, msg->kvp_hdr.operation); + ret = 0; + } + + if (ret) { + /* + * We have a compatible daemon; complete the handshake. + */ + pr_info("KVP: user-mode registering done.\n"); + kvp_register(dm_reg_value); + kvp_transaction.active = false; + if (kvp_transaction.kvp_context) + hv_kvp_onchannelcallback(kvp_transaction.kvp_context); + } + return ret; } + /* * Callback when data is received from user mode. */ @@ -109,27 +154,52 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct hv_kvp_msg *message; struct hv_kvp_msg_enumerate *data; + int error = 0; message = (struct hv_kvp_msg *)msg->data; - switch (message->kvp_hdr.operation) { + + /* + * If we are negotiating the version information + * with the daemon; handle that first. + */ + + if (in_hand_shake) { + if (kvp_handle_handshake(message)) + in_hand_shake = false; + return; + } + + /* + * Based on the version of the daemon, we propagate errors from the + * daemon differently. + */ + + data = &message->body.kvp_enum_data; + + switch (dm_reg_value) { case KVP_OP_REGISTER: - pr_info("KVP: user-mode registering done.\n"); - kvp_register(); - kvp_transaction.active = false; - hv_kvp_onchannelcallback(kvp_transaction.kvp_context); + /* + * Null string is used to pass back error condition. + */ + if (data->data.key[0] == 0) + error = HV_S_CONT; break; - default: - data = &message->body.kvp_enum_data; + case KVP_OP_REGISTER1: /* - * Complete the transaction by forwarding the key value - * to the host. But first, cancel the timeout. + * We use the message header information from + * the user level daemon to transmit errors. */ - if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(data->data.key, - data->data.value, - !strlen(data->data.key)); + error = message->error; + break; } + + /* + * Complete the transaction by forwarding the key value + * to the host. But first, cancel the timeout. + */ + if (cancel_delayed_work_sync(&kvp_work)) + kvp_respond_to_host(data->data.key, data->data.value, error); } static void @@ -287,6 +357,7 @@ kvp_respond_to_host(char *key, char *value, int error) */ return; + icmsghdrp->status = error; /* * If the error parameter is set, terminate the host's enumeration @@ -294,15 +365,12 @@ kvp_respond_to_host(char *key, char *value, int error) */ if (error) { /* - * Something failed or the we have timedout; - * terminate the current host-side iteration. + * Something failed or we have timedout; + * terminate the current host-side iteration. */ - icmsghdrp->status = HV_S_CONT; goto response_done; } - icmsghdrp->status = HV_S_OK; - kvp_msg = (struct hv_kvp_msg *) &recv_buffer[sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 11afc4e0849..b587c448e8a 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -181,6 +181,17 @@ enum hv_kvp_exchg_pool { KVP_POOL_COUNT /* Number of pools, must be last. */ }; +/* + * Some Hyper-V status codes. + */ + +#define HV_S_OK 0x00000000 +#define HV_E_FAIL 0x80004005 +#define HV_S_CONT 0x80070103 +#define HV_ERROR_NOT_SUPPORTED 0x80070032 +#define HV_ERROR_MACHINE_LOCKED 0x800704F7 +#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F + #define ADDR_FAMILY_NONE 0x00 #define ADDR_FAMILY_IPV4 0x01 #define ADDR_FAMILY_IPV6 0x02 @@ -1048,12 +1059,6 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); #define ICMSGHDRFLAG_REQUEST 2 #define ICMSGHDRFLAG_RESPONSE 4 -#define HV_S_OK 0x00000000 -#define HV_E_FAIL 0x80004005 -#define HV_S_CONT 0x80070103 -#define HV_ERROR_NOT_SUPPORTED 0x80070032 -#define HV_ERROR_MACHINE_LOCKED 0x800704F7 -#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F /* * While we want to handle util services as regular devices, diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 8fbcf7b3c69..069e2b38dec 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -71,13 +71,14 @@ enum key_index { static char kvp_send_buffer[4096]; static char kvp_recv_buffer[4096 * 2]; static struct sockaddr_nl addr; +static int in_hand_shake = 1; static char *os_name = ""; static char *os_major = ""; static char *os_minor = ""; static char *processor_arch; static char *os_build; -static char *lic_version; +static char *lic_version = "Unknown version"; static struct utsname uts_buf; @@ -394,7 +395,7 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, return 1; } -static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, +static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, __u8 *value, int value_size) { struct kvp_record *record; @@ -406,16 +407,12 @@ static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, record = kvp_file_info[pool].records; if (index >= kvp_file_info[pool].num_records) { - /* - * This is an invalid index; terminate enumeration; - * - a NULL value will do the trick. - */ - strcpy(value, ""); - return; + return 1; } memcpy(key, record[index].key, key_size); memcpy(value, record[index].value, value_size); + return 0; } @@ -646,6 +643,8 @@ int main(void) char *p; char *key_value; char *key_name; + int op; + int pool; daemon(1, 0); openlog("KVP", 0, LOG_USER); @@ -687,7 +686,7 @@ int main(void) message->id.val = CN_KVP_VAL; hv_msg = (struct hv_kvp_msg *)message->data; - hv_msg->kvp_hdr.operation = KVP_OP_REGISTER; + hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1; message->ack = 0; message->len = sizeof(struct hv_kvp_msg); @@ -721,12 +720,21 @@ int main(void) incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; - switch (hv_msg->kvp_hdr.operation) { - case KVP_OP_REGISTER: + /* + * We will use the KVP header information to pass back + * the error from this daemon. So, first copy the state + * and set the error code to success. + */ + op = hv_msg->kvp_hdr.operation; + pool = hv_msg->kvp_hdr.pool; + hv_msg->error = HV_S_OK; + + if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) { /* * Driver is registering with us; stash away the version * information. */ + in_hand_shake = 0; p = (char *)hv_msg->body.kvp_register.version; lic_version = malloc(strlen(p) + 1); if (lic_version) { @@ -737,44 +745,39 @@ int main(void) syslog(LOG_ERR, "malloc failed"); } continue; + } - /* - * The current protocol with the kernel component uses a - * NULL key name to pass an error condition. - * For the SET, GET and DELETE operations, - * use the existing protocol to pass back error. - */ - + switch (op) { case KVP_OP_SET: - if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool, + if (kvp_key_add_or_modify(pool, hv_msg->body.kvp_set.data.key, hv_msg->body.kvp_set.data.key_size, hv_msg->body.kvp_set.data.value, hv_msg->body.kvp_set.data.value_size)) - strcpy(hv_msg->body.kvp_set.data.key, ""); + hv_msg->error = HV_S_CONT; break; case KVP_OP_GET: - if (kvp_get_value(hv_msg->kvp_hdr.pool, + if (kvp_get_value(pool, hv_msg->body.kvp_set.data.key, hv_msg->body.kvp_set.data.key_size, hv_msg->body.kvp_set.data.value, hv_msg->body.kvp_set.data.value_size)) - strcpy(hv_msg->body.kvp_set.data.key, ""); + hv_msg->error = HV_S_CONT; break; case KVP_OP_DELETE: - if (kvp_key_delete(hv_msg->kvp_hdr.pool, + if (kvp_key_delete(pool, hv_msg->body.kvp_delete.key, hv_msg->body.kvp_delete.key_size)) - strcpy(hv_msg->body.kvp_delete.key, ""); + hv_msg->error = HV_S_CONT; break; default: break; } - if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) + if (op != KVP_OP_ENUMERATE) goto kvp_done; /* @@ -782,13 +785,14 @@ int main(void) * both the key and the value; if not read from the * appropriate pool. */ - if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) { - kvp_pool_enumerate(hv_msg->kvp_hdr.pool, + if (pool != KVP_POOL_AUTO) { + if (kvp_pool_enumerate(pool, hv_msg->body.kvp_enum_data.index, hv_msg->body.kvp_enum_data.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE, hv_msg->body.kvp_enum_data.data.value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + hv_msg->error = HV_S_CONT; goto kvp_done; } @@ -841,11 +845,7 @@ int main(void) strcpy(key_name, "ProcessorArchitecture"); break; default: - strcpy(key_value, "Unknown Key"); - /* - * We use a null key name to terminate enumeration. - */ - strcpy(key_name, ""); + hv_msg->error = HV_S_CONT; break; } /* -- cgit v1.2.3-70-g09d2 From 1508d8111f0e965ebe06c32dc4d176750eb53c3a Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 16 Aug 2012 08:23:20 -0700 Subject: Drivers: hv: Explicitly size elements of protocol structures Use explicitly sized types in data structures defining the host/guest protocol. Reported-by: Juan Sanchez-Agrelo Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b587c448e8a..7585d5533e4 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -487,7 +487,7 @@ struct vmtransfer_page_range { struct vmtransfer_page_packet_header { struct vmpacket_descriptor d; u16 xfer_pageset_id; - bool sender_owns_set; + u8 sender_owns_set; u8 reserved; u32 range_cnt; struct vmtransfer_page_range ranges[1]; @@ -641,7 +641,7 @@ struct vmbus_channel_query_vmbus_version { /* VMBus Version Supported parameters */ struct vmbus_channel_version_supported { struct vmbus_channel_message_header header; - bool version_supported; + u8 version_supported; } __packed; /* Offer Channel parameters */ @@ -650,7 +650,7 @@ struct vmbus_channel_offer_channel { struct vmbus_channel_offer offer; u32 child_relid; u8 monitorid; - bool monitor_allocated; + u8 monitor_allocated; } __packed; /* Rescind Offer parameters */ @@ -786,7 +786,7 @@ struct vmbus_channel_initiate_contact { struct vmbus_channel_version_response { struct vmbus_channel_message_header header; - bool version_supported; + u8 version_supported; } __packed; enum vmbus_channel_state { -- cgit v1.2.3-70-g09d2 From dfe3212e0196c01402154971841463d721dea915 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 17 Aug 2012 22:06:59 +0800 Subject: PM / Sleep: introduce dpm_for_each_dev dpm_list and its pm lock provide a good way to iterate all devices in system. Except this way, there is no other easy way to iterate devices in system. firmware loader need to cache firmware images for devices before system sleep, so introduce the function to meet its demand. Reported-by: Fengguang Wu Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 22 ++++++++++++++++++++++ include/linux/pm.h | 5 +++++ 2 files changed, 27 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 0113adc310d..b0b072a88f5 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1324,3 +1324,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) return async_error; } EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); + +/** + * dpm_for_each_dev - device iterator. + * @data: data for the callback. + * @fn: function to be called for each device. + * + * Iterate over devices in dpm_list, and call @fn for each device, + * passing it @data. + */ +void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ + struct device *dev; + + if (!fn) + return; + + device_pm_lock(); + list_for_each_entry(dev, &dpm_list, power.entry) + fn(dev, data); + device_pm_unlock(); +} +EXPORT_SYMBOL_GPL(dpm_for_each_dev); diff --git a/include/linux/pm.h b/include/linux/pm.h index f067e60a383..88f034a23f2 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -638,6 +638,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret); } while (0) extern int device_pm_wait_for_dev(struct device *sub, struct device *dev); +extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)); extern int pm_generic_prepare(struct device *dev); extern int pm_generic_suspend_late(struct device *dev); @@ -677,6 +678,10 @@ static inline int device_pm_wait_for_dev(struct device *a, struct device *b) return 0; } +static inline void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) +{ +} + #define pm_generic_prepare NULL #define pm_generic_suspend NULL #define pm_generic_resume NULL -- cgit v1.2.3-70-g09d2 From c338bb0380b1cd64ae566d1ea7d1bfd6c811297d Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Thu, 23 Aug 2012 09:11:54 +0900 Subject: extcon: fixing typos Signed-off-by: Peter Meerwald Signed-off-by: Myungjoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/extcon-class.c | 8 ++++---- include/linux/extcon.h | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 481cfa0f211..946a3188b2b 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -443,7 +443,7 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val, /** * extcon_register_interest() - Register a notifier for a state change of a - * specific cable, not a entier set of cables of a + * specific cable, not an entier set of cables of a * extcon device. * @obj: an empty extcon_specific_cable_nb object to be returned. * @extcon_name: the name of extcon device. @@ -499,7 +499,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj) } /** - * extcon_register_notifier() - Register a notifee to get notified by + * extcon_register_notifier() - Register a notifiee to get notified by * any attach status changes from the extcon. * @edev: the extcon device. * @nb: a notifier block to be registered. @@ -516,7 +516,7 @@ int extcon_register_notifier(struct extcon_dev *edev, EXPORT_SYMBOL_GPL(extcon_register_notifier); /** - * extcon_unregister_notifier() - Unregister a notifee from the extcon device. + * extcon_unregister_notifier() - Unregister a notifiee from the extcon device. * @edev: the extcon device. * @nb: a registered notifier block to be unregistered. */ @@ -806,7 +806,7 @@ EXPORT_SYMBOL_GPL(extcon_dev_register); /** * extcon_dev_unregister() - Unregister the extcon device. - * @edev: the extcon device instance to be unregitered. + * @edev: the extcon device instance to be unregistered. * * Note that this does not call kfree(edev) because edev was not allocated * by this class. diff --git a/include/linux/extcon.h b/include/linux/extcon.h index cdd40147765..7443a560c9d 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -30,19 +30,19 @@ /* * The standard cable name is to help support general notifier - * and notifee device drivers to share the common names. + * and notifiee device drivers to share the common names. * Please use standard cable names unless your notifier device has * a very unique and abnormal cable or * the cable type is supposed to be used with only one unique - * pair of notifier/notifee devices. + * pair of notifier/notifiee devices. * * Please add any other "standard" cables used with extcon dev. * * You may add a dot and number to specify version or specification * of the specific cable if it is required. (e.g., "Fast-charger.18" * and "Fast-charger.10" for 1.8A and 1.0A chargers) - * However, the notifee and notifier should be able to handle such - * string and if the notifee can negotiate the protocol or idenify, + * However, the notifiee and notifier should be able to handle such + * string and if the notifiee can negotiate the protocol or identify, * you don't need such convention. This convention is helpful when * notifier can distinguish but notifiee cannot. */ @@ -76,7 +76,7 @@ struct extcon_cable; * struct extcon_dev - An extcon device represents one external connector. * @name The name of this extcon device. Parent device name is used * if NULL. - * @supported_cable Array of supported cable name ending with NULL. + * @supported_cable Array of supported cable names ending with NULL. * If supported_cable is NULL, cable name related APIs * are disabled. * @mutually_exclusive Array of mutually exclusive set of cables that cannot @@ -95,7 +95,7 @@ struct extcon_cable; * @state Attach/detach state of this extcon. Do not provide at * register-time * @nh Notifier for the state change events from this extcon - * @entry To support list of extcon devices so that uses can search + * @entry To support list of extcon devices so that users can search * for extcon devices based on the extcon name. * @lock * @max_supported Internal value to store the number of cables. @@ -199,7 +199,7 @@ extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state); /* * get/set_cable_state access each bit of the 32b encoded state value. * They are used to access the status of each cable based on the cable_name - * or cable_index, which is retrived by extcon_find_cable_index + * or cable_index, which is retrieved by extcon_find_cable_index */ extern int extcon_find_cable_index(struct extcon_dev *sdev, const char *cable_name); @@ -226,9 +226,9 @@ extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb); /* * Following APIs are to monitor every action of a notifier. - * Registerer gets notified for every external port of a connection device. + * Registrar gets notified for every external port of a connection device. * Probably this could be used to debug an action of notifier; however, - * we do not recommend to use this at normal 'notifiee' device drivers who + * we do not recommend to use this for normal 'notifiee' device drivers who * want to be notified by a specific external port of the notifier. */ extern int extcon_register_notifier(struct extcon_dev *edev, -- cgit v1.2.3-70-g09d2 From 19939860dcae5a3b2e11318eb7d65b4db2e55e2b Mon Sep 17 00:00:00 2001 From: anish kumar Date: Fri, 17 Aug 2012 09:57:22 -0700 Subject: extcon: adc_jack: adc-jack driver to support 3.5 pi or simliar devices External connector devices that decides connection information based on ADC values may use adc-jack device driver. The user simply needs to provide a table of adc range and connection states. Then, extcon framework will automatically notify others. Changes in V1: added Lars-Peter Clausen suggested changes: Using macros to get rid of boiler plate code such as devm_kzalloc and module_platform_driver.Other changes suggested are related to coding guidelines. Changes in V2: Removed some unnecessary checks and changed the way we are un-regitering extcon and freeing the irq while removing. Changes in V3: Renamed the files to comply with extcon naming. Changes in V4: Added the cancel_work_sync during removing of driver. Changes in V5: Added the dependency of IIO in Kconfig. Changes in V6: Some nitpicks related to naming. Changes in this version: V6 patch version patch broke the build: ERROR: "extcon_cable_name" [drivers/extcon/extcon-adc-jack.ko] undefined! Fixed it in this version. Acked-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen Signed-off-by: anish kumar Signed-off-by: MyungJoo Ham Signed-off-by: Greg Kroah-Hartman --- drivers/extcon/Kconfig | 6 + drivers/extcon/Makefile | 1 + drivers/extcon/extcon-adc-jack.c | 198 +++++++++++++++++++++++++++++++++ include/linux/extcon/extcon-adc-jack.h | 71 ++++++++++++ 4 files changed, 276 insertions(+) create mode 100644 drivers/extcon/extcon-adc-jack.c create mode 100644 include/linux/extcon/extcon-adc-jack.h (limited to 'include/linux') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index e175c8ed4ec..dd5b01b957a 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -21,6 +21,12 @@ config EXTCON_GPIO Say Y here to enable GPIO based extcon support. Note that GPIO extcon supports single state per extcon instance. +config EXTCON_ADC_JACK + tristate "ADC Jack extcon support" + depends on IIO + help + Say Y here to enable extcon device driver based on ADC values. + config EXTCON_MAX77693 tristate "MAX77693 EXTCON Support" depends on MFD_MAX77693 diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 9c0682daefb..f98a3c4d46e 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_EXTCON) += extcon-class.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o +obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c new file mode 100644 index 00000000000..60ac3fbb4cd --- /dev/null +++ b/drivers/extcon/extcon-adc-jack.c @@ -0,0 +1,198 @@ +/* + * drivers/extcon/extcon-adc-jack.c + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * Modified for calling to IIO to get adc by + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct adc_jack_data - internal data for adc_jack device driver + * @edev - extcon device. + * @cable_names - list of supported cables. + * @num_cables - size of cable_names. + * @adc_conditions - list of adc value conditions. + * @num_conditions - size of adc_conditions. + * @irq - irq number of attach/detach event (0 if not exist). + * @handling_delay - interrupt handler will schedule extcon event + * handling at handling_delay jiffies. + * @handler - extcon event handler called by interrupt handler. + * @chan - iio channel being queried. + */ +struct adc_jack_data { + struct extcon_dev edev; + + const char **cable_names; + int num_cables; + struct adc_jack_cond *adc_conditions; + int num_conditions; + + int irq; + unsigned long handling_delay; /* in jiffies */ + struct delayed_work handler; + + struct iio_channel *chan; +}; + +static void adc_jack_handler(struct work_struct *work) +{ + struct adc_jack_data *data = container_of(to_delayed_work(work), + struct adc_jack_data, + handler); + u32 state = 0; + int ret, adc_val; + int i; + + ret = iio_read_channel_raw(data->chan, &adc_val); + if (ret < 0) { + dev_err(data->edev.dev, "read channel() error: %d\n", ret); + return; + } + + /* Get state from adc value with adc_conditions */ + for (i = 0; i < data->num_conditions; i++) { + struct adc_jack_cond *def = &data->adc_conditions[i]; + if (!def->state) + break; + if (def->min_adc <= adc_val && def->max_adc >= adc_val) { + state = def->state; + break; + } + } + /* if no def has met, it means state = 0 (no cables attached) */ + + extcon_set_state(&data->edev, state); +} + +static irqreturn_t adc_jack_irq_thread(int irq, void *_data) +{ + struct adc_jack_data *data = _data; + + schedule_delayed_work(&data->handler, data->handling_delay); + return IRQ_HANDLED; +} + +static int __devinit adc_jack_probe(struct platform_device *pdev) +{ + struct adc_jack_data *data; + struct adc_jack_pdata *pdata = pdev->dev.platform_data; + int i, err = 0; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->edev.name = pdata->name; + + if (!pdata->cable_names) { + err = -EINVAL; + dev_err(&pdev->dev, "error: cable_names not defined.\n"); + goto out; + } + + data->edev.supported_cable = pdata->cable_names; + + /* Check the length of array and set num_cables */ + for (i = 0; data->edev.supported_cable[i]; i++) + ; + if (i == 0 || i > SUPPORTED_CABLE_MAX) { + err = -EINVAL; + dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n", + i - 1); + goto out; + } + data->num_cables = i; + + if (!pdata->adc_conditions || + !pdata->adc_conditions[0].state) { + err = -EINVAL; + dev_err(&pdev->dev, "error: adc_conditions not defined.\n"); + goto out; + } + data->adc_conditions = pdata->adc_conditions; + + /* Check the length of array and set num_conditions */ + for (i = 0; data->adc_conditions[i].state; i++) + ; + data->num_conditions = i; + + data->chan = iio_channel_get(dev_name(&pdev->dev), + pdata->consumer_channel); + if (IS_ERR(data->chan)) { + err = PTR_ERR(data->chan); + goto out; + } + + data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms); + + INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler); + + platform_set_drvdata(pdev, data); + + err = extcon_dev_register(&data->edev, &pdev->dev); + if (err) + goto out; + + data->irq = platform_get_irq(pdev, 0); + if (!data->irq) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + err = -ENODEV; + goto err_irq; + } + + err = request_any_context_irq(data->irq, adc_jack_irq_thread, + pdata->irq_flags, pdata->name, data); + + if (err) { + dev_err(&pdev->dev, "error: irq %d\n", data->irq); + err = -EINVAL; + goto err_irq; + } + + goto out; + +err_irq: + extcon_dev_unregister(&data->edev); +out: + return err; +} + +static int __devexit adc_jack_remove(struct platform_device *pdev) +{ + struct adc_jack_data *data = platform_get_drvdata(pdev); + + free_irq(data->irq, data); + cancel_work_sync(&data->handler.work); + extcon_dev_unregister(&data->edev); + + return 0; +} + +static struct platform_driver adc_jack_driver = { + .probe = adc_jack_probe, + .remove = __devexit_p(adc_jack_remove), + .driver = { + .name = "adc-jack", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(adc_jack_driver); diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h new file mode 100644 index 00000000000..20e9eef25d4 --- /dev/null +++ b/include/linux/extcon/extcon-adc-jack.h @@ -0,0 +1,71 @@ +/* + * include/linux/extcon/extcon-adc-jack.h + * + * Analog Jack extcon driver with ADC-based detection capability. + * + * Copyright (C) 2012 Samsung Electronics + * MyungJoo Ham + * + * 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. + * + */ + +#ifndef _EXTCON_ADC_JACK_H_ +#define _EXTCON_ADC_JACK_H_ __FILE__ + +#include +#include + +/** + * struct adc_jack_cond - condition to use an extcon state + * @state - the corresponding extcon state (if 0, this struct denotes + * the last adc_jack_cond element among the array) + * @min_adc - min adc value for this condition + * @max_adc - max adc value for this condition + * + * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means + * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and + * 1 are attached (1<<0 | 1<<1 == 0x3) + * + * Note that you don't need to describe condition for "no cable attached" + * because when no adc_jack_cond is met, state = 0 is automatically chosen. + */ +struct adc_jack_cond { + u32 state; /* extcon state value. 0 if invalid */ + u32 min_adc; + u32 max_adc; +}; + +/** + * struct adc_jack_pdata - platform data for adc jack device. + * @name - name of the extcon device. If null, "adc-jack" is used. + * @consumer_channel - Unique name to identify the channel on the consumer + * side. This typically describes the channels used within + * the consumer. E.g. 'battery_voltage' + * @cable_names - array of cable names ending with null. + * @adc_contitions - array of struct adc_jack_cond conditions ending + * with .state = 0 entry. This describes how to decode + * adc values into extcon state. + * @irq_flags - irq flags used for the @irq + * @handling_delay_ms - in some devices, we need to read ADC value some + * milli-seconds after the interrupt occurs. You may + * describe such delays with @handling_delay_ms, which + * is rounded-off by jiffies. + */ +struct adc_jack_pdata { + const char *name; + const char *consumer_channel; + /* + * The last entry should be NULL + */ + const char **cable_names; + /* The last entry's state should be 0 */ + struct adc_jack_cond *adc_conditions; + + unsigned long irq_flags; + unsigned long handling_delay_ms; /* in ms */ +}; + +#endif /* _EXTCON_ADC_JACK_H */ -- cgit v1.2.3-70-g09d2 From 32061b4d3830e61975ede409df389804507fd220 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 5 Sep 2012 13:50:13 -0700 Subject: Tools: hv: Implement the KVP verb - KVP_OP_SET_IP_INFO Implement the KVP verb - KVP_OP_SET_IP_INFO. This operation configures the specified interface based on the given configuration. Since configuring an interface is very distro specific, we invoke an external (Distro specific) script to configure the interface. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 2 + tools/hv/hv_kvp_daemon.c | 443 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 445 insertions(+) (limited to 'include/linux') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7585d5533e4..e73b852156b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -191,6 +191,8 @@ enum hv_kvp_exchg_pool { #define HV_ERROR_NOT_SUPPORTED 0x80070032 #define HV_ERROR_MACHINE_LOCKED 0x800704F7 #define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F +#define HV_INVALIDARG 0x80070057 +#define HV_GUID_NOTFOUND 0x80041002 #define ADDR_FAMILY_NONE 0x00 #define ADDR_FAMILY_IPV4 0x01 diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 6fb2c1c6c32..ac144b9da0d 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include /* * KVP protocol: The user mode component first registers with the @@ -68,6 +70,14 @@ enum key_index { ProcessorArchitecture }; + +enum { + IPADDR = 0, + NETMASK, + GATEWAY, + DNS +}; + static char kvp_send_buffer[4096]; static char kvp_recv_buffer[4096 * 2]; static struct sockaddr_nl addr; @@ -81,6 +91,11 @@ static char *os_build; static char *lic_version = "Unknown version"; static struct utsname uts_buf; +/* + * The location of the interface configuration file. + */ + +#define KVP_CONFIG_LOC "/var/opt/" #define MAX_FILE_NAME 100 #define ENTRIES_PER_BLOCK 50 @@ -490,6 +505,104 @@ done: return; } + + +/* + * Retrieve an interface name corresponding to the specified guid. + * If there is a match, the function returns a pointer + * to the interface name and if not, a NULL is returned. + * If a match is found, the caller is responsible for + * freeing the memory. + */ + +static char *kvp_get_if_name(char *guid) +{ + DIR *dir; + struct dirent *entry; + FILE *file; + char *p, *q, *x; + char *if_name = NULL; + char buf[256]; + char *kvp_net_dir = "/sys/class/net/"; + char dev_id[256]; + + dir = opendir(kvp_net_dir); + if (dir == NULL) + return NULL; + + snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir); + q = dev_id + strlen(kvp_net_dir); + + while ((entry = readdir(dir)) != NULL) { + /* + * Set the state for the next pass. + */ + *q = '\0'; + strcat(dev_id, entry->d_name); + strcat(dev_id, "/device/device_id"); + + file = fopen(dev_id, "r"); + if (file == NULL) + continue; + + p = fgets(buf, sizeof(buf), file); + if (p) { + x = strchr(p, '\n'); + if (x) + *x = '\0'; + + if (!strcmp(p, guid)) { + /* + * Found the guid match; return the interface + * name. The caller will free the memory. + */ + if_name = strdup(entry->d_name); + fclose(file); + break; + } + } + fclose(file); + } + + closedir(dir); + return if_name; +} + +/* + * Retrieve the MAC address given the interface name. + */ + +static char *kvp_if_name_to_mac(char *if_name) +{ + FILE *file; + char *p, *x; + char buf[256]; + char addr_file[256]; + int i; + char *mac_addr = NULL; + + snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/", + if_name, "/address"); + + file = fopen(addr_file, "r"); + if (file == NULL) + return NULL; + + p = fgets(buf, sizeof(buf), file); + if (p) { + x = strchr(p, '\n'); + if (x) + *x = '\0'; + for (i = 0; i < strlen(p); i++) + p[i] = toupper(p[i]); + mac_addr = strdup(p); + } + + fclose(file); + return mac_addr; +} + + static void kvp_process_ipconfig_file(char *cmd, char *config_buf, int len, int element_size, int offset) @@ -790,6 +903,315 @@ getaddr_done: } +static int expand_ipv6(char *addr, int type) +{ + int ret; + struct in6_addr v6_addr; + + ret = inet_pton(AF_INET6, addr, &v6_addr); + + if (ret != 1) { + if (type == NETMASK) + return 1; + return 0; + } + + sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x", + (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1], + (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3], + (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5], + (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7], + (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9], + (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11], + (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13], + (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]); + + return 1; + +} + +static int is_ipv4(char *addr) +{ + int ret; + struct in_addr ipv4_addr; + + ret = inet_pton(AF_INET, addr, &ipv4_addr); + + if (ret == 1) + return 1; + return 0; +} + +static int parse_ip_val_buffer(char *in_buf, int *offset, + char *out_buf, int out_len) +{ + char *x; + char *start; + + /* + * in_buf has sequence of characters that are seperated by + * the character ';'. The last sequence does not have the + * terminating ";" character. + */ + start = in_buf + *offset; + + x = strchr(start, ';'); + if (x) + *x = 0; + else + x = start + strlen(start); + + if (strlen(start) != 0) { + int i = 0; + /* + * Get rid of leading spaces. + */ + while (start[i] == ' ') + i++; + + if ((x - start) <= out_len) { + strcpy(out_buf, (start + i)); + *offset += (x - start) + 1; + return 1; + } + } + return 0; +} + +static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3) +{ + int ret; + + ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3); + + if (ret < 0) + return HV_E_FAIL; + + return 0; +} + + +static int process_ip_string(FILE *f, char *ip_string, int type) +{ + int error = 0; + char addr[INET6_ADDRSTRLEN]; + int i = 0; + int j = 0; + char str[256]; + char sub_str[10]; + int offset = 0; + + memset(addr, 0, sizeof(addr)); + + while (parse_ip_val_buffer(ip_string, &offset, addr, + (MAX_IP_ADDR_SIZE * 2))) { + + sub_str[0] = 0; + if (is_ipv4(addr)) { + switch (type) { + case IPADDR: + snprintf(str, sizeof(str), "%s", "IPADDR"); + break; + case NETMASK: + snprintf(str, sizeof(str), "%s", "NETMASK"); + break; + case GATEWAY: + snprintf(str, sizeof(str), "%s", "GATEWAY"); + break; + case DNS: + snprintf(str, sizeof(str), "%s", "DNS"); + break; + } + if (i != 0) { + if (type != DNS) { + snprintf(sub_str, sizeof(sub_str), + "_%d", i++); + } else { + snprintf(sub_str, sizeof(sub_str), + "%d", ++i); + } + } else if (type == DNS) { + snprintf(sub_str, sizeof(sub_str), "%d", ++i); + } + + + } else if (expand_ipv6(addr, type)) { + switch (type) { + case IPADDR: + snprintf(str, sizeof(str), "%s", "IPV6ADDR"); + break; + case NETMASK: + snprintf(str, sizeof(str), "%s", "IPV6NETMASK"); + break; + case GATEWAY: + snprintf(str, sizeof(str), "%s", + "IPV6_DEFAULTGW"); + break; + case DNS: + snprintf(str, sizeof(str), "%s", "DNS"); + break; + } + if ((j != 0) || (type == DNS)) { + if (type != DNS) { + snprintf(sub_str, sizeof(sub_str), + "_%d", j++); + } else { + snprintf(sub_str, sizeof(sub_str), + "%d", ++i); + } + } else if (type == DNS) { + snprintf(sub_str, sizeof(sub_str), + "%d", ++i); + } + } else { + return HV_INVALIDARG; + } + + error = kvp_write_file(f, str, sub_str, addr); + if (error) + return error; + memset(addr, 0, sizeof(addr)); + } + + return 0; +} + +static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) +{ + int error = 0; + char if_file[128]; + FILE *file; + char cmd[512]; + char *mac_addr; + + /* + * Set the configuration for the specified interface with + * the information provided. Since there is no standard + * way to configure an interface, we will have an external + * script that does the job of configuring the interface and + * flushing the configuration. + * + * The parameters passed to this external script are: + * 1. A configuration file that has the specified configuration. + * + * We will embed the name of the interface in the configuration + * file: ifcfg-ethx (where ethx is the interface name). + * + * The information provided here may be more than what is needed + * in a given distro to configure the interface and so are free + * ignore information that may not be relevant. + * + * Here is the format of the ip configuration file: + * + * HWADDR=macaddr + * IF_NAME=interface name + * DHCP=yes (This is optional; if yes, DHCP is configured) + * + * IPADDR=ipaddr1 + * IPADDR_1=ipaddr2 + * IPADDR_x=ipaddry (where y = x + 1) + * + * NETMASK=netmask1 + * NETMASK_x=netmasky (where y = x + 1) + * + * GATEWAY=ipaddr1 + * GATEWAY_x=ipaddry (where y = x + 1) + * + * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) + * + * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be + * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as + * IPV6NETMASK. + * + * The host can specify multiple ipv4 and ipv6 addresses to be + * configured for the interface. Furthermore, the configuration + * needs to be persistent. A subsequent GET call on the interface + * is expected to return the configuration that is set via the SET + * call. + */ + + snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, + "hyperv/ifcfg-", if_name); + + file = fopen(if_file, "w"); + + if (file == NULL) { + syslog(LOG_ERR, "Failed to open config file"); + return HV_E_FAIL; + } + + /* + * First write out the MAC address. + */ + + mac_addr = kvp_if_name_to_mac(if_name); + if (mac_addr == NULL) { + error = HV_E_FAIL; + goto setval_error; + } + + error = kvp_write_file(file, "HWADDR", "", mac_addr); + if (error) + goto setval_error; + + error = kvp_write_file(file, "IF_NAME", "", if_name); + if (error) + goto setval_error; + + if (new_val->dhcp_enabled) { + error = kvp_write_file(file, "DHCP", "", "yes"); + if (error) + goto setval_error; + + /* + * We are done!. + */ + goto setval_done; + } + + /* + * Write the configuration for ipaddress, netmask, gateway and + * name servers. + */ + + error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); + if (error) + goto setval_error; + + error = process_ip_string(file, (char *)new_val->dns_addr, DNS); + if (error) + goto setval_error; + +setval_done: + free(mac_addr); + fclose(file); + + /* + * Now that we have populated the configuration file, + * invoke the external script to do its magic. + */ + + snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file); + system(cmd); + return 0; + +setval_error: + syslog(LOG_ERR, "Failed to write config file"); + free(mac_addr); + fclose(file); + return error; +} + + static int kvp_get_domain_name(char *buffer, int length) { @@ -859,6 +1281,8 @@ int main(void) char *key_name; int op; int pool; + char *if_name; + struct hv_kvp_ipaddr_value *kvp_ip_val; daemon(1, 0); openlog("KVP", 0, LOG_USER); @@ -962,6 +1386,25 @@ int main(void) } switch (op) { + case KVP_OP_SET_IP_INFO: + kvp_ip_val = &hv_msg->body.kvp_ip_val; + if_name = kvp_get_if_name( + (char *)kvp_ip_val->adapter_id); + if (if_name == NULL) { + /* + * We could not map the guid to an + * interface name; return error. + */ + hv_msg->error = HV_GUID_NOTFOUND; + break; + } + error = kvp_set_ip_info(if_name, kvp_ip_val); + if (error) + hv_msg->error = error; + + free(if_name); + break; + case KVP_OP_SET: if (kvp_key_add_or_modify(pool, hv_msg->body.kvp_set.data.key, -- cgit v1.2.3-70-g09d2 From 6f586e663e3b3674cadad0d5329424b006a0a289 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Tue, 4 Sep 2012 06:40:40 +0200 Subject: driver-core: Shut up dev_dbg_reatelimited() without DEBUG dev_dbg_reatelimited() without DEBUG printed "217078 callbacks suppressed". This shouldn't print anything without DEBUG. With CONFIG_DYNAMIC_DEBUG, the print should be configured as expected. Signed-off-by: Hiroshi Doyu Reported-by: Hin-Tak Leung Tested-by: Antti Palosaari Tested-by: Hin-Tak Leung Acked-by: Hin-Tak Leung Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 62 +++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index ecd90066372..378a27c1d1a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -950,6 +950,32 @@ int _dev_info(const struct device *dev, const char *fmt, ...) #endif +/* + * Stupid hackaround for existing uses of non-printk uses dev_info + * + * Note that the definition of dev_info below is actually _dev_info + * and a macro is used to avoid redefining dev_info + */ + +#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg) + +#if defined(CONFIG_DYNAMIC_DEBUG) +#define dev_dbg(dev, format, ...) \ +do { \ + dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ +} while (0) +#elif defined(DEBUG) +#define dev_dbg(dev, format, arg...) \ + dev_printk(KERN_DEBUG, dev, format, ##arg) +#else +#define dev_dbg(dev, format, arg...) \ +({ \ + if (0) \ + dev_printk(KERN_DEBUG, dev, format, ##arg); \ + 0; \ +}) +#endif + #define dev_level_ratelimited(dev_level, dev, fmt, ...) \ do { \ static DEFINE_RATELIMIT_STATE(_rs, \ @@ -973,33 +999,21 @@ do { \ dev_level_ratelimited(dev_notice, dev, fmt, ##__VA_ARGS__) #define dev_info_ratelimited(dev, fmt, ...) \ dev_level_ratelimited(dev_info, dev, fmt, ##__VA_ARGS__) +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) #define dev_dbg_ratelimited(dev, fmt, ...) \ - dev_level_ratelimited(dev_dbg, dev, fmt, ##__VA_ARGS__) - -/* - * Stupid hackaround for existing uses of non-printk uses dev_info - * - * Note that the definition of dev_info below is actually _dev_info - * and a macro is used to avoid redefining dev_info - */ - -#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg) - -#if defined(CONFIG_DYNAMIC_DEBUG) -#define dev_dbg(dev, format, ...) \ -do { \ - dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ +do { \ + static DEFINE_RATELIMIT_STATE(_rs, \ + DEFAULT_RATELIMIT_INTERVAL, \ + DEFAULT_RATELIMIT_BURST); \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) && \ + __ratelimit(&_rs)) \ + __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \ + ##__VA_ARGS__); \ } while (0) -#elif defined(DEBUG) -#define dev_dbg(dev, format, arg...) \ - dev_printk(KERN_DEBUG, dev, format, ##arg) #else -#define dev_dbg(dev, format, arg...) \ -({ \ - if (0) \ - dev_printk(KERN_DEBUG, dev, format, ##arg); \ - 0; \ -}) +#define dev_dbg_ratelimited(dev, fmt, ...) \ + no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #endif #ifdef VERBOSE_DEBUG -- cgit v1.2.3-70-g09d2 From 798efc60e4276825df34af0e91ecbe0781237834 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Sep 2012 20:11:29 -0700 Subject: dev_dbg/dynamic_debug: Update to use printk_emit, optimize stack commit c4e00daaa9 ("driver-core: extend dev_printk() to pass structured data") changed __dev_printk and broke dynamic-debug's ability to control the dynamic prefix of dev_dbg(dev,..). commit af7f2158fd ("drivers-core: make structured logging play nice with dynamic-debug") made a minimal correction. The current dynamic debug code uses up to 3 recursion levels via %pV. This can consume quite a bit of stack. Directly call printk_emit to reduce the recursion depth. These changes include: dev_dbg: o Create and use function create_syslog_header to format the syslog header for printk_emit uses. o Call create_syslog_header and neaten __dev_printk o Make __dev_printk static not global o Remove include header declaration of __dev_printk o Remove now unused EXPORT_SYMBOL() of __dev_printk o Whitespace neatening dynamic_dev_dbg: o Remove KERN_DEBUG from dynamic_emit_prefix o Call create_syslog_header and printk_emit o Whitespace neatening Signed-off-by: Joe Perches Acked-by: David S. Miller Tested-by: Jim Cromie Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 64 +++++++++++++++++++++++++++----------------------- include/linux/device.h | 8 +++---- lib/dynamic_debug.c | 39 ++++++++++++++++++++++-------- 3 files changed, 67 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index 91478bd3541..d325b9dab3f 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1865,26 +1865,19 @@ void device_shutdown(void) */ #ifdef CONFIG_PRINTK -int __dev_printk(const char *level, const struct device *dev, - struct va_format *vaf) +int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) { - char dict[128]; - const char *level_extra = ""; - size_t dictlen = 0; const char *subsys; - - if (!dev) - return printk("%s(NULL device *): %pV", level, vaf); + size_t pos = 0; if (dev->class) subsys = dev->class->name; else if (dev->bus) subsys = dev->bus->name; else - goto skip; + return 0; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "SUBSYSTEM=%s", subsys); + pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys); /* * Add device identifier DEVICE=: @@ -1900,32 +1893,41 @@ int __dev_printk(const char *level, const struct device *dev, c = 'b'; else c = 'c'; - dictlen++; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "DEVICE=%c%u:%u", - c, MAJOR(dev->devt), MINOR(dev->devt)); + pos++; + pos += snprintf(hdr + pos, hdrlen - pos, + "DEVICE=%c%u:%u", + c, MAJOR(dev->devt), MINOR(dev->devt)); } else if (strcmp(subsys, "net") == 0) { struct net_device *net = to_net_dev(dev); - dictlen++; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "DEVICE=n%u", net->ifindex); + pos++; + pos += snprintf(hdr + pos, hdrlen - pos, + "DEVICE=n%u", net->ifindex); } else { - dictlen++; - dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, - "DEVICE=+%s:%s", subsys, dev_name(dev)); + pos++; + pos += snprintf(hdr + pos, hdrlen - pos, + "DEVICE=+%s:%s", subsys, dev_name(dev)); } -skip: - if (level[2]) - level_extra = &level[2]; /* skip past KERN_SOH "L" */ - return printk_emit(0, level[1] - '0', - dictlen ? dict : NULL, dictlen, - "%s %s: %s%pV", - dev_driver_string(dev), dev_name(dev), - level_extra, vaf); + return pos; +} +EXPORT_SYMBOL(create_syslog_header); + +static int __dev_printk(const char *level, const struct device *dev, + struct va_format *vaf) +{ + char hdr[128]; + size_t hdrlen; + + if (!dev) + return printk("%s(NULL device *): %pV", level, vaf); + + hdrlen = create_syslog_header(dev, hdr, sizeof(hdr)); + + return printk_emit(0, level[1] - '0', hdrlen ? hdr : NULL, hdrlen, + "%s %s: %pV", + dev_driver_string(dev), dev_name(dev), vaf); } -EXPORT_SYMBOL(__dev_printk); int dev_printk(const char *level, const struct device *dev, const char *fmt, ...) @@ -1940,6 +1942,7 @@ int dev_printk(const char *level, const struct device *dev, vaf.va = &args; r = __dev_printk(level, dev, &vaf); + va_end(args); return r; @@ -1959,6 +1962,7 @@ int func(const struct device *dev, const char *fmt, ...) \ vaf.va = &args; \ \ r = __dev_printk(kern_level, dev, &vaf); \ + \ va_end(args); \ \ return r; \ diff --git a/include/linux/device.h b/include/linux/device.h index 378a27c1d1a..a1443b48ed4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -895,12 +895,12 @@ extern const char *dev_driver_string(const struct device *dev); #ifdef CONFIG_PRINTK -extern int __dev_printk(const char *level, const struct device *dev, - struct va_format *vaf); +extern int create_syslog_header(const struct device *dev, + char *hdr, size_t hdrlen); + extern __printf(3, 4) int dev_printk(const char *level, const struct device *dev, - const char *fmt, ...) - ; + const char *fmt, ...); extern __printf(2, 3) int dev_emerg(const struct device *dev, const char *fmt, ...); extern __printf(2, 3) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 7ca29a0a301..29ff2e4cfb7 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -521,25 +521,25 @@ static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf) int pos_after_tid; int pos = 0; - pos += snprintf(buf + pos, remaining(pos), "%s", KERN_DEBUG); + *buf = '\0'; + if (desc->flags & _DPRINTK_FLAGS_INCL_TID) { if (in_interrupt()) - pos += snprintf(buf + pos, remaining(pos), "%s ", - ""); + pos += snprintf(buf + pos, remaining(pos), " "); else pos += snprintf(buf + pos, remaining(pos), "[%d] ", - task_pid_vnr(current)); + task_pid_vnr(current)); } pos_after_tid = pos; if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", - desc->modname); + desc->modname); if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME) pos += snprintf(buf + pos, remaining(pos), "%s:", - desc->function); + desc->function); if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO) pos += snprintf(buf + pos, remaining(pos), "%d:", - desc->lineno); + desc->lineno); if (pos - pos_after_tid) pos += snprintf(buf + pos, remaining(pos), " "); if (pos >= PREFIX_SIZE) @@ -559,9 +559,13 @@ int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) BUG_ON(!fmt); va_start(args, fmt); + vaf.fmt = fmt; vaf.va = &args; - res = printk("%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf); + + res = printk(KERN_DEBUG "%s%pV", + dynamic_emit_prefix(descriptor, buf), &vaf); + va_end(args); return res; @@ -574,15 +578,30 @@ int __dynamic_dev_dbg(struct _ddebug *descriptor, struct va_format vaf; va_list args; int res; - char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); + vaf.fmt = fmt; vaf.va = &args; - res = __dev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf); + + if (!dev) { + res = printk(KERN_DEBUG "(NULL device *): %pV", &vaf); + } else { + char buf[PREFIX_SIZE]; + char dict[128]; + size_t dictlen; + + dictlen = create_syslog_header(dev, dict, sizeof(dict)); + + res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen, + "%s%s %s: %pV", + dynamic_emit_prefix(descriptor, buf), + dev_driver_string(dev), dev_name(dev), &vaf); + } + va_end(args); return res; -- cgit v1.2.3-70-g09d2 From b004ff4972e2a42aa4512c90cc6a9e4dc1bb36b6 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Sep 2012 20:12:19 -0700 Subject: netdev_printk/dynamic_netdev_dbg: Directly call printk_emit A lot of stack is used in recursive printks with %pV. Using multiple levels of %pV (a logging function with %pV that calls another logging function with %pV) can consume more stack than necessary. Avoid excessive stack use by not calling dev_printk from netdev_printk and dynamic_netdev_dbg. Duplicate the logic and form of dev_printk instead. Make __netdev_printk static. Remove EXPORT_SYMBOL(__netdev_printk) Whitespace and brace style neatening. Signed-off-by: Joe Perches Acked-by: David S. Miller Tested-by: Jim Cromie Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/netdevice.h | 3 --- lib/dynamic_debug.c | 26 +++++++++++++++++++++++--- net/core/dev.c | 24 +++++++++++++++++------- 3 files changed, 40 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 59dc05f3824..5f49cc0a107 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2720,9 +2720,6 @@ static inline const char *netdev_name(const struct net_device *dev) return dev->name; } -extern int __netdev_printk(const char *level, const struct net_device *dev, - struct va_format *vaf); - extern __printf(3, 4) int netdev_printk(const char *level, const struct net_device *dev, const char *format, ...); diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 29ff2e4cfb7..2a29f4e04bd 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -611,20 +611,40 @@ EXPORT_SYMBOL(__dynamic_dev_dbg); #ifdef CONFIG_NET int __dynamic_netdev_dbg(struct _ddebug *descriptor, - const struct net_device *dev, const char *fmt, ...) + const struct net_device *dev, const char *fmt, ...) { struct va_format vaf; va_list args; int res; - char buf[PREFIX_SIZE]; BUG_ON(!descriptor); BUG_ON(!fmt); va_start(args, fmt); + vaf.fmt = fmt; vaf.va = &args; - res = __netdev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf); + + if (dev && dev->dev.parent) { + char buf[PREFIX_SIZE]; + char dict[128]; + size_t dictlen; + + dictlen = create_syslog_header(dev->dev.parent, + dict, sizeof(dict)); + + res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen, + "%s%s %s: %s: %pV", + dynamic_emit_prefix(descriptor, buf), + dev_driver_string(dev->dev.parent), + dev_name(dev->dev.parent), + netdev_name(dev), &vaf); + } else if (dev) { + res = printk(KERN_DEBUG "%s: %pV", netdev_name(dev), &vaf); + } else { + res = printk(KERN_DEBUG "(NULL net_device): %pV", &vaf); + } + va_end(args); return res; diff --git a/net/core/dev.c b/net/core/dev.c index d7fe32c946c..ac890f14613 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6423,22 +6423,30 @@ const char *netdev_drivername(const struct net_device *dev) return empty; } -int __netdev_printk(const char *level, const struct net_device *dev, +static int __netdev_printk(const char *level, const struct net_device *dev, struct va_format *vaf) { int r; - if (dev && dev->dev.parent) - r = dev_printk(level, dev->dev.parent, "%s: %pV", - netdev_name(dev), vaf); - else if (dev) + if (dev && dev->dev.parent) { + char dict[128]; + size_t dictlen = create_syslog_header(dev->dev.parent, + dict, sizeof(dict)); + + r = printk_emit(0, level[1] - '0', + dictlen ? dict : NULL, dictlen, + "%s %s: %s: %pV", + dev_driver_string(dev->dev.parent), + dev_name(dev->dev.parent), + netdev_name(dev), vaf); + } else if (dev) { r = printk("%s%s: %pV", level, netdev_name(dev), vaf); - else + } else { r = printk("%s(NULL net_device): %pV", level, vaf); + } return r; } -EXPORT_SYMBOL(__netdev_printk); int netdev_printk(const char *level, const struct net_device *dev, const char *format, ...) @@ -6453,6 +6461,7 @@ int netdev_printk(const char *level, const struct net_device *dev, vaf.va = &args; r = __netdev_printk(level, dev, &vaf); + va_end(args); return r; @@ -6472,6 +6481,7 @@ int func(const struct net_device *dev, const char *fmt, ...) \ vaf.va = &args; \ \ r = __netdev_printk(level, dev, &vaf); \ + \ va_end(args); \ \ return r; \ -- cgit v1.2.3-70-g09d2 From 05e4e5b87aab74f994acf9dd4bed4a8f1367cd09 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Sep 2012 20:13:37 -0700 Subject: dev: Add dev_vprintk_emit and dev_printk_emit Add utility functions to consolidate the use of create_syslog_header and vprintk_emit. This allows conversion of logging functions that call create_syslog_header and then call vprintk_emit or printk_emit to the dev_ equivalents. Signed-off-by: Joe Perches Acked-by: David S. Miller Tested-by: Jim Cromie Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 27 +++++++++++++++++++++++++++ include/linux/device.h | 11 +++++++++++ 2 files changed, 38 insertions(+) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index d325b9dab3f..dff94c8fb35 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1913,6 +1913,33 @@ int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) } EXPORT_SYMBOL(create_syslog_header); +int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args) +{ + char hdr[128]; + size_t hdrlen; + + hdrlen = create_syslog_header(dev, hdr, sizeof(hdr)); + + return vprintk_emit(0, level, hdrlen ? hdr : NULL, hdrlen, fmt, args); +} +EXPORT_SYMBOL(dev_vprintk_emit); + +int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...) +{ + va_list args; + int r; + + va_start(args, fmt); + + r = dev_vprintk_emit(level, dev, fmt, args); + + va_end(args); + + return r; +} +EXPORT_SYMBOL(dev_printk_emit); + static int __dev_printk(const char *level, const struct device *dev, struct va_format *vaf) { diff --git a/include/linux/device.h b/include/linux/device.h index a1443b48ed4..0d9ba0c0979 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -897,6 +897,10 @@ extern const char *dev_driver_string(const struct device *dev); extern int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen); +extern int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args); +extern __printf(3, 4) +int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...); extern __printf(3, 4) int dev_printk(const char *level, const struct device *dev, @@ -918,6 +922,13 @@ int _dev_info(const struct device *dev, const char *fmt, ...); #else +static int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args) +{ return 0; } +static inline __printf(3, 4) +int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...) +{ return 0; } + static inline int __dev_printk(const char *level, const struct device *dev, struct va_format *vaf) { return 0; } -- cgit v1.2.3-70-g09d2 From 666f355f3805d68b6ed5f7013806f1f65abfbf03 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Sep 2012 20:14:11 -0700 Subject: device and dynamic_debug: Use dev_vprintk_emit and dev_printk_emit Convert direct calls of vprintk_emit and printk_emit to the dev_ equivalents. Make create_syslog_header static. Signed-off-by: Joe Perches Acked-by: David S. Miller Tested-by: Jim Cromie Acked-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 14 +++++--------- include/linux/device.h | 2 -- lib/dynamic_debug.c | 31 +++++++++++-------------------- net/core/dev.c | 16 ++++++---------- 4 files changed, 22 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/core.c b/drivers/base/core.c index dff94c8fb35..abea76c36a4 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1865,7 +1865,8 @@ void device_shutdown(void) */ #ifdef CONFIG_PRINTK -int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) +static int +create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen) { const char *subsys; size_t pos = 0; @@ -1943,17 +1944,12 @@ EXPORT_SYMBOL(dev_printk_emit); static int __dev_printk(const char *level, const struct device *dev, struct va_format *vaf) { - char hdr[128]; - size_t hdrlen; - if (!dev) return printk("%s(NULL device *): %pV", level, vaf); - hdrlen = create_syslog_header(dev, hdr, sizeof(hdr)); - - return printk_emit(0, level[1] - '0', hdrlen ? hdr : NULL, hdrlen, - "%s %s: %pV", - dev_driver_string(dev), dev_name(dev), vaf); + return dev_printk_emit(level[1] - '0', dev, + "%s %s: %pV", + dev_driver_string(dev), dev_name(dev), vaf); } int dev_printk(const char *level, const struct device *dev, diff --git a/include/linux/device.h b/include/linux/device.h index 0d9ba0c0979..6d37e59db57 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -895,8 +895,6 @@ extern const char *dev_driver_string(const struct device *dev); #ifdef CONFIG_PRINTK -extern int create_syslog_header(const struct device *dev, - char *hdr, size_t hdrlen); extern int dev_vprintk_emit(int level, const struct device *dev, const char *fmt, va_list args); extern __printf(3, 4) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 6b3ebabacfa..e7f7d993357 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -591,15 +591,11 @@ int __dynamic_dev_dbg(struct _ddebug *descriptor, res = printk(KERN_DEBUG "(NULL device *): %pV", &vaf); } else { char buf[PREFIX_SIZE]; - char dict[128]; - size_t dictlen; - dictlen = create_syslog_header(dev, dict, sizeof(dict)); - - res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen, - "%s%s %s: %pV", - dynamic_emit_prefix(descriptor, buf), - dev_driver_string(dev), dev_name(dev), &vaf); + res = dev_printk_emit(7, dev, "%s%s %s: %pV", + dynamic_emit_prefix(descriptor, buf), + dev_driver_string(dev), dev_name(dev), + &vaf); } va_end(args); @@ -627,18 +623,13 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor, if (dev && dev->dev.parent) { char buf[PREFIX_SIZE]; - char dict[128]; - size_t dictlen; - - dictlen = create_syslog_header(dev->dev.parent, - dict, sizeof(dict)); - - res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen, - "%s%s %s %s: %pV", - dynamic_emit_prefix(descriptor, buf), - dev_driver_string(dev->dev.parent), - dev_name(dev->dev.parent), - netdev_name(dev), &vaf); + + res = dev_printk_emit(7, dev->dev.parent, + "%s%s %s %s: %pV", + dynamic_emit_prefix(descriptor, buf), + dev_driver_string(dev->dev.parent), + dev_name(dev->dev.parent), + netdev_name(dev), &vaf); } else if (dev) { res = printk(KERN_DEBUG "%s: %pV", netdev_name(dev), &vaf); } else { diff --git a/net/core/dev.c b/net/core/dev.c index cb9d43be07e..76c0fe66fdc 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6429,16 +6429,12 @@ static int __netdev_printk(const char *level, const struct net_device *dev, int r; if (dev && dev->dev.parent) { - char dict[128]; - size_t dictlen = create_syslog_header(dev->dev.parent, - dict, sizeof(dict)); - - r = printk_emit(0, level[1] - '0', - dictlen ? dict : NULL, dictlen, - "%s %s %s: %pV", - dev_driver_string(dev->dev.parent), - dev_name(dev->dev.parent), - netdev_name(dev), vaf); + r = dev_printk_emit(level[1] - '0', + dev->dev.parent, + "%s %s %s: %pV", + dev_driver_string(dev->dev.parent), + dev_name(dev->dev.parent), + netdev_name(dev), vaf); } else if (dev) { r = printk("%s%s: %pV", level, netdev_name(dev), vaf); } else { -- cgit v1.2.3-70-g09d2 From 0a18b05043acc01d1d6a4bac459e62c79628881c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 25 Sep 2012 18:19:57 -0700 Subject: device.h: Add missing inline to #ifndef CONFIG_PRINTK dev_vprintk_emit Also add __printf() verification for format string. Reported-by: Geert Uytterhoeven Signed-off-by: Joe Perches Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index 6d37e59db57..af92883bb4a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -895,8 +895,9 @@ extern const char *dev_driver_string(const struct device *dev); #ifdef CONFIG_PRINTK -extern int dev_vprintk_emit(int level, const struct device *dev, - const char *fmt, va_list args); +extern __printf(3, 0) +int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args); extern __printf(3, 4) int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...); @@ -920,8 +921,9 @@ int _dev_info(const struct device *dev, const char *fmt, ...); #else -static int dev_vprintk_emit(int level, const struct device *dev, - const char *fmt, va_list args) +static inline __printf(3, 0) +int dev_vprintk_emit(int level, const struct device *dev, + const char *fmt, va_list args) { return 0; } static inline __printf(3, 4) int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...) -- cgit v1.2.3-70-g09d2