mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
linux-can-next-for-3.16-20140425
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iEYEABECAAYFAlNZi6kACgkQjTAFq1RaXHO1YACfXTdM/OBitnFB8Jy2EiCj87tb Z2cAn3rrgz7LBLq2WoypQLc9JxeJUwqu =pdU3 -----END PGP SIGNATURE----- Merge tag 'linux-can-next-for-3.16-20140425' of git://gitorious.org/linux-can/linux-can-next Marc Kleine-Budde says: ==================== this is a pull request of 10 patches for net-next/master. It consists of three patches by Alexander Shiyan, which improve the mcp251x driver. Stefano Babic's patch move the SPI driver into a sub folder. The three patches by Olivier Sobrie add support for the Kvaser Leaf v2 and usb mini PCIe hardware to the existing driver. Alexander Stein contributes eg20t support to the c_can pci driver. Together with the patches on the net-tree, we'll be able to remove the pch_can driver soon. Kurt Van Dijck's two patches clean up the sysfs attributes of the softing driver. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
98a20b735b
@ -77,12 +77,6 @@ config CAN_TI_HECC
|
||||
Driver for TI HECC (High End CAN Controller) module found on many
|
||||
TI devices. The device specifications are available from www.ti.com
|
||||
|
||||
config CAN_MCP251X
|
||||
tristate "Microchip MCP251x SPI CAN controllers"
|
||||
depends on SPI && HAS_DMA
|
||||
---help---
|
||||
Driver for the Microchip MCP251x SPI CAN controllers.
|
||||
|
||||
config CAN_BFIN
|
||||
depends on BF534 || BF536 || BF537 || BF538 || BF539 || BF54x
|
||||
tristate "Analog Devices Blackfin on-chip CAN"
|
||||
@ -133,6 +127,8 @@ source "drivers/net/can/c_can/Kconfig"
|
||||
|
||||
source "drivers/net/can/cc770/Kconfig"
|
||||
|
||||
source "drivers/net/can/spi/Kconfig"
|
||||
|
||||
source "drivers/net/can/usb/Kconfig"
|
||||
|
||||
source "drivers/net/can/softing/Kconfig"
|
||||
|
@ -10,6 +10,7 @@ can-dev-y := dev.o
|
||||
|
||||
can-dev-$(CONFIG_CAN_LEDS) += led.o
|
||||
|
||||
obj-y += spi/
|
||||
obj-y += usb/
|
||||
obj-y += softing/
|
||||
|
||||
@ -19,7 +20,6 @@ obj-$(CONFIG_CAN_C_CAN) += c_can/
|
||||
obj-$(CONFIG_CAN_CC770) += cc770/
|
||||
obj-$(CONFIG_CAN_AT91) += at91_can.o
|
||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||
obj-$(CONFIG_CAN_BFIN) += bfin_can.o
|
||||
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
|
||||
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
|
||||
|
@ -19,9 +19,13 @@
|
||||
|
||||
#include "c_can.h"
|
||||
|
||||
#define PCI_DEVICE_ID_PCH_CAN 0x8818
|
||||
#define PCH_PCI_SOFT_RESET 0x01fc
|
||||
|
||||
enum c_can_pci_reg_align {
|
||||
C_CAN_REG_ALIGN_16,
|
||||
C_CAN_REG_ALIGN_32,
|
||||
C_CAN_REG_32,
|
||||
};
|
||||
|
||||
struct c_can_pci_data {
|
||||
@ -31,6 +35,10 @@ struct c_can_pci_data {
|
||||
enum c_can_pci_reg_align reg_align;
|
||||
/* Set the frequency */
|
||||
unsigned int freq;
|
||||
/* PCI bar number */
|
||||
int bar;
|
||||
/* Callback for reset */
|
||||
void (*init)(const struct c_can_priv *priv, bool enable);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -63,6 +71,29 @@ static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv,
|
||||
writew(val, priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static u16 c_can_pci_read_reg_32bit(struct c_can_priv *priv,
|
||||
enum reg index)
|
||||
{
|
||||
return (u16)ioread32(priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_pci_write_reg_32bit(struct c_can_priv *priv,
|
||||
enum reg index, u16 val)
|
||||
{
|
||||
iowrite32((u32)val, priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
u32 __iomem *addr = priv->base + PCH_PCI_SOFT_RESET;
|
||||
|
||||
/* write to sw reset register */
|
||||
iowrite32(1, addr);
|
||||
iowrite32(0, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int c_can_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
@ -87,7 +118,8 @@ static int c_can_pci_probe(struct pci_dev *pdev,
|
||||
pci_set_master(pdev);
|
||||
pci_enable_msi(pdev);
|
||||
|
||||
addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
|
||||
addr = pci_iomap(pdev, c_can_pci_data->bar,
|
||||
pci_resource_len(pdev, c_can_pci_data->bar));
|
||||
if (!addr) {
|
||||
dev_err(&pdev->dev,
|
||||
"device has no PCI memory resources, "
|
||||
@ -142,11 +174,17 @@ static int c_can_pci_probe(struct pci_dev *pdev,
|
||||
priv->read_reg = c_can_pci_read_reg_aligned_to_16bit;
|
||||
priv->write_reg = c_can_pci_write_reg_aligned_to_16bit;
|
||||
break;
|
||||
case C_CAN_REG_32:
|
||||
priv->read_reg = c_can_pci_read_reg_32bit;
|
||||
priv->write_reg = c_can_pci_write_reg_32bit;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_free_c_can;
|
||||
}
|
||||
|
||||
priv->raminit = c_can_pci_data->init;
|
||||
|
||||
ret = register_c_can_dev(dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
|
||||
@ -193,6 +231,15 @@ static struct c_can_pci_data c_can_sta2x11= {
|
||||
.type = BOSCH_C_CAN,
|
||||
.reg_align = C_CAN_REG_ALIGN_32,
|
||||
.freq = 52000000, /* 52 Mhz */
|
||||
.bar = 0,
|
||||
};
|
||||
|
||||
static struct c_can_pci_data c_can_pch = {
|
||||
.type = BOSCH_C_CAN,
|
||||
.reg_align = C_CAN_REG_32,
|
||||
.freq = 50000000, /* 50 MHz */
|
||||
.init = c_can_pci_reset_pch,
|
||||
.bar = 1,
|
||||
};
|
||||
|
||||
#define C_CAN_ID(_vend, _dev, _driverdata) { \
|
||||
@ -202,6 +249,8 @@ static struct c_can_pci_data c_can_sta2x11= {
|
||||
static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = {
|
||||
C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN,
|
||||
c_can_sta2x11),
|
||||
C_CAN_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_CAN,
|
||||
c_can_pch),
|
||||
{},
|
||||
};
|
||||
static struct pci_driver c_can_pci_driver = {
|
||||
|
@ -556,15 +556,6 @@ failed:
|
||||
/*
|
||||
* netdev sysfs
|
||||
*/
|
||||
static ssize_t show_channel(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_device *ndev = to_net_dev(dev);
|
||||
struct softing_priv *priv = netdev2softing(ndev);
|
||||
|
||||
return sprintf(buf, "%i\n", priv->index);
|
||||
}
|
||||
|
||||
static ssize_t show_chip(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -609,12 +600,10 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
static const DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
|
||||
static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL);
|
||||
static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
|
||||
|
||||
static const struct attribute *const netdev_sysfs_attrs[] = {
|
||||
&dev_attr_channel.attr,
|
||||
&dev_attr_chip.attr,
|
||||
&dev_attr_output.attr,
|
||||
NULL,
|
||||
@ -679,17 +668,20 @@ static int softing_netdev_register(struct net_device *netdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
netdev->sysfs_groups[0] = &netdev_sysfs_group;
|
||||
ret = register_candev(netdev);
|
||||
if (ret) {
|
||||
dev_alert(&netdev->dev, "register failed\n");
|
||||
return ret;
|
||||
}
|
||||
if (sysfs_create_group(&netdev->dev.kobj, &netdev_sysfs_group) < 0)
|
||||
netdev_alert(netdev, "sysfs group failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void softing_netdev_cleanup(struct net_device *netdev)
|
||||
{
|
||||
sysfs_remove_group(&netdev->dev.kobj, &netdev_sysfs_group);
|
||||
unregister_candev(netdev);
|
||||
free_candev(netdev);
|
||||
}
|
||||
@ -721,8 +713,6 @@ DEV_ATTR_RO(firmware_version, id.fw_version);
|
||||
DEV_ATTR_RO_STR(hardware, pdat->name);
|
||||
DEV_ATTR_RO(hardware_version, id.hw_version);
|
||||
DEV_ATTR_RO(license, id.license);
|
||||
DEV_ATTR_RO(frequency, id.freq);
|
||||
DEV_ATTR_RO(txpending, tx.pending);
|
||||
|
||||
static struct attribute *softing_pdev_attrs[] = {
|
||||
&dev_attr_serial.attr,
|
||||
@ -731,8 +721,6 @@ static struct attribute *softing_pdev_attrs[] = {
|
||||
&dev_attr_hardware.attr,
|
||||
&dev_attr_hardware_version.attr,
|
||||
&dev_attr_license.attr,
|
||||
&dev_attr_frequency.attr,
|
||||
&dev_attr_txpending.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
10
drivers/net/can/spi/Kconfig
Normal file
10
drivers/net/can/spi/Kconfig
Normal file
@ -0,0 +1,10 @@
|
||||
menu "CAN SPI interfaces"
|
||||
depends on SPI
|
||||
|
||||
config CAN_MCP251X
|
||||
tristate "Microchip MCP251x SPI CAN controllers"
|
||||
depends on HAS_DMA
|
||||
---help---
|
||||
Driver for the Microchip MCP251x SPI CAN controllers.
|
||||
|
||||
endmenu
|
8
drivers/net/can/spi/Makefile
Normal file
8
drivers/net/can/spi/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# Makefile for the Linux Controller Area Network SPI drivers.
|
||||
#
|
||||
|
||||
|
||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
@ -214,6 +214,8 @@
|
||||
|
||||
#define TX_ECHO_SKB_MAX 1
|
||||
|
||||
#define MCP251X_OST_DELAY_MS (5)
|
||||
|
||||
#define DEVICE_NAME "mcp251x"
|
||||
|
||||
static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
|
||||
@ -624,50 +626,45 @@ static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
|
||||
static int mcp251x_hw_reset(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
u8 reg;
|
||||
int ret;
|
||||
unsigned long timeout;
|
||||
|
||||
/* Wait for oscillator startup timer after power up */
|
||||
mdelay(MCP251X_OST_DELAY_MS);
|
||||
|
||||
priv->spi_tx_buf[0] = INSTRUCTION_RESET;
|
||||
ret = spi_write(spi, priv->spi_tx_buf, 1);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "reset failed: ret = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
ret = mcp251x_spi_trans(spi, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for oscillator startup timer after reset */
|
||||
mdelay(MCP251X_OST_DELAY_MS);
|
||||
|
||||
reg = mcp251x_read_reg(spi, CANSTAT);
|
||||
if ((reg & CANCTRL_REQOP_MASK) != CANCTRL_REQOP_CONF)
|
||||
return -ENODEV;
|
||||
|
||||
/* Wait for reset to finish */
|
||||
timeout = jiffies + HZ;
|
||||
mdelay(10);
|
||||
while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK)
|
||||
!= CANCTRL_REQOP_CONF) {
|
||||
schedule();
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(&spi->dev, "MCP251x didn't"
|
||||
" enter in conf mode after reset\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp251x_hw_probe(struct spi_device *spi)
|
||||
{
|
||||
int st1, st2;
|
||||
u8 ctrl;
|
||||
int ret;
|
||||
|
||||
mcp251x_hw_reset(spi);
|
||||
ret = mcp251x_hw_reset(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Please note that these are "magic values" based on after
|
||||
* reset defaults taken from data sheet which allows us to see
|
||||
* if we really have a chip on the bus (we avoid common all
|
||||
* zeroes or all ones situations)
|
||||
*/
|
||||
st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
|
||||
st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
|
||||
ctrl = mcp251x_read_reg(spi, CANCTRL);
|
||||
|
||||
dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2);
|
||||
dev_dbg(&spi->dev, "CANCTRL 0x%02x\n", ctrl);
|
||||
|
||||
/* Check for power up default values */
|
||||
return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
|
||||
/* Check for power up default value */
|
||||
if ((ctrl & 0x17) != 0x07)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp251x_power_enable(struct regulator *reg, int enable)
|
||||
@ -776,7 +773,6 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
|
||||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
if (priv->after_suspend) {
|
||||
mdelay(10);
|
||||
mcp251x_hw_reset(spi);
|
||||
mcp251x_setup(net, priv, spi);
|
||||
if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
|
||||
@ -1032,8 +1028,8 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
||||
struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
struct net_device *net;
|
||||
struct mcp251x_priv *priv;
|
||||
int freq, ret = -ENODEV;
|
||||
struct clk *clk;
|
||||
int freq, ret;
|
||||
|
||||
clk = devm_clk_get(&spi->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
@ -1076,6 +1072,18 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
||||
priv->net = net;
|
||||
priv->clk = clk;
|
||||
|
||||
spi_set_drvdata(spi, priv);
|
||||
|
||||
/* Configure the SPI bus */
|
||||
spi->bits_per_word = 8;
|
||||
if (mcp251x_is_2510(spi))
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
|
||||
else
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
|
||||
ret = spi_setup(spi);
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
priv->power = devm_regulator_get(&spi->dev, "vdd");
|
||||
priv->transceiver = devm_regulator_get(&spi->dev, "xceiver");
|
||||
if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
|
||||
@ -1088,8 +1096,6 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
spi_set_drvdata(spi, priv);
|
||||
|
||||
priv->spi = spi;
|
||||
mutex_init(&priv->mcp_lock);
|
||||
|
||||
@ -1134,20 +1140,11 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
||||
|
||||
SET_NETDEV_DEV(net, &spi->dev);
|
||||
|
||||
/* Configure the SPI bus */
|
||||
spi->mode = spi->mode ? : SPI_MODE_0;
|
||||
if (mcp251x_is_2510(spi))
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
|
||||
else
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
|
||||
spi->bits_per_word = 8;
|
||||
spi_setup(spi);
|
||||
|
||||
/* Here is OK to not lock the MCP, no one knows about it yet */
|
||||
if (!mcp251x_hw_probe(spi)) {
|
||||
ret = -ENODEV;
|
||||
ret = mcp251x_hw_probe(spi);
|
||||
if (ret)
|
||||
goto error_probe;
|
||||
}
|
||||
|
||||
mcp251x_hw_sleep(spi);
|
||||
|
||||
ret = register_candev(net);
|
||||
@ -1156,7 +1153,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
||||
|
||||
devm_can_led_init(net);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
error_probe:
|
||||
if (mcp251x_enable_dma)
|
@ -19,7 +19,7 @@ config CAN_KVASER_USB
|
||||
This driver adds support for Kvaser CAN/USB devices like Kvaser
|
||||
Leaf Light.
|
||||
|
||||
The driver gives support for the following devices:
|
||||
The driver provides support for the following devices:
|
||||
- Kvaser Leaf Light
|
||||
- Kvaser Leaf Professional HS
|
||||
- Kvaser Leaf SemiPro HS
|
||||
@ -36,6 +36,8 @@ config CAN_KVASER_USB
|
||||
- Kvaser Leaf Light "China"
|
||||
- Kvaser BlackBird SemiPro
|
||||
- Kvaser USBcan R
|
||||
- Kvaser Leaf Light v2
|
||||
- Kvaser Mini PCI Express HS
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
@ -53,6 +53,8 @@
|
||||
#define USB_OEM_MERCURY_PRODUCT_ID 34
|
||||
#define USB_OEM_LEAF_PRODUCT_ID 35
|
||||
#define USB_CAN_R_PRODUCT_ID 39
|
||||
#define USB_LEAF_LITE_V2_PRODUCT_ID 288
|
||||
#define USB_MINI_PCIE_HS_PRODUCT_ID 289
|
||||
|
||||
/* USB devices features */
|
||||
#define KVASER_HAS_SILENT_MODE BIT(0)
|
||||
@ -356,6 +358,8 @@ static const struct usb_device_id kvaser_usb_table[] = {
|
||||
.driver_info = KVASER_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
|
||||
.driver_info = KVASER_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
|
||||
@ -379,38 +383,43 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
|
||||
void *buf;
|
||||
int actual_len;
|
||||
int err;
|
||||
int pos = 0;
|
||||
int pos;
|
||||
unsigned long to = jiffies + msecs_to_jiffies(USB_RECV_TIMEOUT);
|
||||
|
||||
buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
err = usb_bulk_msg(dev->udev,
|
||||
usb_rcvbulkpipe(dev->udev,
|
||||
dev->bulk_in->bEndpointAddress),
|
||||
buf, RX_BUFFER_SIZE, &actual_len,
|
||||
USB_RECV_TIMEOUT);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
while (pos <= actual_len - MSG_HEADER_LEN) {
|
||||
tmp = buf + pos;
|
||||
|
||||
if (!tmp->len)
|
||||
break;
|
||||
|
||||
if (pos + tmp->len > actual_len) {
|
||||
dev_err(dev->udev->dev.parent, "Format error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp->id == id) {
|
||||
memcpy(msg, tmp, tmp->len);
|
||||
do {
|
||||
err = usb_bulk_msg(dev->udev,
|
||||
usb_rcvbulkpipe(dev->udev,
|
||||
dev->bulk_in->bEndpointAddress),
|
||||
buf, RX_BUFFER_SIZE, &actual_len,
|
||||
USB_RECV_TIMEOUT);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
}
|
||||
|
||||
pos += tmp->len;
|
||||
}
|
||||
pos = 0;
|
||||
while (pos <= actual_len - MSG_HEADER_LEN) {
|
||||
tmp = buf + pos;
|
||||
|
||||
if (!tmp->len)
|
||||
break;
|
||||
|
||||
if (pos + tmp->len > actual_len) {
|
||||
dev_err(dev->udev->dev.parent,
|
||||
"Format error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp->id == id) {
|
||||
memcpy(msg, tmp, tmp->len);
|
||||
goto end;
|
||||
}
|
||||
|
||||
pos += tmp->len;
|
||||
}
|
||||
} while (time_before(jiffies, to));
|
||||
|
||||
err = -EINVAL;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user