Commit 1d1fdd95 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'char-misc-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc patches from Greg KH:
 "Here is the big char/misc driver pull request for 3.12-rc1

  Lots of driver updates all over the char/misc tree, full details in
  the shortlog"

* tag 'char-misc-3.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (62 commits)
  drivers: uio: Kconfig: add MMU dependancy for UIO
  drivers: uio: Add driver for Humusoft MF624 DAQ PCI card
  drivers: uio_pdrv_genirq: use dev_get_platdata()
  drivers: uio_pruss: use dev_get_platdata()
  drivers: uio_dmem_genirq: use dev_get_platdata()
  drivers: parport: Kconfig: exclude h8300 for PARPORT_PC
  drivers: misc: ti-st: fix potential race if st_kim_start fails
  Drivers: hv: vmbus: Do not attempt to negoatiate a new version prematurely
  misc: vmw_balloon: Remove braces to fix build for clang.
  Drivers: hv: vmbus: Fix a bug in the handling of channel offers
  vme: vme_ca91cx42.c: fix to pass correct device identity to free_irq()
  VMCI: Add support for virtual IOMMU
  VMCI: Remove non-blocking/pinned queuepair support
  uio: uio_pruss: remove unnecessary platform_set_drvdata()
  parport: amiga: remove unnecessary platform_set_drvdata()
  vme: vme_vmivme7805.c: add missing __iomem annotation
  vme: vme_ca91cx42.c: add missing __iomem annotation
  vme: vme_tsi148.c: add missing __iomem annotation
  drivers/misc/hpilo: Correct panic when an AUX iLO is detected
  uio: drop unused vma_count member in uio_device struct
  ...
parents b3b49114 3cc1f952
EXTCON FOR TWL CHIPS
EXTCON FOR PALMAS/TWL CHIPS
PALMAS USB COMPARATOR
Required Properties:
- compatible : Should be "ti,palmas-usb" or "ti,twl6035-usb"
- vbus-supply : phandle to the regulator device tree node.
Optional Properties:
- ti,wakeup : To enable the wakeup comparator in probe
- ti,enable-id-detection: Perform ID detection.
- ti,enable-vbus-detection: Perform VBUS detection.
palmas-usb {
compatible = "ti,twl6035-usb", "ti,palmas-usb";
vbus-supply = <&smps10_reg>;
ti,wakeup;
};
......@@ -53,6 +53,11 @@ OMAP DWC3 GLUE
It should be set to "1" for HW mode and "2" for SW mode.
- ranges: the child address space are mapped 1:1 onto the parent address space
Optional Properties:
- extcon : phandle for the extcon device omap dwc3 uses to detect
connect/disconnect events.
- vbus-supply : phandle to the regulator device tree node if needed.
Sub-nodes:
The dwc3 core should be added as subnode to omap dwc3 glue.
- dwc3 :
......
......@@ -1182,14 +1182,14 @@ static irqreturn_t mgslpc_isr(int dummy, void *dev_id)
}
count++;
if (gis & (BIT1 + BIT0)) {
if (gis & (BIT1 | BIT0)) {
isr = read_reg16(info, CHB + ISR);
if (isr & IRQ_DCD)
dcd_change(info, tty);
if (isr & IRQ_CTS)
cts_change(info, tty);
}
if (gis & (BIT3 + BIT2))
if (gis & (BIT3 | BIT2))
{
isr = read_reg16(info, CHA + ISR);
if (isr & IRQ_TIMER) {
......@@ -1210,7 +1210,7 @@ static irqreturn_t mgslpc_isr(int dummy, void *dev_id)
if (isr & IRQ_RXTIME) {
issue_command(info, CHA, CMD_RXFIFO_READ);
}
if (isr & (IRQ_RXEOM + IRQ_RXFIFO)) {
if (isr & (IRQ_RXEOM | IRQ_RXFIFO)) {
if (info->params.mode == MGSL_MODE_HDLC)
rx_ready_hdlc(info, isr & IRQ_RXEOM);
else
......@@ -3031,11 +3031,11 @@ static void loopback_enable(MGSLPC_INFO *info)
unsigned char val;
/* CCR1:02..00 CM[2..0] Clock Mode = 111 (clock mode 7) */
val = read_reg(info, CHA + CCR1) | (BIT2 + BIT1 + BIT0);
val = read_reg(info, CHA + CCR1) | (BIT2 | BIT1 | BIT0);
write_reg(info, CHA + CCR1, val);
/* CCR2:04 SSEL Clock source select, 1=submode b */
val = read_reg(info, CHA + CCR2) | (BIT4 + BIT5);
val = read_reg(info, CHA + CCR2) | (BIT4 | BIT5);
write_reg(info, CHA + CCR2, val);
/* set LinkSpeed if available, otherwise default to 2Mbps */
......@@ -3125,10 +3125,10 @@ static void hdlc_mode(MGSLPC_INFO *info)
val |= BIT4;
break; // FM0
case HDLC_ENCODING_BIPHASE_MARK:
val |= BIT4 + BIT2;
val |= BIT4 | BIT2;
break; // FM1
case HDLC_ENCODING_BIPHASE_LEVEL:
val |= BIT4 + BIT3;
val |= BIT4 | BIT3;
break; // Manchester
}
write_reg(info, CHA + CCR0, val);
......@@ -3185,7 +3185,7 @@ static void hdlc_mode(MGSLPC_INFO *info)
*/
val = 0x00;
if (info->params.crc_type == HDLC_CRC_NONE)
val |= BIT2 + BIT1;
val |= BIT2 | BIT1;
if (info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE)
val |= BIT5;
switch (info->params.preamble_length)
......@@ -3197,7 +3197,7 @@ static void hdlc_mode(MGSLPC_INFO *info)
val |= BIT6;
break;
case HDLC_PREAMBLE_LENGTH_64BITS:
val |= BIT7 + BIT6;
val |= BIT7 | BIT6;
break;
}
write_reg(info, CHA + CCR3, val);
......@@ -3264,8 +3264,8 @@ static void hdlc_mode(MGSLPC_INFO *info)
clear_reg_bits(info, CHA + PVR, BIT3);
irq_enable(info, CHA,
IRQ_RXEOM + IRQ_RXFIFO + IRQ_ALLSENT +
IRQ_UNDERRUN + IRQ_TXFIFO);
IRQ_RXEOM | IRQ_RXFIFO | IRQ_ALLSENT |
IRQ_UNDERRUN | IRQ_TXFIFO);
issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
wait_command_complete(info, CHA);
read_reg16(info, CHA + ISR); /* clear pending IRQs */
......@@ -3582,8 +3582,8 @@ static void async_mode(MGSLPC_INFO *info)
} else
clear_reg_bits(info, CHA + PVR, BIT3);
irq_enable(info, CHA,
IRQ_RXEOM + IRQ_RXFIFO + IRQ_BREAK_ON + IRQ_RXTIME +
IRQ_ALLSENT + IRQ_TXFIFO);
IRQ_RXEOM | IRQ_RXFIFO | IRQ_BREAK_ON | IRQ_RXTIME |
IRQ_ALLSENT | IRQ_TXFIFO);
issue_command(info, CHA, CMD_TXRESET + CMD_RXRESET);
wait_command_complete(info, CHA);
read_reg16(info, CHA + ISR); /* clear pending IRQs */
......
......@@ -14,6 +14,10 @@ if EXTCON
comment "Extcon Device Drivers"
config OF_EXTCON
def_tristate y
depends on OF
config EXTCON_GPIO
tristate "GPIO extcon support"
depends on GPIOLIB
......
......@@ -2,6 +2,8 @@
# Makefile for external connector class (extcon) devices
#
obj-$(CONFIG_OF_EXTCON) += of_extcon.o
obj-$(CONFIG_EXTCON) += extcon-class.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
......
......@@ -87,7 +87,8 @@ 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);
queue_delayed_work(system_power_efficient_wq,
&data->handler, data->handling_delay);
return IRQ_HANDLED;
}
......
......@@ -890,7 +890,8 @@ static void arizona_micd_detect(struct work_struct *work)
handled:
if (info->detecting)
schedule_delayed_work(&info->micd_timeout_work,
queue_delayed_work(system_power_efficient_wq,
&info->micd_timeout_work,
msecs_to_jiffies(info->micd_timeout));
pm_runtime_mark_last_busy(info->dev);
......@@ -912,7 +913,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
mutex_unlock(&info->lock);
if (debounce)
schedule_delayed_work(&info->micd_detect_work,
queue_delayed_work(system_power_efficient_wq,
&info->micd_detect_work,
msecs_to_jiffies(debounce));
else
arizona_micd_detect(&info->micd_detect_work.work);
......@@ -967,11 +969,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
if (val == info->last_jackdet) {
dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
if (cancelled_hp)
schedule_delayed_work(&info->hpdet_work,
queue_delayed_work(system_power_efficient_wq,
&info->hpdet_work,
msecs_to_jiffies(HPDET_DEBOUNCE));
if (cancelled_mic)
schedule_delayed_work(&info->micd_timeout_work,
queue_delayed_work(system_power_efficient_wq,
&info->micd_timeout_work,
msecs_to_jiffies(info->micd_timeout));
goto out;
......@@ -994,7 +998,8 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
arizona_start_mic(info);
} else {
schedule_delayed_work(&info->hpdet_work,
queue_delayed_work(system_power_efficient_wq,
&info->hpdet_work,
msecs_to_jiffies(HPDET_DEBOUNCE));
}
......
......@@ -602,7 +602,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
edev->dev->class = extcon_class;
edev->dev->release = extcon_dev_release;
dev_set_name(edev->dev, "%s", edev->name ? edev->name : dev_name(dev));
edev->name = edev->name ? edev->name : dev_name(dev);
dev_set_name(edev->dev, "%s", edev->name);
if (edev->max_supported) {
char buf[10];
......
......@@ -56,7 +56,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
struct gpio_extcon_data *extcon_data = dev_id;
schedule_delayed_work(&extcon_data->work,
queue_delayed_work(system_power_efficient_wq, &extcon_data->work,
extcon_data->debounce_jiffies);
return IRQ_HANDLED;
}
......
......@@ -57,6 +57,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
extcon_set_cable_state(&palmas_usb->edev, "USB", true);
dev_info(palmas_usb->dev, "USB cable is attached\n");
} else {
dev_dbg(palmas_usb->dev,
"Spurious connect event detected\n");
......@@ -65,6 +66,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB", false);
dev_info(palmas_usb->dev, "USB cable is detached\n");
} else {
dev_dbg(palmas_usb->dev,
"Spurious disconnect event detected\n");
......@@ -83,29 +85,24 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_SET, &set);
if (set & PALMAS_USB_ID_INT_SRC_ID_GND) {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_SET,
PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
} else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_SET,
PALMAS_USB_ID_INT_EN_HI_SET_ID_GND);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
}
return IRQ_HANDLED;
......@@ -122,13 +119,17 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb)
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_EN_HI_SET,
PALMAS_USB_ID_INT_EN_HI_SET_ID_GND);
PALMAS_USB_ID_INT_EN_HI_SET_ID_GND |
PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT);
if (palmas_usb->enable_vbus_detection)
palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb);
/* cold plug for host mode needs this delay */
if (palmas_usb->enable_id_detection) {
msleep(30);
palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb);
}
}
static int palmas_usb_probe(struct platform_device *pdev)
......@@ -139,21 +140,25 @@ static int palmas_usb_probe(struct platform_device *pdev)
struct palmas_usb *palmas_usb;
int status;
if (node && !pdata) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->wakeup = of_property_read_bool(node, "ti,wakeup");
} else if (!pdata) {
return -EINVAL;
}
palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
if (!palmas_usb)
return -ENOMEM;
if (node && !pdata) {
palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup");
palmas_usb->enable_id_detection = of_property_read_bool(node,
"ti,enable-id-detection");
palmas_usb->enable_vbus_detection = of_property_read_bool(node,
"ti,enable-vbus-detection");
} else {
palmas_usb->wakeup = true;
palmas_usb->enable_id_detection = true;
palmas_usb->enable_vbus_detection = true;
if (pdata)
palmas_usb->wakeup = pdata->wakeup;
}
palmas->usb = palmas_usb;
palmas_usb->palmas = palmas;
......@@ -168,11 +173,10 @@ static int palmas_usb_probe(struct platform_device *pdev)
palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data,
PALMAS_VBUS_IRQ);
palmas_usb_wakeup(palmas, pdata->wakeup);
palmas_usb_wakeup(palmas, palmas_usb->wakeup);
platform_set_drvdata(pdev, palmas_usb);
palmas_usb->edev.name = "palmas-usb";
palmas_usb->edev.supported_cable = palmas_extcon_cable;
palmas_usb->edev.mutually_exclusive = mutually_exclusive;
......@@ -182,28 +186,36 @@ static int palmas_usb_probe(struct platform_device *pdev)
return status;
}
status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->id_irq,
if (palmas_usb->enable_id_detection) {
status = devm_request_threaded_irq(palmas_usb->dev,
palmas_usb->id_irq,
NULL, palmas_id_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT | IRQF_EARLY_RESUME,
"palmas_usb_id", palmas_usb);
if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->id_irq, status);
goto fail_extcon;
}
}
if (palmas_usb->enable_vbus_detection) {
status = devm_request_threaded_irq(palmas_usb->dev,
palmas_usb->vbus_irq, NULL, palmas_vbus_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
palmas_usb->vbus_irq, NULL,
palmas_vbus_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
IRQF_ONESHOT | IRQF_EARLY_RESUME,
"palmas_usb_vbus", palmas_usb);
if (status < 0) {
dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
palmas_usb->vbus_irq, status);
goto fail_extcon;
}
}
palmas_enable_irq(palmas_usb);
device_set_wakeup_capable(&pdev->dev, true);
return 0;
fail_extcon:
......@@ -221,6 +233,39 @@ static int palmas_usb_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int palmas_usb_suspend(struct device *dev)
{
struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
if (device_may_wakeup(dev)) {
if (palmas_usb->enable_vbus_detection)
enable_irq_wake(palmas_usb->vbus_irq);
if (palmas_usb->enable_id_detection)
enable_irq_wake(palmas_usb->id_irq);
}
return 0;
}
static int palmas_usb_resume(struct device *dev)
{
struct palmas_usb *palmas_usb = dev_get_drvdata(dev);
if (device_may_wakeup(dev)) {
if (palmas_usb->enable_vbus_detection)
disable_irq_wake(palmas_usb->vbus_irq);
if (palmas_usb->enable_id_detection)
disable_irq_wake(palmas_usb->id_irq);
}
return 0;
};
#endif
static const struct dev_pm_ops palmas_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(palmas_usb_suspend,
palmas_usb_resume)
};
static struct of_device_id of_palmas_match_tbl[] = {
{ .compatible = "ti,palmas-usb", },
{ .compatible = "ti,twl6035-usb", },
......@@ -234,6 +279,7 @@ static struct platform_driver palmas_usb_driver = {
.name = "palmas-usb",
.of_match_table = of_palmas_match_tbl,
.owner = THIS_MODULE,
.pm = &palmas_pm_ops,
},
};
......
/*
* OF helpers for External connector (extcon) framework
*
* Copyright (C) 2013 Texas Instruments, Inc.
* Kishon Vijay Abraham I <kishon@ti.com>
*
* Copyright (C) 2013 Samsung Electronics
* Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/extcon/of_extcon.h>
/*
* of_extcon_get_extcon_dev - Get the name of extcon device from devicetree
* @dev - instance to the given device
* @index - index into list of extcon_dev
*
* return the instance of extcon device
*/
struct extcon_dev *of_extcon_get_extcon_dev(struct device *dev, int index)
{
struct device_node *node;
struct extcon_dev *edev;
struct platform_device *extcon_parent_dev;
if (!dev->of_node) {
dev_dbg(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL);
}
node = of_parse_phandle(dev->of_node, "extcon", index);
if (!node) {
dev_dbg(dev, "failed to get phandle in %s node\n",
dev->of_node->full_name);
return ERR_PTR(-ENODEV);
}
extcon_parent_dev = of_find_device_by_node(node);
if (!extcon_parent_dev) {
dev_dbg(dev, "unable to find device by node\n");
return ERR_PTR(-EPROBE_DEFER);
}
edev = extcon_get_extcon_dev(dev_name(&extcon_parent_dev->dev));
if (!edev) {
dev_dbg(dev, "unable to get extcon device : %s\n",
dev_name(&extcon_parent_dev->dev));
return ERR_PTR(-ENODEV);
}
return edev;
}
EXPORT_SYMBOL_GPL(of_extcon_get_extcon_dev);
......@@ -143,18 +143,17 @@ static int fc_probe(struct fmc_device *fmc)
fc->misc.fops = &fc_fops;
fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
spin_lock(&fc_lock);
ret = misc_register(&fc->misc);
if (ret < 0)
goto err_unlock;
goto out;
spin_lock(&fc_lock);
list_add(&fc->list, &fc_devices);
spin_unlock(&fc_lock);
dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
fc->misc.name);
return 0;
err_unlock:
spin_unlock(&fc_lock);
out:
kfree(fc->misc.name);
kfree(fc);
return ret;
......@@ -174,10 +173,10 @@ static int fc_remove(struct fmc_device *fmc)
spin_lock(&fc_lock);
list_del(&fc->list);
spin_unlock(&fc_lock);
misc_deregister(&fc->misc);
kfree(fc->misc.name);
kfree(fc);
spin_unlock(&fc_lock);
return 0;
}
......
......@@ -103,7 +103,7 @@ static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s)
* difficult to know in advance when probing the first card if others
* are there.
*/
int fwe_probe(struct fmc_device *fmc)
static int fwe_probe(struct fmc_device *fmc)
{
int err, index = 0;
const struct firmware *fw;
......@@ -144,7 +144,7 @@ int fwe_probe(struct fmc_device *fmc)
return 0;
}
int fwe_remove(struct fmc_device *fmc)
static int fwe_remove(struct fmc_device *fmc)
{
return 0;
}
......
......@@ -590,6 +590,5 @@ static void __exit mousevsc_exit(void)
}
MODULE_LICENSE("GPL");
MODULE_VERSION(HV_DRV_VERSION);
module_init(mousevsc_init);
module_exit(mousevsc_exit);
......@@ -48,30 +48,39 @@ struct vmbus_channel_message_table_entry {
* @negop is of type &struct icmsg_negotiate.
* Set up and fill in default negotiate response message.
*
* The max_fw_version specifies the maximum framework version that
* we can support and max _srv_version specifies the maximum service
* version we can support. A special value MAX_SRV_VER can be
* specified to indicate that we can handle the maximum version
* exposed by the host.
* The fw_version specifies the framework version that
* we can support and srv_version specifies the service
* version we can support.
*
* Mainly used by Hyper-V drivers.
*/
void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
struct icmsg_negotiate *negop, u8 *buf,
int max_fw_version, int max_srv_version)
int fw_version, int srv_version)
{
int icframe_vercnt;
int icmsg_vercnt;
int icframe_major, icframe_minor;
int icmsg_major, icmsg_minor;
int fw_major, fw_minor;
int srv_major, srv_minor;
int i;
bool found_match = false;
icmsghdrp->icmsgsize = 0x10;
fw_major = (fw_version >> 16);
fw_minor = (fw_version & 0xFFFF);
srv_major = (srv_version >> 16);
srv_minor = (srv_version & 0xFFFF);
negop = (struct icmsg_negotiate *)&buf[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
icframe_vercnt = negop->icframe_vercnt;
icmsg_vercnt = negop->icmsg_vercnt;
icframe_major = negop->icframe_vercnt;
icframe_minor = 0;
icmsg_major = negop->icmsg_vercnt;
icmsg_minor = 0;
/*
* Select the framework version number we will
......@@ -79,26 +88,48 @@ void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
*/
for (i = 0; i < negop->icframe_vercnt; i++) {
if (negop->icversion_data[i].major <= max_fw_version)
icframe_vercnt = negop->icversion_data[i].major;
if ((negop->icversion_data[i].major == fw_major) &&
(negop->icversion_data[i].minor == fw_minor)) {
icframe_major = negop->icversion_data[i].major;
icframe_minor = negop->icversion_data[i].minor;
found_match = true;
}
}
if (!found_match)
goto fw_error;
found_match = false;
for (i = negop->icframe_vercnt;
(i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) {
if (negop->icversion_data[i].major <= max_srv_version)
icmsg_vercnt = negop->icversion_data[i].major;
if ((negop->icversion_data[i].major == srv_major) &&
(negop->icversion_data[i].minor == srv_minor)) {
icmsg_major = negop->icversion_data[i].major;
icmsg_minor = negop->icversion_data[i].minor;
found_match = true;
}
}
/*
* Respond with the maximum framework and service
* Respond with the framework and service
* version numbers we can support.
*/
fw_error:
if (!found_match) {
negop->icframe_vercnt = 0;
negop->icmsg_vercnt = 0;
} else {
negop->icframe_vercnt = 1;
negop->icmsg_vercnt = 1;
negop->icversion_data[0].major = icframe_vercnt;
negop->icversion_data[0].minor = 0;
negop->icversion_data[1].major = icmsg_vercnt;
negop->icversion_data[1].minor = 0;
}
negop->icversion_data[0].major = icframe_major;
negop->icversion_data[0].minor = icframe_minor;
negop->icversion_data[1].major = icmsg_major;
negop->icversion_data[1].minor = icmsg_minor;
return found_match;
}
EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
......@@ -261,6 +292,13 @@ static void vmbus_process_offer(struct work_struct *work)
return;
}
/*
* This state is used to indicate a successful open
* so that when we do close the channel normally, we
* can cleanup properly
*/
newchannel->state = CHANNEL_OPEN_STATE;
/*
* Start the process of binding this offer to the driver
* We need to set the DeviceObject field before calling
......@@ -287,13 +325,6 @@ static void vmbus_process_offer(struct work_struct *work)
kfree(newchannel->device_obj);
free_channel(newchannel);
} else {
/*
* This state is used to indicate a successful open
* so that when we do close the channel normally, we
* can cleanup properly
*/
newchannel->state = CHANNEL_OPEN_STATE;
}
}
......
......@@ -195,7 +195,10 @@ int vmbus_connect(void)
do {
ret = vmbus_negotiate_version(msginfo, version);
if (ret == 0)
if (ret)
goto cleanup;
if (vmbus_connection.conn_state == CONNECTED)
break;
version = vmbus_get_next_version(version);
......
......@@ -825,7 +825,6 @@ static void hot_add_req(struct work_struct *dummy)
memset(&resp, 0, sizeof(struct dm_hot_add_response));
resp.hdr.type = DM_MEM_HOT_ADD_RESPONSE;
resp.hdr.size = sizeof(struct dm_hot_add_response);
resp.hdr.trans_id = atomic_inc_return(&trans_id);
#ifdef CONFIG_MEMORY_HOTPLUG
pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
......@@ -887,6 +886,7 @@ static void hot_add_req(struct work_struct *dummy)
pr_info("Memory hot add failed\n");
dm->state = DM_INITIALIZED;
resp.hdr.trans_id = atomic_inc_return(&trans_id);
vmbus_sendpacket(dm->dev->channel, &resp,
sizeof(struct dm_hot_add_response),
(unsigned long)NULL,
......@@ -1081,7 +1081,6 @@ static void balloon_up(struct work_struct *dummy)
bl_resp = (struct dm_balloon_response *)send_buffer;
memset(send_buffer, 0, PAGE_SIZE);
bl_resp->hdr.type = DM_BALLOON_RESPONSE;
bl_resp->hdr.trans_id = atomic_inc_return(&trans_id);
bl_resp->hdr.size = sizeof(struct dm_balloon_response);
bl_resp->more_pages = 1;
......@@ -1109,6 +1108,7 @@ static void balloon_up(struct work_struct *dummy)
*/
do {
bl_resp->hdr.trans_id = atomic_inc_return(&trans_id);
ret = vmbus_sendpacket(dm_device.dev->channel,
bl_resp,
bl_resp->hdr.size,
......@@ -1526,5 +1526,4 @@ static int __init init_balloon_drv(void)
module_init(init_balloon_drv);
MODULE_DESCRIPTION("Hyper-V Balloon");
MODULE_VERSION(HV_DRV_VERSION);
MODULE_LICENSE("GPL");
......@@ -29,6 +29,16 @@
#include <linux/hyperv.h>
/*
* Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7)
*/
#define WIN7_SRV_MAJOR 3
#define WIN7_SRV_MINOR 0
#define WIN7_SRV_MAJOR_MINOR (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR)
#define WIN8_SRV_MAJOR 4
#define WIN8_SRV_MINOR 0
#define WIN8_SRV_MAJOR_MINOR (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
/*
* Global state maintained for transaction that is being processed.
......@@ -76,7 +86,9 @@ static u8 *recv_buffer;
/*
* Register the kernel component with the user-level daemon.
* As part of this registration, pass the LIC version number.
* This number has no meaning, it satisfies the registration protocol.
*/
#define HV_DRV_VERSION "3.1"
static void
kvp_register(int reg_value)
......@@ -593,8 +605,19 @@ void hv_kvp_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
/*
* We start with win8 version and if the host cannot
* support that we use the previous version.
*/
if (vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, UTIL_FW_MAJOR_MINOR,
WIN8_SRV_MAJOR_MINOR))
goto done;
vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
recv_buffer, UTIL_FW_MAJOR_MINOR,
WIN7_SRV_MAJOR_MINOR);
} else {
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
......@@ -626,6 +649,7 @@ void hv_kvp_onchannelcallback(void *context)
return;
}
done:
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
| ICMSGHDRFLAG_RESPONSE;
......
......@@ -24,6 +24,10 @@
#include <linux/workqueue.h>
#include <linux/hyperv.h>
#define VSS_MAJOR 5
#define VSS_MINOR 0
#define VSS_MAJOR_MINOR (VSS_MAJOR << 16 | VSS_MINOR)
/*
......@@ -186,18 +190,8 @@ void hv_vss_onchannelcallback(void *context)
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
/*
* We currently negotiate the highest number the
* host has presented. If this version is not
* atleast 5.0, reject.
*/
negop = (struct icmsg_negotiate *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
if (negop->icversion_data[1].major < 5)
negop->icframe_vercnt = 0;
recv_buffer, UTIL_FW_MAJOR_MINOR,
VSS_MAJOR_MINOR);
} else {
vss_msg = (struct hv_vss_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
......
......@@ -28,6 +28,18 @@
#include <linux/reboot.h>
#include <linux/hyperv.h>
#define SHUTDOWN_MAJOR 3
#define SHUTDOWN_MINOR 0
#define SHUTDOWN_MAJOR_MINOR (SHUTDOWN_MAJOR << 16 | SHUTDOWN_MINOR)
#define TIMESYNCH_MAJOR 3
#define TIMESYNCH_MINOR 0
#define TIMESYNCH_MAJOR_MINOR (TIMESYNCH_MAJOR << 16 | TIMESYNCH_MINOR)
#define HEARTBEAT_MAJOR 3
#define HEARTBEAT_MINOR 0
#define HEARTBEAT_MAJOR_MINOR (HEARTBEAT_MAJOR << 16 | HEARTBEAT_MINOR)
static void shutdown_onchannelcallback(void *context);
static struct hv_util_service util_shutdown = {
.util_cb = shutdown_onchannelcallback,
......@@ -87,7 +99,8 @@ static void shutdown_onchannelcallback(void *context)
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
shut_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
shut_txf_buf, UTIL_FW_MAJOR_MINOR,
SHUTDOWN_MAJOR_MINOR);
} else {
shutdown_msg =
(struct shutdown_msg_data *)&shut_txf_buf[
......@@ -213,7 +226,8 @@ static void timesync_onchannelcallback(void *context)
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf,
MAX_SRV_VER, MAX_SRV_VER);
UTIL_FW_MAJOR_MINOR,
TIMESYNCH_MAJOR_MINOR);
} else {
timedatap = (struct ictimesync_data *)&time_txf_buf[
sizeof(struct vmbuspipe_hdr) +
......@@ -253,7 +267,8 @@ static void heartbeat_onchannelcallback(void *context)
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, NULL,
hbeat_txf_buf, MAX_SRV_VER, MAX_SRV_VER);
hbeat_txf_buf, UTIL_FW_MAJOR_MINOR,
HEARTBEAT_MAJOR_MINOR);
} else {
heartbeat_msg =
(struct heartbeat_msg_data *)&hbeat_txf_buf[
......@@ -380,5 +395,4 @@ module_init(init_hyperv_utils);
module_exit(exit_hyperv_utils);
MODULE_DESCRIPTION("Hyper-V Utilities");
MODULE_VERSION(HV_DRV_VERSION);
MODULE_LICENSE("GPL");
......@@ -816,7 +816,6 @@ static void __exit vmbus_exit(void)
MODULE_LICENSE("GPL");
MODULE_VERSION(HV_DRV_VERSION);
subsys_initcall(hv_acpi_init);
module_exit(vmbus_exit);
......@@ -19,7 +19,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
/* Serialize access to ssc_list and user count */
static DEFINE_SPINLOCK(user_lock);
......@@ -137,13 +136,6 @@ static int ssc_probe(struct platform_device *pdev)
struct resource *regs;
struct ssc_device *ssc;
const struct atmel_ssc_platform_data *plat_dat;
struct pinctrl *pinctrl;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl)) {
dev_err(&pdev->dev, "Failed to request pinctrl\n");
return PTR_ERR(pinctrl);
}
ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
if (!ssc) {
......
......@@ -759,7 +759,7 @@ static int ilo_probe(struct pci_dev *pdev,
/* Ignore subsystem_device = 0x1979 (set by BIOS) */
if (pdev->subsystem_device == 0x1979)
goto out;
return 0;
if (max_ccb > MAX_CCB)
max_ccb = MAX_CCB;
......@@ -899,7 +899,7 @@ static void __exit ilo_exit(void)
class_destroy(ilo_class);
}
MODULE_VERSION("1.4");
MODULE_VERSION("1.4.1");
MODULE_ALIAS(ILO_NAME);
MODULE_DESCRIPTION(ILO_NAME);
MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>");
......
......@@ -43,6 +43,7 @@
#include <linux/slab.h>
#include <scsi/scsi_cmnd.h>
#include <linux/debugfs.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_IDE
#include <linux/ide.h>
......@@ -50,6 +51,7 @@
#define DEFAULT_COUNT 10
#define REC_NUM_DEFAULT 10
#define EXEC_SIZE 64
enum cname {
CN_INVALID,
......@@ -68,6 +70,7 @@ enum ctype {
CT_NONE,
CT_PANIC,
CT_BUG,
CT_WARNING,
CT_EXCEPTION,
CT_LOOP,
CT_OVERFLOW,
......@@ -77,7 +80,12 @@ enum ctype {
CT_WRITE_AFTER_FREE,
CT_SOFTLOCKUP,
CT_HARDLOCKUP,
CT_SPINLOCKUP,
CT_HUNG_TASK,
CT_EXEC_DATA,
CT_EXEC_STACK,
CT_EXEC_KMALLOC,
CT_EXEC_VMALLOC,
};
static char* cp_name[] = {
......@@ -95,6 +103,7 @@ static char* cp_name[] = {
static char* cp_type[] = {
"PANIC",
"BUG",
"WARNING",
"EXCEPTION",
"LOOP",
"OVERFLOW",
......@@ -104,7 +113,12 @@ static char* cp_type[] = {
"WRITE_AFTER_FREE",
"SOFTLOCKUP",
"HARDLOCKUP",
"SPINLOCKUP",
"HUNG_TASK",
"EXEC_DATA",
"EXEC_STACK",
"EXEC_KMALLOC",
"EXEC_VMALLOC",
};
static struct jprobe lkdtm;
......@@ -121,6 +135,9 @@ static enum cname cpoint = CN_INVALID;
static enum ctype cptype = CT_NONE;
static int count = DEFAULT_COUNT;
static DEFINE_SPINLOCK(count_lock);
static DEFINE_SPINLOCK(lock_me_up);
static u8 data_area[EXEC_SIZE];
module_param(recur_count, int, 0644);
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\
......@@ -275,6 +292,19 @@ static int recursive_loop(int a)
return recursive_loop(a);
}
static void do_nothing(void)
{
return;
}
static void execute_location(void *dst)
{
void (*func)(void) = dst;
memcpy(dst, do_nothing, EXEC_SIZE);
func();
}
static void lkdtm_do_action(enum ctype which)
{
switch (which) {
......@@ -284,6 +314,9 @@ static void lkdtm_do_action(enum ctype which)
case CT_BUG:
BUG();
break;
case CT_WARNING:
WARN_ON(1);
break;
case CT_EXCEPTION:
*((int *) 0) = 0;
break;
......@@ -295,10 +328,10 @@ static void lkdtm_do_action(enum ctype which)
(void) recursive_loop(0);
break;
case CT_CORRUPT_STACK: {
volatile u32 data[8];
volatile u32 *p = data;
/* Make sure the compiler creates and uses an 8 char array. */
volatile char data[8];
p[12] = 0x12345678;
memset((void *)data, 0, 64);
break;
}
case CT_UNALIGNED_LOAD_STORE_WRITE: {
......@@ -340,10 +373,34 @@ static void lkdtm_do_action(enum ctype which)
for (;;)
cpu_relax();
break;
case CT_SPINLOCKUP:
/* Must be called twice to trigger. */
spin_lock(&lock_me_up);
break;
case CT_HUNG_TASK:
set_current_state(TASK_UNINTERRUPTIBLE);
schedule();
break;
case CT_EXEC_DATA:
execute_location(data_area);
break;
case CT_EXEC_STACK: {
u8 stack_area[EXEC_SIZE];
execute_location(stack_area);
break;
}
case CT_EXEC_KMALLOC: {
u32 *kmalloc_area = kmalloc(EXEC_SIZE, GFP_KERNEL);
execute_location(kmalloc_area);
kfree(kmalloc_area);
break;
}
case CT_EXEC_VMALLOC: {
u32 *vmalloc_area = vmalloc(EXEC_SIZE);
execute_location(vmalloc_area);
vfree(vmalloc_area);
break;
}
case CT_NONE:
default:
break;
......
......@@ -418,15 +418,23 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
struct file *file, poll_table *wait)
{
unsigned int mask = 0;
mutex_unlock(&dev->device_lock);
poll_wait(file, &dev->iamthif_cl.wait, wait);
mutex_lock(&dev->device_lock);
if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
if (!mei_cl_is_connected(&dev->iamthif_cl)) {
mask = POLLERR;
} else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
dev->iamthif_file_object == file) {
mask |= (POLLIN | POLLRDNORM);
dev_dbg(&dev->pdev->dev, "run next amthif cb\n");
mei_amthif_run_next_cmd(dev);
}
mutex_unlock(&dev->device_lock);
return mask;
}
......
......@@ -47,7 +47,7 @@ static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
id = driver->id_table;
while (id->name[0]) {
if (!strcmp(dev_name(dev), id->name))
if (!strncmp(dev_name(dev), id->name, sizeof(id->name)))
return 1;
id++;
......@@ -71,7 +71,7 @@ static int mei_cl_device_probe(struct device *dev)
dev_dbg(dev, "Device probe\n");
strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE);
strncpy(id.name, dev_name(dev), sizeof(id.name));
return driver->probe(device, &id);
}
......
......@@ -635,10 +635,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
dev = cl->dev;
if (cl->state != MEI_FILE_CONNECTED)
return -ENODEV;
if (dev->dev_state != MEI_DEV_ENABLED)
if (!mei_cl_is_connected(cl))
return -ENODEV;
if (cl->read_cb) {
......@@ -892,18 +889,22 @@ void mei_cl_all_disconnect(struct mei_device *dev)
/**
* mei_cl_all_read_wakeup - wake up all readings so they can be interrupted
* mei_cl_all_wakeup - wake up all readers and writers they can be interrupted
*
* @dev - mei device
*/
void mei_cl_all_read_wakeup(struct mei_device *dev)
void mei_cl_all_wakeup(struct mei_device *dev)
{
struct mei_cl *cl, *next;
list_for_each_entry_safe(cl, next, &dev->file_list, link) {
if (waitqueue_active(&cl->rx_wait)) {
dev_dbg(&dev->pdev->dev, "Waking up client!\n");
dev_dbg(&dev->pdev->dev, "Waking up reading client!\n");
wake_up_interruptible(&cl->rx_wait);
}
if (waitqueue_active(&cl->tx_wait)) {
dev_dbg(&dev->pdev->dev, "Waking up writing client!\n");
wake_up_interruptible(&cl->tx_wait);
}
}
}
......
......@@ -84,6 +84,13 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
/*
* MEI input output function prototype
*/
static inline bool mei_cl_is_connected(struct mei_cl *cl)
{
return (cl->dev &&
cl->dev->dev_state == MEI_DEV_ENABLED &&
cl->state == MEI_FILE_CONNECTED);
}
bool mei_cl_is_other_connecting(struct mei_cl *cl);
int mei_cl_disconnect(struct mei_cl *cl);
int mei_cl_connect(struct mei_cl *cl, struct file *file);
......@@ -99,7 +106,7 @@ void mei_host_client_init(struct work_struct *work);
void mei_cl_all_disconnect(struct mei_device *dev);
void mei_cl_all_read_wakeup(struct mei_device *dev);
void mei_cl_all_wakeup(struct mei_device *dev);
void mei_cl_all_write_clear(struct mei_device *dev);
#endif /* _MEI_CLIENT_H_ */
......@@ -176,21 +176,18 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr = mei_hcsr_read(hw);
dev_dbg(&dev->pdev->dev, "before reset HCSR = 0x%08x.\n", hcsr);
hcsr |= (H_RST | H_IG);
hcsr |= H_RST | H_IG | H_IS;
if (intr_enable)
hcsr |= H_IE;
else
hcsr |= ~H_IE;
hcsr &= ~H_IE;
mei_hcsr_set(hw, hcsr);
mei_me_reg_write(hw, H_CSR, hcsr);
if (dev->dev_state == MEI_DEV_POWER_DOWN)
mei_me_hw_reset_release(dev);
dev_dbg(&dev->pdev->dev, "current HCSR = 0x%08x.\n", mei_hcsr_read(hw));
return 0;
}
......
......@@ -154,8 +154,14 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->dev_state != MEI_DEV_POWER_DOWN)
dev->dev_state = MEI_DEV_RESETTING;
/* remove all waiting requests */
mei_cl_all_write_clear(dev);
mei_cl_all_disconnect(dev);
/* wake up all readings so they can be interrupted */
mei_cl_all_wakeup(dev);
/* remove entry if already in list */
dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
mei_cl_unlink(&dev->wd_cl);
......@@ -196,11 +202,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
mei_hbm_start_req(dev);
/* wake up all readings so they can be interrupted */
mei_cl_all_read_wakeup(dev);
/* remove all waiting requests */
mei_cl_all_write_clear(dev);
}
EXPORT_SYMBOL_GPL(mei_reset);
......
......@@ -625,24 +625,32 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
unsigned int mask = 0;
if (WARN_ON(!cl || !cl->dev))
return mask;
return POLLERR;
dev = cl->dev;
mutex_lock(&dev->device_lock);
if (dev->dev_state != MEI_DEV_ENABLED)
goto out;
if (cl == &dev->iamthif_cl) {
mask = mei_amthif_poll(dev, file, wait);
if (!mei_cl_is_connected(cl)) {
mask = POLLERR;
goto out;
}
mutex_unlock(&dev->device_lock);
if (cl == &dev->iamthif_cl)
return mei_amthif_poll(dev, file, wait);
poll_wait(file, &cl->tx_wait, wait);
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
mask = POLLERR;
goto out;
}
if (MEI_WRITE_COMPLETE == cl->writing_state)
mask |= (POLLIN | POLLRDNORM);
......
......@@ -68,7 +68,8 @@ static int sram_probe(struct platform_device *pdev)
ret = gen_pool_add_virt(sram->pool, (unsigned long)virt_base,
res->start, size, -1);
if (ret < 0) {
gen_pool_destroy(sram->pool);
if (sram->clk)
clk_disable_unprepare(sram->clk);
return ret;
}
......
......@@ -562,7 +562,9 @@ long st_register(struct st_proto_s *new_proto)
if ((st_gdata->protos_registered != ST_EMPTY) &&
(test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
pr_err(" KIM failure complete callback ");
spin_lock_irqsave(&st_gdata->lock, flags);
st_reg_complete(st_gdata, err);
spin_unlock_irqrestore(&st_gdata->lock, flags);
clear_bit(ST_REG_PENDING, &st_gdata->st_state);
}
return -EINVAL;
......
......@@ -133,7 +133,7 @@ MODULE_LICENSE("GPL");
#define VMWARE_BALLOON_CMD(cmd, data, result) \
({ \
unsigned long __stat, __dummy1, __dummy2; \
__asm__ __volatile__ ("inl (%%dx)" : \
__asm__ __volatile__ ("inl %%dx" : \
"=a"(__stat), \
"=c"(__dummy1), \
"=d"(__dummy2), \
......
......@@ -113,5 +113,5 @@ module_exit(vmci_drv_exit);
MODULE_AUTHOR("VMware, Inc.");
MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface.");
MODULE_VERSION("1.0.0.0-k");
MODULE_VERSION("1.1.0.0-k");
MODULE_LICENSE("GPL v2");
......@@ -35,6 +35,13 @@ struct vmci_obj {
enum vmci_obj_type type;
};
/*
* Needed by other components of this module. It's okay to have one global
* instance of this because there can only ever be one VMCI device. Our
* virtual hardware enforces this.
*/
extern struct pci_dev *vmci_pdev;
u32 vmci_get_context_id(void);
int vmci_send_datagram(struct vmci_datagram *dg);
......
......@@ -65,9 +65,11 @@ struct vmci_guest_device {
void *data_buffer;
void *notification_bitmap;
dma_addr_t notification_base;
};
/* vmci_dev singleton device and supporting data*/
struct pci_dev *vmci_pdev;
static struct vmci_guest_device *vmci_dev_g;
static DEFINE_SPINLOCK(vmci_dev_spinlock);
......@@ -528,7 +530,9 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
* well.
*/
if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
vmci_dev->notification_bitmap = vmalloc(PAGE_SIZE);
vmci_dev->notification_bitmap = dma_alloc_coherent(
&pdev->dev, PAGE_SIZE, &vmci_dev->notification_base,
GFP_KERNEL);
if (!vmci_dev->notification_bitmap) {
dev_warn(&pdev->dev,
"Unable to allocate notification bitmap\n");
......@@ -546,6 +550,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
/* Set up global device so that we can start sending datagrams */
spin_lock_irq(&vmci_dev_spinlock);
vmci_dev_g = vmci_dev;
vmci_pdev = pdev;
spin_unlock_irq(&vmci_dev_spinlock);
/*
......@@ -553,9 +558,8 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
* used.
*/
if (capabilities & VMCI_CAPS_NOTIFICATIONS) {
struct page *page =
vmalloc_to_page(vmci_dev->notification_bitmap);
unsigned long bitmap_ppn = page_to_pfn(page);
unsigned long bitmap_ppn =
vmci_dev->notification_base >> PAGE_SHIFT;
if (!vmci_dbell_register_notification_bitmap(bitmap_ppn)) {
dev_warn(&pdev->dev,
"VMCI device unable to register notification bitmap with PPN 0x%x\n",
......@@ -665,11 +669,14 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
if (vmci_dev->notification_bitmap) {
iowrite32(VMCI_CONTROL_RESET,
vmci_dev->iobase + VMCI_CONTROL_ADDR);
vfree(vmci_dev->notification_bitmap);
dma_free_coherent(&pdev->dev, PAGE_SIZE,
vmci_dev->notification_bitmap,
vmci_dev->notification_base);
}
err_remove_vmci_dev_g:
spin_lock_irq(&vmci_dev_spinlock);
vmci_pdev = NULL;
vmci_dev_g = NULL;
spin_unlock_irq(&vmci_dev_spinlock);
......@@ -699,6 +706,7 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
spin_lock_irq(&vmci_dev_spinlock);
vmci_dev_g = NULL;
vmci_pdev = NULL;
spin_unlock_irq(&vmci_dev_spinlock);
dev_dbg(&pdev->dev, "Resetting vmci device\n");
......@@ -727,7 +735,9 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
* device, so we can safely free it here.
*/
vfree(vmci_dev->notification_bitmap);
dma_free_coherent(&pdev->dev, PAGE_SIZE,
vmci_dev->notification_bitmap,
vmci_dev->notification_base);
}
vfree(vmci_dev->data_buffer);
......
This diff is collapsed.
......@@ -146,24 +146,6 @@ VMCI_QP_PAGESTORE_IS_WELLFORMED(struct vmci_qp_page_store *page_store)
return page_store->len >= 2;
}
/*
* Helper function to check if the non-blocking flag
* is set for a given queue pair.
*/
static inline bool vmci_can_block(u32 flags)
{
return !(flags & VMCI_QPFLAG_NONBLOCK);
}
/*
* Helper function to check if the queue pair is pinned
* into memory.
*/
static inline bool vmci_qp_pinned(u32 flags)
{
return flags & VMCI_QPFLAG_PINNED;
}
void vmci_qp_broker_exit(void);
int vmci_qp_broker_alloc(struct vmci_handle handle, u32 peer,
u32 flags, u32 priv_flags,
......
......@@ -306,7 +306,6 @@ static void netvsc_get_drvinfo(struct net_device *net,
struct ethtool_drvinfo *info)
{
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strlcpy(info->version, HV_DRV_VERSION, sizeof(info->version));
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
}
......@@ -529,7 +528,6 @@ static int __init netvsc_drv_init(void)
}
MODULE_LICENSE("GPL");
MODULE_VERSION(HV_DRV_VERSION);
MODULE_DESCRIPTION("Microsoft Hyper-V network driver");
module_init(netvsc_drv_init);
......
......@@ -37,7 +37,7 @@ config PARPORT_PC
tristate "PC-style hardware"
depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && !S390 && \
(!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && \
!XTENSA && !CRIS
!XTENSA && !CRIS && !H8300
---help---
You should say Y here if you have a PC-style parallel port. All
......
......@@ -232,7 +232,6 @@ static int __exit amiga_parallel_remove(struct platform_device *pdev)
if (port->irq != PARPORT_IRQ_NONE)
free_irq(IRQ_AMIGA_CIAA_FLG, port);
parport_put_port(port);
platform_set_drvdata(pdev, NULL);
return 0;
}
......
......@@ -1879,7 +1879,6 @@ static void __exit storvsc_drv_exit(void)
}
MODULE_LICENSE("GPL");
MODULE_VERSION(HV_DRV_VERSION);
MODULE_DESCRIPTION("Microsoft Hyper-V virtual storage driver");
module_init(storvsc_drv_init);
module_exit(storvsc_drv_exit);
menuconfig UIO
tristate "Userspace I/O drivers"
depends on MMU
help
Enable this to allow the userspace driver core code to be
built. This code allows userspace programs easy access to
......@@ -23,13 +24,6 @@ config UIO_CIF
To compile this driver as a module, choose M here: the module
will be called uio_cif.
config UIO_PDRV
tristate "Userspace I/O platform driver"
help
Generic platform driver for Userspace I/O devices.
If you don't know what to do here, say N.
config UIO_PDRV_GENIRQ
tristate "Userspace I/O platform driver with generic IRQ handling"
help
......@@ -128,4 +122,17 @@ config UIO_PRUSS
To compile this driver as a module, choose M here: the module
will be called uio_pruss.
config UIO_MF624
tristate "Humusoft MF624 DAQ PCI card driver"
depends on PCI
help
Userspace I/O interface for the Humusoft MF624 PCI card.
A sample userspace application using this driver is available
(among other MF624 related information and software components)
for download in a git repository:
git clone git://rtime.felk.cvut.cz/mf6xx.git
If you compile this as a module, it will be called uio_mf624.
endif
obj-$(CONFIG_UIO) += uio.o
obj-$(CONFIG_UIO_CIF) += uio_cif.o
obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o
obj-$(CONFIG_UIO_DMEM_GENIRQ) += uio_dmem_genirq.o
obj-$(CONFIG_UIO_AEC) += uio_aec.o
......@@ -8,3 +7,4 @@ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
obj-$(CONFIG_UIO_NETX) += uio_netx.o
obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
obj-$(CONFIG_UIO_MF624) += uio_mf624.o
......@@ -35,7 +35,6 @@ struct uio_device {
atomic_t event;
struct fasync_struct *async_queue;
wait_queue_head_t wait;
int vma_count;
struct uio_info *info;
struct kobject *map_dir;
struct kobject *portio_dir;
......@@ -593,18 +592,6 @@ static int uio_find_mem_index(struct vm_area_struct *vma)
return -1;
}
static void uio_vma_open(struct vm_area_struct *vma)
{
struct uio_device *idev = vma->vm_private_data;
idev->vma_count++;
}
static void uio_vma_close(struct vm_area_struct *vma)
{
struct uio_device *idev = vma->vm_private_data;
idev->vma_count--;
}
static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct uio_device *idev = vma->vm_private_data;
......@@ -630,12 +617,23 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
return 0;
}
static const struct vm_operations_struct uio_vm_ops = {
.open = uio_vma_open,
.close = uio_vma_close,
static const struct vm_operations_struct uio_logical_vm_ops = {
.fault = uio_vma_fault,
};
static int uio_mmap_logical(struct vm_area_struct *vma)
{
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_ops = &uio_logical_vm_ops;
return 0;
}
static const struct vm_operations_struct uio_physical_vm_ops = {
#ifdef CONFIG_HAVE_IOREMAP_PROT
.access = generic_access_phys,
#endif
};
static int uio_mmap_physical(struct vm_area_struct *vma)
{
struct uio_device *idev = vma->vm_private_data;
......@@ -643,6 +641,8 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
if (mi < 0)
return -EINVAL;
vma->vm_ops = &uio_physical_vm_ops;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
return remap_pfn_range(vma,
......@@ -652,14 +652,6 @@ static int uio_mmap_physical(struct vm_area_struct *vma)
vma->vm_page_prot);
}
static int uio_mmap_logical(struct vm_area_struct *vma)
{
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_ops = &uio_vm_ops;
uio_vma_open(vma);
return 0;
}
static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
{
struct uio_listener *listener = filep->private_data;
......
......@@ -146,7 +146,7 @@ static int uio_dmem_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
static int uio_dmem_genirq_probe(struct platform_device *pdev)
{
struct uio_dmem_genirq_pdata *pdata = pdev->dev.platform_data;
struct uio_dmem_genirq_pdata *pdata = dev_get_platdata(&pdev->dev);
struct uio_info *uioinfo = &pdata->uioinfo;
struct uio_dmem_genirq_platdata *priv;
struct uio_mem *uiomem;
......
/*
* UIO driver fo Humusoft MF624 DAQ card.
* Copyright (C) 2011 Rostislav Lisovy <lisovy@gmail.com>,
* Czech Technical University in Prague
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/uio_driver.h>
#define PCI_VENDOR_ID_HUMUSOFT 0x186c
#define PCI_DEVICE_ID_MF624 0x0624
#define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c
#define PCI_SUBDEVICE_DEVICE 0x0624
/* BAR0 Interrupt control/status register */
#define INTCSR 0x4C
#define INTCSR_ADINT_ENABLE (1 << 0)
#define INTCSR_CTR4INT_ENABLE (1 << 3)
#define INTCSR_PCIINT_ENABLE (1 << 6)
#define INTCSR_ADINT_STATUS (1 << 2)
#define INTCSR_CTR4INT_STATUS (1 << 5)
enum mf624_interrupt_source {ADC, CTR4, ALL};
void mf624_disable_interrupt(enum mf624_interrupt_source source,
struct uio_info *info)
{
void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
switch (source) {
case ADC:
iowrite32(ioread32(INTCSR_reg)
& ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
INTCSR_reg);
break;
case CTR4:
iowrite32(ioread32(INTCSR_reg)
& ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
INTCSR_reg);
break;
case ALL:
default:
iowrite32(ioread32(INTCSR_reg)
& ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
| INTCSR_PCIINT_ENABLE),
INTCSR_reg);
break;
}
}
void mf624_enable_interrupt(enum mf624_interrupt_source source,
struct uio_info *info)
{
void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
switch (source) {
case ADC:
iowrite32(ioread32(INTCSR_reg)
| INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
INTCSR_reg);
break;
case CTR4:
iowrite32(ioread32(INTCSR_reg)
| INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
INTCSR_reg);
break;
case ALL:
default:
iowrite32(ioread32(INTCSR_reg)
| INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
| INTCSR_PCIINT_ENABLE,
INTCSR_reg);
break;
}
}
static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
{
void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
&& (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
mf624_disable_interrupt(ADC, info);
return IRQ_HANDLED;
}
if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
&& (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
mf624_disable_interrupt(CTR4, info);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
{
if (irq_on == 0)
mf624_disable_interrupt(ALL, info);
else if (irq_on == 1)
mf624_enable_interrupt(ALL, info);
return 0;
}
static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct uio_info *info;
info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
if (pci_enable_device(dev))
goto out_free;
if (pci_request_regions(dev, "mf624"))
goto out_disable;
info->name = "mf624";
info->version = "0.0.1";
/* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
/* BAR0 */
info->mem[0].name = "PCI chipset, interrupts, status "
"bits, special functions";
info->mem[0].addr = pci_resource_start(dev, 0);
if (!info->mem[0].addr)
goto out_release;
info->mem[0].size = pci_resource_len(dev, 0);
info->mem[0].memtype = UIO_MEM_PHYS;
info->mem[0].internal_addr = pci_ioremap_bar(dev, 0);
if (!info->mem[0].internal_addr)
goto out_release;
/* BAR2 */
info->mem[1].name = "ADC, DAC, DIO";
info->mem[1].addr = pci_resource_start(dev, 2);
if (!info->mem[1].addr)
goto out_unmap0;
info->mem[1].size = pci_resource_len(dev, 2);
info->mem[1].memtype = UIO_MEM_PHYS;
info->mem[1].internal_addr = pci_ioremap_bar(dev, 2);
if (!info->mem[1].internal_addr)
goto out_unmap0;
/* BAR4 */
info->mem[2].name = "Counter/timer chip";
info->mem[2].addr = pci_resource_start(dev, 4);
if (!info->mem[2].addr)
goto out_unmap1;
info->mem[2].size = pci_resource_len(dev, 4);
info->mem[2].memtype = UIO_MEM_PHYS;
info->mem[2].internal_addr = pci_ioremap_bar(dev, 4);
if (!info->mem[2].internal_addr)
goto out_unmap1;
info->irq = dev->irq;
info->irq_flags = IRQF_SHARED;
info->handler = mf624_irq_handler;
info->irqcontrol = mf624_irqcontrol;
if (uio_register_device(&dev->dev, info))
goto out_unmap2;
pci_set_drvdata(dev, info);
return 0;
out_unmap2:
iounmap(info->mem[2].internal_addr);
out_unmap1:
iounmap(info->mem[1].internal_addr);
out_unmap0:
iounmap(info->mem[0].internal_addr);
out_release:
pci_release_regions(dev);
out_disable:
pci_disable_device(dev);
out_free:
kfree(info);
return -ENODEV;
}
static void mf624_pci_remove(struct pci_dev *dev)
{
struct uio_info *info = pci_get_drvdata(dev);
mf624_disable_interrupt(ALL, info);
uio_unregister_device(info);
pci_release_regions(dev);
pci_disable_device(dev);
pci_set_drvdata(dev, NULL);
iounmap(info->mem[0].internal_addr);
iounmap(info->mem[1].internal_addr);
iounmap(info->mem[2].internal_addr);
kfree(info);
}
static DEFINE_PCI_DEVICE_TABLE(mf624_pci_id) = {
{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
{ 0, }
};
static struct pci_driver mf624_pci_driver = {
.name = "mf624",
.id_table = mf624_pci_id,
.probe = mf624_pci_probe,
.remove = mf624_pci_remove,
};
MODULE_DEVICE_TABLE(pci, mf624_pci_id);
module_pci_driver(mf624_pci_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
/*
* drivers/uio/uio_pdrv.c
*
* Copyright (C) 2008 by Digi International Inc.
* All rights reserved.
*
* 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 <linux/platform_device.h>
#include <linux/uio_driver.h>
#include <linux/stringify.h>
#include <linux/module.h>
#include <linux/slab.h>
#define DRIVER_NAME "uio_pdrv"
struct uio_platdata {
struct uio_info *uioinfo;
};
static int uio_pdrv_probe(struct platform_device *pdev)
{
struct uio_info *uioinfo = pdev->dev.platform_data;
struct uio_platdata *pdata;
struct uio_mem *uiomem;
int ret = -ENODEV;
int i;
if (!uioinfo || !uioinfo->name || !uioinfo->version) {
dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__);
goto err_uioinfo;
}
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__);
goto err_alloc_pdata;
}
pdata->uioinfo = uioinfo;
uiomem = &uioinfo->mem[0];
for (i = 0; i < pdev->num_resources; ++i) {
struct resource *r = &pdev->resource[i];
if (r->flags != IORESOURCE_MEM)
continue;
if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
dev_warn(&pdev->dev, "device has more than "
__stringify(MAX_UIO_MAPS)
" I/O memory resources.\n");
break;
}
uiomem->memtype = UIO_MEM_PHYS;
uiomem->addr = r->start;
uiomem->size = resource_size(r);
uiomem->name = r->name;
++uiomem;
}
while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
uiomem->size = 0;
++uiomem;
}
pdata->uioinfo->priv = pdata;
ret = uio_register_device(&pdev->dev, pdata->uioinfo);
if (ret) {
kfree(pdata);
err_alloc_pdata:
err_uioinfo:
return ret;
}
platform_set_drvdata(pdev, pdata);
return 0;
}
static int uio_pdrv_remove(struct platform_device *pdev)
{
struct uio_platdata *pdata = platform_get_drvdata(pdev);
uio_unregister_device(pdata->uioinfo);
kfree(pdata);
return 0;
}
static struct platform_driver uio_pdrv = {
.probe = uio_pdrv_probe,
.remove = uio_pdrv_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};
module_platform_driver(uio_pdrv);
MODULE_AUTHOR("Uwe Kleine-Koenig");
MODULE_DESCRIPTION("Userspace I/O platform driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRIVER_NAME);
......@@ -104,7 +104,7 @@ static int uio_pdrv_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
static int uio_pdrv_genirq_probe(struct platform_device *pdev)
{
struct uio_info *uioinfo = pdev->dev.platform_data;
struct uio_info *uioinfo = dev_get_platdata(&pdev->dev);
struct uio_pdrv_genirq_platdata *priv;
struct uio_mem *uiomem;
int ret = -EINVAL;
......
......@@ -121,7 +121,7 @@ static int pruss_probe(struct platform_device *dev)
struct uio_pruss_dev *gdev;
struct resource *regs_prussio;
int ret = -ENODEV, cnt = 0, len;
struct uio_pruss_pdata *pdata = dev->dev.platform_data;
struct uio_pruss_pdata *pdata = dev_get_platdata(&dev->dev);
gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL);
if (!gdev)
......@@ -224,7 +224,6 @@ static int pruss_remove(struct platform_device *dev)
struct uio_pruss_dev *gdev = platform_get_drvdata(dev);
pruss_cleanup(dev, gdev);
platform_set_drvdata(dev, NULL);
return 0;
}
......
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET) && GENERIC_HARDIRQS && HAS_DMA
depends on EXTCON
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
help
Say Y or M here if your system has a Dual Role SuperSpeed
......
......@@ -23,13 +23,15 @@
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-omap.h>
#include <linux/usb/dwc3-omap.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/extcon.h>
#include <linux/extcon/of_extcon.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/otg.h>
......@@ -135,9 +137,21 @@ struct dwc3_omap {
u32 revision;
u32 dma_status:1;
struct extcon_specific_cable_nb extcon_vbus_dev;
struct extcon_specific_cable_nb extcon_id_dev;
struct notifier_block vbus_nb;
struct notifier_block id_nb;
struct regulator *vbus_reg;
};
static struct dwc3_omap *_omap;
enum omap_dwc3_vbus_id_status {
OMAP_DWC3_ID_FLOAT,
OMAP_DWC3_ID_GROUND,
OMAP_DWC3_VBUS_OFF,
OMAP_DWC3_VBUS_VALID,
};
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
{
......@@ -201,18 +215,24 @@ static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value)
omap->irq0_offset, value);
}
int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
enum omap_dwc3_vbus_id_status status)
{
int ret;
u32 val;
struct dwc3_omap *omap = _omap;
if (!omap)
return -EPROBE_DEFER;
switch (status) {
case OMAP_DWC3_ID_GROUND:
dev_dbg(omap->dev, "ID GND\n");
if (omap->vbus_reg) {
ret = regulator_enable(omap->vbus_reg);
if (ret) {
dev_dbg(omap->dev, "regulator enable failed\n");
return;
}
}
val = dwc3_omap_read_utmi_status(omap);
val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
......@@ -235,6 +255,9 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
break;
case OMAP_DWC3_ID_FLOAT:
if (omap->vbus_reg)
regulator_disable(omap->vbus_reg);
case OMAP_DWC3_VBUS_OFF:
dev_dbg(omap->dev, "VBUS Disconnect\n");
......@@ -248,12 +271,9 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
break;
default:
dev_dbg(omap->dev, "ID float\n");
dev_dbg(omap->dev, "invalid state\n");
}
return 0;
}
EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
......@@ -346,6 +366,32 @@ static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
static int dwc3_omap_id_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, id_nb);
if (event)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
else
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_FLOAT);
return NOTIFY_DONE;
}
static int dwc3_omap_vbus_notifier(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, vbus_nb);
if (event)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
else
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_OFF);
return NOTIFY_DONE;
}
static int dwc3_omap_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
......@@ -353,6 +399,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
struct extcon_dev *edev;
struct regulator *vbus_reg = NULL;
int ret = -ENOMEM;
int irq;
......@@ -393,19 +441,22 @@ static int dwc3_omap_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
if (of_property_read_bool(node, "vbus-supply")) {
vbus_reg = devm_regulator_get(dev, "vbus");
if (IS_ERR(vbus_reg)) {
dev_err(dev, "vbus init failed\n");
return PTR_ERR(vbus_reg);
}
}
spin_lock_init(&omap->lock);
omap->dev = dev;
omap->irq = irq;
omap->base = base;
omap->vbus_reg = vbus_reg;
dev->dma_mask = &dwc3_omap_dma_mask;
/*
* REVISIT if we ever have two instances of the wrapper, we will be
* in big trouble
*/
_omap = omap;
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
......@@ -480,14 +531,46 @@ static int dwc3_omap_probe(struct platform_device *pdev)
dwc3_omap_enable_irqs(omap);
if (of_property_read_bool(node, "extcon")) {
edev = of_extcon_get_extcon_dev(dev, 0);
if (IS_ERR(edev)) {
dev_vdbg(dev, "couldn't get extcon device\n");
ret = PTR_ERR(edev);
goto err2;
}
omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
ret = extcon_register_interest(&omap->extcon_vbus_dev,
edev->name, "USB", &omap->vbus_nb);
if (ret < 0)
dev_vdbg(dev, "failed to register notifier for USB\n");
omap->id_nb.notifier_call = dwc3_omap_id_notifier;
ret = extcon_register_interest(&omap->extcon_id_dev, edev->name,
"USB-HOST", &omap->id_nb);
if (ret < 0)
dev_vdbg(dev,
"failed to register notifier for USB-HOST\n");
if (extcon_get_cable_state(edev, "USB") == true)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
if (extcon_get_cable_state(edev, "USB-HOST") == true)
dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
}
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(&pdev->dev, "failed to create dwc3 core\n");
goto err2;
goto err3;
}
return 0;
err3:
if (omap->extcon_vbus_dev.edev)
extcon_unregister_interest(&omap->extcon_vbus_dev);
if (omap->extcon_id_dev.edev)
extcon_unregister_interest(&omap->extcon_id_dev);
err2:
dwc3_omap_disable_irqs(omap);
......@@ -504,6 +587,10 @@ static int dwc3_omap_remove(struct platform_device *pdev)
{
struct dwc3_omap *omap = platform_get_drvdata(pdev);
if (omap->extcon_vbus_dev.edev)
extcon_unregister_interest(&omap->extcon_vbus_dev);
if (omap->extcon_id_dev.edev)
extcon_unregister_interest(&omap->extcon_id_dev);
dwc3_omap_disable_irqs(omap);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
......
......@@ -825,5 +825,4 @@ module_init(hvfb_drv_init);
module_exit(hvfb_drv_exit);
MODULE_LICENSE("GPL");
MODULE_VERSION(HV_DRV_VERSION);
MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver");
......@@ -23,7 +23,7 @@ static int vmic_probe(struct pci_dev *, const struct pci_device_id *);
static void vmic_remove(struct pci_dev *);
/** Base address to access FPGA register */
static void *vmic_base;
static void __iomem *vmic_base;
static const char driver_name[] = "vmivme_7805";
......
......@@ -243,6 +243,8 @@ static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge)
static void ca91cx42_irq_exit(struct ca91cx42_driver *bridge,
struct pci_dev *pdev)
{
struct vme_bridge *ca91cx42_bridge;
/* Disable interrupts from PCI to VME */
iowrite32(0, bridge->base + VINT_EN);
......@@ -251,7 +253,9 @@ static void ca91cx42_irq_exit(struct ca91cx42_driver *bridge,
/* Clear Any Pending PCI Interrupts */
iowrite32(0x00FFFFFF, bridge->base + LINT_STAT);
free_irq(pdev->irq, pdev);
ca91cx42_bridge = container_of((void *)bridge, struct vme_bridge,
driver_priv);
free_irq(pdev->irq, ca91cx42_bridge);
}
static int ca91cx42_iack_received(struct ca91cx42_driver *bridge, int level)
......@@ -856,7 +860,7 @@ static ssize_t ca91cx42_master_read(struct vme_master_resource *image,
void *buf, size_t count, loff_t offset)
{
ssize_t retval;
void *addr = image->kern_base + offset;
void __iomem *addr = image->kern_base + offset;
unsigned int done = 0;
unsigned int count32;
......@@ -916,7 +920,7 @@ static ssize_t ca91cx42_master_write(struct vme_master_resource *image,
void *buf, size_t count, loff_t offset)
{
ssize_t retval;
void *addr = image->kern_base + offset;
void __iomem *addr = image->kern_base + offset;
unsigned int done = 0;
unsigned int count32;
......
......@@ -1267,7 +1267,7 @@ static ssize_t tsi148_master_read(struct vme_master_resource *image, void *buf,
u32 aspace, cycle, dwidth;
struct vme_bus_error *vme_err = NULL;
struct vme_bridge *tsi148_bridge;
void *addr = image->kern_base + offset;
void __iomem *addr = image->kern_base + offset;
unsigned int done = 0;
unsigned int count32;
......@@ -1348,7 +1348,7 @@ static ssize_t tsi148_master_write(struct vme_master_resource *image, void *buf,
int retval = 0, enabled;
unsigned long long vme_base, size;
u32 aspace, cycle, dwidth;
void *addr = image->kern_base + offset;
void __iomem *addr = image->kern_base + offset;
unsigned int done = 0;
unsigned int count32;
......
/*
* OF helpers for External connector (extcon) framework
*
* Copyright (C) 2013 Texas Instruments, Inc.
* Kishon Vijay Abraham I <kishon@ti.com>
*
* Copyright (C) 2013 Samsung Electronics
* Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_OF_EXTCON_H
#define __LINUX_OF_EXTCON_H
#include <linux/err.h>
#if IS_ENABLED(CONFIG_OF_EXTCON)
extern struct extcon_dev
*of_extcon_get_extcon_dev(struct device *dev, int index);
#else
static inline struct extcon_dev
*of_extcon_get_extcon_dev(struct device *dev, int index)
{
return ERR_PTR(-ENOSYS);
}
#endif /* CONFIG_OF_EXTCON */
#endif /* __LINUX_OF_EXTCON_H */
......@@ -27,6 +27,14 @@
#include <linux/types.h>
/*
* Framework version for util services.
*/
#define UTIL_FW_MAJOR 3
#define UTIL_FW_MINOR 0
#define UTIL_FW_MAJOR_MINOR (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR)
/*
* Implementation of host controlled snapshot of the guest.
......@@ -455,27 +463,6 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi,
*read = dsize - *write;
}
/*
* We use the same version numbering for all Hyper-V modules.
*
* Definition of versioning is as follows;
*
* Major Number Changes for these scenarios;
* 1. When a new version of Windows Hyper-V
* is released.
* 2. A Major change has occurred in the
* Linux IC's.
* (For example the merge for the first time
* into the kernel) Every time the Major Number
* changes, the Revision number is reset to 0.
* Minor Number Changes when new functionality is added
* to the Linux IC's that is not a bug fix.
*
* 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync
*/
#define HV_DRV_VERSION "3.1"
/*
* VMBUS version is 32 bit entity broken up into
* two 16 bit quantities: major_number. minor_number.
......@@ -1494,7 +1481,7 @@ struct hyperv_service_callback {
};
#define MAX_SRV_VER 0x7ffffff
extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *,
extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *,
struct icmsg_negotiate *, u8 *, int,
int);
......
......@@ -372,17 +372,15 @@ struct palmas_usb {
struct extcon_dev edev;
/* used to set vbus, in atomic path */
struct work_struct set_vbus_work;
int id_otg_irq;
int id_irq;
int vbus_otg_irq;
int vbus_irq;
int vbus_enable;
enum palmas_usb_state linkstat;
int wakeup;
bool enable_vbus_detection;
bool enable_id_detection;
};
#define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator)
......
/*
* Copyright (C) 2013 by Texas Instruments
*
* The Inventra Controller Driver for Linux 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 __DWC3_OMAP_H__
#define __DWC3_OMAP_H__
enum omap_dwc3_vbus_id_status {
OMAP_DWC3_UNKNOWN = 0,
OMAP_DWC3_ID_GROUND,
OMAP_DWC3_ID_FLOAT,
OMAP_DWC3_VBUS_VALID,
OMAP_DWC3_VBUS_OFF,
};
#if (defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_DWC3_MODULE))
extern int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status);
#else
static inline int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
{
return -ENODEV;
}
#endif
#endif /* __DWC3_OMAP_H__ */
......@@ -4079,6 +4079,7 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
return len;
}
EXPORT_SYMBOL_GPL(generic_access_phys);
#endif
/*
......
......@@ -79,8 +79,6 @@ enum {
DNS
};
static char kvp_send_buffer[4096];
static char kvp_recv_buffer[4096 * 2];
static struct sockaddr_nl addr;
static int in_hand_shake = 1;
......@@ -1301,6 +1299,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
}
error = kvp_write_file(file, "HWADDR", "", mac_addr);
free(mac_addr);
if (error)
goto setval_error;
......@@ -1346,7 +1345,6 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
goto setval_error;
setval_done:
free(mac_addr);
fclose(file);
/*
......@@ -1355,12 +1353,15 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
*/
snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file);
system(cmd);
if (system(cmd)) {
syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
cmd, errno, strerror(errno));
return HV_E_FAIL;
}
return 0;
setval_error:
syslog(LOG_ERR, "Failed to write config file");
free(mac_addr);
fclose(file);
return error;
}
......@@ -1391,23 +1392,18 @@ kvp_get_domain_name(char *buffer, int length)
static int
netlink_send(int fd, struct cn_msg *msg)
{
struct nlmsghdr *nlh;
struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE };
unsigned int size;
struct msghdr message;
char buffer[64];
struct iovec iov[2];
size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
size = sizeof(struct cn_msg) + msg->len;
nlh = (struct nlmsghdr *)buffer;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = getpid();
nlh->nlmsg_type = NLMSG_DONE;
nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
nlh->nlmsg_flags = 0;
nlh.nlmsg_pid = getpid();
nlh.nlmsg_len = NLMSG_LENGTH(size);
iov[0].iov_base = nlh;
iov[0].iov_len = sizeof(*nlh);
iov[0].iov_base = &nlh;
iov[0].iov_len = sizeof(nlh);
iov[1].iov_base = msg;
iov[1].iov_len = size;
......@@ -1437,10 +1433,22 @@ int main(void)
int pool;
char *if_name;
struct hv_kvp_ipaddr_value *kvp_ip_val;
char *kvp_send_buffer;
char *kvp_recv_buffer;
size_t kvp_recv_buffer_len;
daemon(1, 0);
if (daemon(1, 0))
return 1;
openlog("KVP", 0, LOG_USER);
syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
kvp_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);
kvp_send_buffer = calloc(1, kvp_recv_buffer_len);
kvp_recv_buffer = calloc(1, kvp_recv_buffer_len);
if (!(kvp_send_buffer && kvp_recv_buffer)) {
syslog(LOG_ERR, "Failed to allocate netlink buffers");
exit(EXIT_FAILURE);
}
/*
* Retrieve OS release information.
*/
......@@ -1514,7 +1522,7 @@ int main(void)
continue;
}
len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0,
len = recvfrom(fd, kvp_recv_buffer, kvp_recv_buffer_len, 0,
addr_p, &addr_l);
if (len < 0) {
......
......@@ -38,8 +38,6 @@
#include <linux/netlink.h>
#include <syslog.h>
static char vss_recv_buffer[4096];
static char vss_send_buffer[4096];
static struct sockaddr_nl addr;
#ifndef SOL_NETLINK
......@@ -107,23 +105,18 @@ static int vss_operate(int operation)
static int netlink_send(int fd, struct cn_msg *msg)
{
struct nlmsghdr *nlh;
struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE };
unsigned int size;
struct msghdr message;
char buffer[64];
struct iovec iov[2];
size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
size = sizeof(struct cn_msg) + msg->len;
nlh = (struct nlmsghdr *)buffer;
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = getpid();
nlh->nlmsg_type = NLMSG_DONE;
nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
nlh->nlmsg_flags = 0;
nlh.nlmsg_pid = getpid();
nlh.nlmsg_len = NLMSG_LENGTH(size);
iov[0].iov_base = nlh;
iov[0].iov_len = sizeof(*nlh);
iov[0].iov_base = &nlh;
iov[0].iov_len = sizeof(nlh);
iov[1].iov_base = msg;
iov[1].iov_len = size;
......@@ -147,6 +140,9 @@ int main(void)
struct cn_msg *incoming_cn_msg;
int op;
struct hv_vss_msg *vss_msg;
char *vss_send_buffer;
char *vss_recv_buffer;
size_t vss_recv_buffer_len;
if (daemon(1, 0))
return 1;
......@@ -154,9 +150,18 @@ int main(void)
openlog("Hyper-V VSS", 0, LOG_USER);
syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
vss_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
vss_send_buffer = calloc(1, vss_recv_buffer_len);
vss_recv_buffer = calloc(1, vss_recv_buffer_len);
if (!(vss_send_buffer && vss_recv_buffer)) {
syslog(LOG_ERR, "Failed to allocate netlink buffers");
exit(EXIT_FAILURE);
}
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (fd < 0) {
syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
syslog(LOG_ERR, "netlink socket creation failed; error:%d %s",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
addr.nl_family = AF_NETLINK;
......@@ -167,12 +172,16 @@ int main(void)
error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
if (error < 0) {
syslog(LOG_ERR, "bind failed; error:%d", error);
syslog(LOG_ERR, "bind failed; error:%d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
nl_group = CN_VSS_IDX;
setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group));
if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) {
syslog(LOG_ERR, "setsockopt failed; error:%d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
/*
* Register ourselves with the kernel.
*/
......@@ -187,7 +196,7 @@ int main(void)
len = netlink_send(fd, message);
if (len < 0) {
syslog(LOG_ERR, "netlink_send failed; error:%d", len);
syslog(LOG_ERR, "netlink_send failed; error:%d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
......@@ -199,9 +208,18 @@ int main(void)
socklen_t addr_l = sizeof(addr);
pfd.events = POLLIN;
pfd.revents = 0;
poll(&pfd, 1, -1);
len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0,
if (poll(&pfd, 1, -1) < 0) {
syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
if (errno == EINVAL) {
close(fd);
exit(EXIT_FAILURE);
}
else
continue;
}
len = recvfrom(fd, vss_recv_buffer, vss_recv_buffer_len, 0,
addr_p, &addr_l);
if (len < 0) {
......@@ -241,7 +259,8 @@ int main(void)
vss_msg->error = error;
len = netlink_send(fd, incoming_cn_msg);
if (len < 0) {
syslog(LOG_ERR, "net_link send failed; error:%d", len);
syslog(LOG_ERR, "net_link send failed; error:%d %s",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment