diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c index c30f0e6f1048..d42a6a7396f2 100644 --- a/drivers/net/can/cc770/cc770.c +++ b/drivers/net/can/cc770/cc770.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include "cc770.h" diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index b21523ddff3c..b60d6c5f29a0 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -43,12 +43,33 @@ config CAN_EMS_PCI CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de). +config CAN_PEAK_PCMCIA + tristate "PEAK PCAN-PC Card" + depends on PCMCIA + ---help--- + This driver is for the PCAN-PC Card PCMCIA adapter (1 or 2 channels) + from PEAK-System (http://www.peak-system.com). To compile this + driver as a module, choose M here: the module will be called + peak_pcmcia. + config CAN_PEAK_PCI - tristate "PEAK PCAN PCI/PCIe Cards" + tristate "PEAK PCAN-PCI/PCIe/miniPCI Cards" depends on PCI ---help--- - This driver is for the PCAN PCI/PCIe cards (1, 2, 3 or 4 channels) - from PEAK Systems (http://www.peak-system.com). + This driver is for the PCAN-PCI/PCIe/miniPCI cards + (1, 2, 3 or 4 channels) from PEAK-System Technik + (http://www.peak-system.com). + +config CAN_PEAK_PCIEC + bool "PEAK PCAN-ExpressCard Cards" + depends on CAN_PEAK_PCI + select I2C + select I2C_ALGOBIT + default y + ---help--- + Say Y here if you want to use a PCAN-ExpressCard from PEAK-System + Technik. This will also automatically select I2C and I2C_ALGO + configuration options. config CAN_KVASER_PCI tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards" diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 0604f240c8b1..b3d05cbfec36 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o obj-$(CONFIG_CAN_EMS_PCMCIA) += ems_pcmcia.o obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o +obj-$(CONFIG_CAN_PEAK_PCMCIA) += peak_pcmcia.o obj-$(CONFIG_CAN_PEAK_PCI) += peak_pci.o obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o obj-$(CONFIG_CAN_TSCAN1) += tscan1.o diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index 214795945bc4..5f92b865f64b 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2007, 2011 Wolfgang Grandegger + * Copyright (C) 2012 Stephane Grosjean * * Derived from the PCAN project file driver/src/pcan_pci.c: * @@ -13,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include @@ -26,22 +23,26 @@ #include #include #include +#include +#include #include #include #include "sja1000.h" MODULE_AUTHOR("Wolfgang Grandegger "); -MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI/PCIe cards"); -MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe CAN card"); +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCI family cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCI/PCIe/PCIeC miniPCI CAN cards"); MODULE_LICENSE("GPL v2"); #define DRV_NAME "peak_pci" +struct peak_pciec_card; struct peak_pci_chan { void __iomem *cfg_base; /* Common for all channels */ struct net_device *prev_dev; /* Chain of network devices */ u16 icr_mask; /* Interrupt mask for fast ack */ + struct peak_pciec_card *pciec_card; /* only for PCIeC LEDs */ }; #define PEAK_PCI_CAN_CLOCK (16000000 / 2) @@ -61,16 +62,464 @@ struct peak_pci_chan { #define PEAK_PCI_VENDOR_ID 0x001C /* The PCI device and vendor IDs */ #define PEAK_PCI_DEVICE_ID 0x0001 /* for PCI/PCIe slot cards */ +#define PEAK_PCIEC_DEVICE_ID 0x0002 /* for ExpressCard slot cards */ +#define PEAK_PCIE_DEVICE_ID 0x0003 /* for nextgen PCIe slot cards */ +#define PEAK_MPCI_DEVICE_ID 0x0008 /* The miniPCI slot cards */ -static const u16 peak_pci_icr_masks[] = {0x02, 0x01, 0x40, 0x80}; +#define PEAK_PCI_CHAN_MAX 4 + +static const u16 peak_pci_icr_masks[PEAK_PCI_CHAN_MAX] = { + 0x02, 0x01, 0x40, 0x80 +}; static DEFINE_PCI_DEVICE_TABLE(peak_pci_tbl) = { {PEAK_PCI_VENDOR_ID, PEAK_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_PCIE_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {PEAK_PCI_VENDOR_ID, PEAK_MPCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, +#ifdef CONFIG_CAN_PEAK_PCIEC + {PEAK_PCI_VENDOR_ID, PEAK_PCIEC_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, +#endif {0,} }; MODULE_DEVICE_TABLE(pci, peak_pci_tbl); +#ifdef CONFIG_CAN_PEAK_PCIEC +/* + * PCAN-ExpressCard needs I2C bit-banging configuration option. + */ + +/* GPIOICR byte access offsets */ +#define PITA_GPOUT 0x18 /* GPx output value */ +#define PITA_GPIN 0x19 /* GPx input value */ +#define PITA_GPOEN 0x1A /* configure GPx as ouput pin */ + +/* I2C GP bits */ +#define PITA_GPIN_SCL 0x01 /* Serial Clock Line */ +#define PITA_GPIN_SDA 0x04 /* Serial DAta line */ + +#define PCA9553_1_SLAVEADDR (0xC4 >> 1) + +/* PCA9553 LS0 fields values */ +enum { + PCA9553_LOW, + PCA9553_HIGHZ, + PCA9553_PWM0, + PCA9553_PWM1 +}; + +/* LEDs control */ +#define PCA9553_ON PCA9553_LOW +#define PCA9553_OFF PCA9553_HIGHZ +#define PCA9553_SLOW PCA9553_PWM0 +#define PCA9553_FAST PCA9553_PWM1 + +#define PCA9553_LED(c) (1 << (c)) +#define PCA9553_LED_STATE(s, c) ((s) << ((c) << 1)) + +#define PCA9553_LED_ON(c) PCA9553_LED_STATE(PCA9553_ON, c) +#define PCA9553_LED_OFF(c) PCA9553_LED_STATE(PCA9553_OFF, c) +#define PCA9553_LED_SLOW(c) PCA9553_LED_STATE(PCA9553_SLOW, c) +#define PCA9553_LED_FAST(c) PCA9553_LED_STATE(PCA9553_FAST, c) +#define PCA9553_LED_MASK(c) PCA9553_LED_STATE(0x03, c) + +#define PCA9553_LED_OFF_ALL (PCA9553_LED_OFF(0) | PCA9553_LED_OFF(1)) + +#define PCA9553_LS0_INIT 0x40 /* initial value (!= from 0x00) */ + +struct peak_pciec_chan { + struct net_device *netdev; + unsigned long prev_rx_bytes; + unsigned long prev_tx_bytes; +}; + +struct peak_pciec_card { + void __iomem *cfg_base; /* Common for all channels */ + void __iomem *reg_base; /* first channel base address */ + u8 led_cache; /* leds state cache */ + + /* PCIExpressCard i2c data */ + struct i2c_algo_bit_data i2c_bit; + struct i2c_adapter led_chip; + struct delayed_work led_work; /* led delayed work */ + int chan_count; + struct peak_pciec_chan channel[PEAK_PCI_CHAN_MAX]; +}; + +/* "normal" pci register write callback is overloaded for leds control */ +static void peak_pci_write_reg(const struct sja1000_priv *priv, + int port, u8 val); + +static inline void pita_set_scl_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SCL; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static inline void pita_set_sda_highz(struct peak_pciec_card *card) +{ + u8 gp_outen = readb(card->cfg_base + PITA_GPOEN) & ~PITA_GPIN_SDA; + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void peak_pciec_init_pita_gpio(struct peak_pciec_card *card) +{ + /* raise SCL & SDA GPIOs to high-Z */ + pita_set_scl_highz(card); + pita_set_sda_highz(card); +} + +static void pita_setsda(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output sda always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SDA; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output sda with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SDA; + else + gp_outen |= PITA_GPIN_SDA; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static void pita_setscl(void *data, int state) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + u8 gp_out, gp_outen; + + /* set output scl always to 0 */ + gp_out = readb(card->cfg_base + PITA_GPOUT) & ~PITA_GPIN_SCL; + writeb(gp_out, card->cfg_base + PITA_GPOUT); + + /* control output scl with GPOEN */ + gp_outen = readb(card->cfg_base + PITA_GPOEN); + if (state) + gp_outen &= ~PITA_GPIN_SCL; + else + gp_outen |= PITA_GPIN_SCL; + + writeb(gp_outen, card->cfg_base + PITA_GPOEN); +} + +static int pita_getsda(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_sda_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SDA) ? 1 : 0; +} + +static int pita_getscl(void *data) +{ + struct peak_pciec_card *card = (struct peak_pciec_card *)data; + + /* set tristate */ + pita_set_scl_highz(card); + + return (readb(card->cfg_base + PITA_GPIN) & PITA_GPIN_SCL) ? 1 : 0; +} + +/* + * write commands to the LED chip though the I2C-bus of the PCAN-PCIeC + */ +static int peak_pciec_write_pca9553(struct peak_pciec_card *card, + u8 offset, u8 data) +{ + u8 buffer[2] = { + offset, + data + }; + struct i2c_msg msg = { + .addr = PCA9553_1_SLAVEADDR, + .len = 2, + .buf = buffer, + }; + int ret; + + /* cache led mask */ + if ((offset == 5) && (data == card->led_cache)) + return 0; + + ret = i2c_transfer(&card->led_chip, &msg, 1); + if (ret < 0) + return ret; + + if (offset == 5) + card->led_cache = data; + + return 0; +} + +/* + * delayed work callback used to control the LEDs + */ +static void peak_pciec_led_work(struct work_struct *work) +{ + struct peak_pciec_card *card = + container_of(work, struct peak_pciec_card, led_work.work); + struct net_device *netdev; + u8 new_led = card->led_cache; + int i, up_count = 0; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_ON(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_SLOW(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_FAST(i); + } + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); + + /* restart timer (except if no more configured channels) */ + if (up_count) + schedule_delayed_work(&card->led_work, HZ); +} + +/* + * set LEDs blinking state + */ +static void peak_pciec_set_leds(struct peak_pciec_card *card, u8 led_mask, u8 s) +{ + u8 new_led = card->led_cache; + int i; + + /* first check what is to do */ + for (i = 0; i < card->chan_count; i++) + if (led_mask & PCA9553_LED(i)) { + new_led &= ~PCA9553_LED_MASK(i); + new_led |= PCA9553_LED_STATE(s, i); + } + + /* check if LS0 settings changed, only update i2c if so */ + peak_pciec_write_pca9553(card, 5, new_led); +} + +/* + * start one second delayed work to control LEDs + */ +static void peak_pciec_start_led_work(struct peak_pciec_card *card) +{ + if (!delayed_work_pending(&card->led_work)) + schedule_delayed_work(&card->led_work, HZ); +} + +/* + * stop LEDs delayed work + */ +static void peak_pciec_stop_led_work(struct peak_pciec_card *card) +{ + cancel_delayed_work_sync(&card->led_work); +} + +/* + * initialize the PCA9553 4-bit I2C-bus LED chip + */ +static int peak_pciec_init_leds(struct peak_pciec_card *card) +{ + int err; + + /* prescaler for frequency 0: "SLOW" = 1 Hz = "44" */ + err = peak_pciec_write_pca9553(card, 1, 44 / 1); + if (err) + return err; + + /* duty cycle 0: 50% */ + err = peak_pciec_write_pca9553(card, 2, 0x80); + if (err) + return err; + + /* prescaler for frequency 1: "FAST" = 5 Hz */ + err = peak_pciec_write_pca9553(card, 3, 44 / 5); + if (err) + return err; + + /* duty cycle 1: 50% */ + err = peak_pciec_write_pca9553(card, 4, 0x80); + if (err) + return err; + + /* switch LEDs to initial state */ + return peak_pciec_write_pca9553(card, 5, PCA9553_LS0_INIT); +} + +/* + * restore LEDs state to off peak_pciec_leds_exit + */ +static void peak_pciec_leds_exit(struct peak_pciec_card *card) +{ + /* switch LEDs to off */ + peak_pciec_write_pca9553(card, 5, PCA9553_LED_OFF_ALL); +} + +/* + * normal write sja1000 register method overloaded to catch when controller + * is started or stopped, to control leds + */ +static void peak_pciec_write_reg(const struct sja1000_priv *priv, + int port, u8 val) +{ + struct peak_pci_chan *chan = priv->priv; + struct peak_pciec_card *card = chan->pciec_card; + int c = (priv->reg_base - card->reg_base) / PEAK_PCI_CHAN_SIZE; + + /* sja1000 register changes control the leds state */ + if (port == REG_MOD) + switch (val) { + case MOD_RM: + /* Reset Mode: set led on */ + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_ON); + break; + case 0x00: + /* Normal Mode: led slow blinking and start led timer */ + peak_pciec_set_leds(card, PCA9553_LED(c), PCA9553_SLOW); + peak_pciec_start_led_work(card); + break; + default: + break; + } + + /* call base function */ + peak_pci_write_reg(priv, port, val); +} + +static struct i2c_algo_bit_data peak_pciec_i2c_bit_ops = { + .setsda = pita_setsda, + .setscl = pita_setscl, + .getsda = pita_getsda, + .getscl = pita_getscl, + .udelay = 10, + .timeout = HZ, +}; + +static int peak_pciec_probe(struct pci_dev *pdev, struct net_device *dev) +{ + struct sja1000_priv *priv = netdev_priv(dev); + struct peak_pci_chan *chan = priv->priv; + struct peak_pciec_card *card; + int err; + + /* copy i2c object address from 1st channel */ + if (chan->prev_dev) { + struct sja1000_priv *prev_priv = netdev_priv(chan->prev_dev); + struct peak_pci_chan *prev_chan = prev_priv->priv; + + card = prev_chan->pciec_card; + if (!card) + return -ENODEV; + + /* channel is the first one: do the init part */ + } else { + /* create the bit banging I2C adapter structure */ + card = kzalloc(sizeof(struct peak_pciec_card), GFP_KERNEL); + if (!card) { + dev_err(&pdev->dev, + "failed allocating memory for i2c chip\n"); + return -ENOMEM; + } + + card->cfg_base = chan->cfg_base; + card->reg_base = priv->reg_base; + + card->led_chip.owner = THIS_MODULE; + card->led_chip.dev.parent = &pdev->dev; + card->led_chip.algo_data = &card->i2c_bit; + strncpy(card->led_chip.name, "peak_i2c", + sizeof(card->led_chip.name)); + + card->i2c_bit = peak_pciec_i2c_bit_ops; + card->i2c_bit.udelay = 10; + card->i2c_bit.timeout = HZ; + card->i2c_bit.data = card; + + peak_pciec_init_pita_gpio(card); + + err = i2c_bit_add_bus(&card->led_chip); + if (err) { + dev_err(&pdev->dev, "i2c init failed\n"); + goto pciec_init_err_1; + } + + err = peak_pciec_init_leds(card); + if (err) { + dev_err(&pdev->dev, "leds hardware init failed\n"); + goto pciec_init_err_2; + } + + INIT_DELAYED_WORK(&card->led_work, peak_pciec_led_work); + /* PCAN-ExpressCard needs its own callback for leds */ + priv->write_reg = peak_pciec_write_reg; + } + + chan->pciec_card = card; + card->channel[card->chan_count++].netdev = dev; + + return 0; + +pciec_init_err_2: + i2c_del_adapter(&card->led_chip); + +pciec_init_err_1: + peak_pciec_init_pita_gpio(card); + kfree(card); + + return err; +} + +static void peak_pciec_remove(struct peak_pciec_card *card) +{ + peak_pciec_stop_led_work(card); + peak_pciec_leds_exit(card); + i2c_del_adapter(&card->led_chip); + peak_pciec_init_pita_gpio(card); + kfree(card); +} + +#else /* CONFIG_CAN_PEAK_PCIEC */ + +/* + * Placebo functions when PCAN-ExpressCard support is not selected + */ +static inline int peak_pciec_probe(struct pci_dev *pdev, struct net_device *dev) +{ + return -ENODEV; +} + +static inline void peak_pciec_remove(struct peak_pciec_card *card) +{ +} +#endif /* CONFIG_CAN_PEAK_PCIEC */ + static u8 peak_pci_read_reg(const struct sja1000_priv *priv, int port) { return readb(priv->reg_base + (port << 2)); @@ -188,17 +637,31 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, SET_NETDEV_DEV(dev, &pdev->dev); - err = register_sja1000dev(dev); - if (err) { - dev_err(&pdev->dev, "failed to register device\n"); - free_sja1000dev(dev); - goto failure_remove_channels; - } - /* Create chain of SJA1000 devices */ chan->prev_dev = pci_get_drvdata(pdev); pci_set_drvdata(pdev, dev); + /* + * PCAN-ExpressCard needs some additional i2c init. + * This must be done *before* register_sja1000dev() but + * *after* devices linkage + */ + if (pdev->device == PEAK_PCIEC_DEVICE_ID) { + err = peak_pciec_probe(pdev, dev); + if (err) { + dev_err(&pdev->dev, + "failed to probe device (err %d)\n", + err); + goto failure_free_dev; + } + } + + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "failed to register device\n"); + goto failure_free_dev; + } + dev_info(&pdev->dev, "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", dev->name, priv->reg_base, chan->cfg_base, dev->irq); @@ -209,10 +672,15 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, return 0; +failure_free_dev: + pci_set_drvdata(pdev, chan->prev_dev); + free_sja1000dev(dev); + failure_remove_channels: /* Disable interrupts */ writew(0x0, cfg_base + PITA_ICR + 2); + chan = NULL; for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { unregister_sja1000dev(dev); free_sja1000dev(dev); @@ -220,6 +688,10 @@ failure_remove_channels: chan = priv->priv; } + /* free any PCIeC resources too */ + if (chan && chan->pciec_card) + peak_pciec_remove(chan->pciec_card); + pci_iounmap(pdev, reg_base); failure_unmap_cfg_base: @@ -251,8 +723,13 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) unregister_sja1000dev(dev); free_sja1000dev(dev); dev = chan->prev_dev; - if (!dev) + + if (!dev) { + /* do that only for first channel */ + if (chan->pciec_card) + peak_pciec_remove(chan->pciec_card); break; + } priv = netdev_priv(dev); chan = priv->priv; } diff --git a/drivers/net/can/sja1000/peak_pcmcia.c b/drivers/net/can/sja1000/peak_pcmcia.c new file mode 100644 index 000000000000..ec6bd9d1b2af --- /dev/null +++ b/drivers/net/can/sja1000/peak_pcmcia.c @@ -0,0 +1,753 @@ +/* + * Copyright (C) 2010-2012 Stephane Grosjean + * + * CAN driver for PEAK-System PCAN-PC Card + * Derived from the PCAN project file driver/src/pcan_pccard.c + * Copyright (C) 2006-2010 PEAK System-Technik GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sja1000.h" + +MODULE_AUTHOR("Stephane Grosjean "); +MODULE_DESCRIPTION("CAN driver for PEAK-System PCAN-PC Cards"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN-PC Card"); + +/* PEAK-System PCMCIA driver name */ +#define PCC_NAME "peak_pcmcia" + +#define PCC_CHAN_MAX 2 + +#define PCC_CAN_CLOCK (16000000 / 2) + +#define PCC_MANF_ID 0x0377 +#define PCC_CARD_ID 0x0001 + +#define PCC_CHAN_SIZE 0x20 +#define PCC_CHAN_OFF(c) ((c) * PCC_CHAN_SIZE) +#define PCC_COMN_OFF (PCC_CHAN_OFF(PCC_CHAN_MAX)) +#define PCC_COMN_SIZE 0x40 + +/* common area registers */ +#define PCC_CCR 0x00 +#define PCC_CSR 0x02 +#define PCC_CPR 0x04 +#define PCC_SPI_DIR 0x06 +#define PCC_SPI_DOR 0x08 +#define PCC_SPI_ADR 0x0a +#define PCC_SPI_IR 0x0c +#define PCC_FW_MAJOR 0x10 +#define PCC_FW_MINOR 0x12 + +/* CCR bits */ +#define PCC_CCR_CLK_16 0x00 +#define PCC_CCR_CLK_10 0x01 +#define PCC_CCR_CLK_21 0x02 +#define PCC_CCR_CLK_8 0x03 +#define PCC_CCR_CLK_MASK PCC_CCR_CLK_8 + +#define PCC_CCR_RST_CHAN(c) (0x01 << ((c) + 2)) +#define PCC_CCR_RST_ALL (PCC_CCR_RST_CHAN(0) | PCC_CCR_RST_CHAN(1)) +#define PCC_CCR_RST_MASK PCC_CCR_RST_ALL + +/* led selection bits */ +#define PCC_LED(c) (1 << (c)) +#define PCC_LED_ALL (PCC_LED(0) | PCC_LED(1)) + +/* led state value */ +#define PCC_LED_ON 0x00 +#define PCC_LED_FAST 0x01 +#define PCC_LED_SLOW 0x02 +#define PCC_LED_OFF 0x03 + +#define PCC_CCR_LED_CHAN(s, c) ((s) << (((c) + 2) << 1)) + +#define PCC_CCR_LED_ON_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_ON, c) +#define PCC_CCR_LED_FAST_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_FAST, c) +#define PCC_CCR_LED_SLOW_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_SLOW, c) +#define PCC_CCR_LED_OFF_CHAN(c) PCC_CCR_LED_CHAN(PCC_LED_OFF, c) +#define PCC_CCR_LED_MASK_CHAN(c) PCC_CCR_LED_OFF_CHAN(c) +#define PCC_CCR_LED_OFF_ALL (PCC_CCR_LED_OFF_CHAN(0) | \ + PCC_CCR_LED_OFF_CHAN(1)) +#define PCC_CCR_LED_MASK PCC_CCR_LED_OFF_ALL + +#define PCC_CCR_INIT (PCC_CCR_CLK_16 | PCC_CCR_RST_ALL | PCC_CCR_LED_OFF_ALL) + +/* CSR bits */ +#define PCC_CSR_SPI_BUSY 0x04 + +/* time waiting for SPI busy (prevent from infinite loop) */ +#define PCC_SPI_MAX_BUSY_WAIT_MS 3 + +/* max count of reading the SPI status register waiting for a change */ +/* (prevent from infinite loop) */ +#define PCC_WRITE_MAX_LOOP 1000 + +/* max nb of int handled by that isr in one shot (prevent from infinite loop) */ +#define PCC_ISR_MAX_LOOP 10 + +/* EEPROM chip instruction set */ +/* note: EEPROM Read/Write instructions include A8 bit */ +#define PCC_EEP_WRITE(a) (0x02 | (((a) & 0x100) >> 5)) +#define PCC_EEP_READ(a) (0x03 | (((a) & 0x100) >> 5)) +#define PCC_EEP_WRDI 0x04 /* EEPROM Write Disable */ +#define PCC_EEP_RDSR 0x05 /* EEPROM Read Status Register */ +#define PCC_EEP_WREN 0x06 /* EEPROM Write Enable */ + +/* EEPROM Status Register bits */ +#define PCC_EEP_SR_WEN 0x02 /* EEPROM SR Write Enable bit */ +#define PCC_EEP_SR_WIP 0x01 /* EEPROM SR Write In Progress bit */ + +/* + * The board configuration is probably following: + * RX1 is connected to ground. + * TX1 is not connected. + * CLKO is not connected. + * Setting the OCR register to 0xDA is a good idea. + * This means normal output mode, push-pull and the correct polarity. + */ +#define PCC_OCR (OCR_TX0_PUSHPULL | OCR_TX1_PUSHPULL) + +/* + * In the CDR register, you should set CBP to 1. + * You will probably also want to set the clock divider value to 7 + * (meaning direct oscillator output) because the second SJA1000 chip + * is driven by the first one CLKOUT output. + */ +#define PCC_CDR (CDR_CBP | CDR_CLKOUT_MASK) + +struct pcan_channel { + struct net_device *netdev; + unsigned long prev_rx_bytes; + unsigned long prev_tx_bytes; +}; + +/* PCAN-PC Card private structure */ +struct pcan_pccard { + struct pcmcia_device *pdev; + int chan_count; + struct pcan_channel channel[PCC_CHAN_MAX]; + u8 ccr; + u8 fw_major; + u8 fw_minor; + void __iomem *ioport_addr; + struct timer_list led_timer; +}; + +static struct pcmcia_device_id pcan_table[] = { + PCMCIA_DEVICE_MANF_CARD(PCC_MANF_ID, PCC_CARD_ID), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, pcan_table); + +static void pcan_set_leds(struct pcan_pccard *card, u8 mask, u8 state); + +/* + * start timer which controls leds state + */ +static void pcan_start_led_timer(struct pcan_pccard *card) +{ + if (!timer_pending(&card->led_timer)) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * stop the timer which controls leds state + */ +static void pcan_stop_led_timer(struct pcan_pccard *card) +{ + del_timer_sync(&card->led_timer); +} + +/* + * read a sja1000 register + */ +static u8 pcan_read_canreg(const struct sja1000_priv *priv, int port) +{ + return ioread8(priv->reg_base + port); +} + +/* + * write a sja1000 register + */ +static void pcan_write_canreg(const struct sja1000_priv *priv, int port, u8 v) +{ + struct pcan_pccard *card = priv->priv; + int c = (priv->reg_base - card->ioport_addr) / PCC_CHAN_SIZE; + + /* sja1000 register changes control the leds state */ + if (port == REG_MOD) + switch (v) { + case MOD_RM: + /* Reset Mode: set led on */ + pcan_set_leds(card, PCC_LED(c), PCC_LED_ON); + break; + case 0x00: + /* Normal Mode: led slow blinking and start led timer */ + pcan_set_leds(card, PCC_LED(c), PCC_LED_SLOW); + pcan_start_led_timer(card); + break; + default: + break; + } + + iowrite8(v, priv->reg_base + port); +} + +/* + * read a register from the common area + */ +static u8 pcan_read_reg(struct pcan_pccard *card, int port) +{ + return ioread8(card->ioport_addr + PCC_COMN_OFF + port); +} + +/* + * write a register into the common area + */ +static void pcan_write_reg(struct pcan_pccard *card, int port, u8 v) +{ + /* cache ccr value */ + if (port == PCC_CCR) { + if (card->ccr == v) + return; + card->ccr = v; + } + + iowrite8(v, card->ioport_addr + PCC_COMN_OFF + port); +} + +/* + * check whether the card is present by checking its fw version numbers + * against values read at probing time. + */ +static inline int pcan_pccard_present(struct pcan_pccard *card) +{ + return ((pcan_read_reg(card, PCC_FW_MAJOR) == card->fw_major) && + (pcan_read_reg(card, PCC_FW_MINOR) == card->fw_minor)); +} + +/* + * wait for SPI engine while it is busy + */ +static int pcan_wait_spi_busy(struct pcan_pccard *card) +{ + unsigned long timeout = jiffies + + msecs_to_jiffies(PCC_SPI_MAX_BUSY_WAIT_MS) + 1; + + /* be sure to read status at least once after sleeping */ + while (pcan_read_reg(card, PCC_CSR) & PCC_CSR_SPI_BUSY) { + if (time_after(jiffies, timeout)) + return -EBUSY; + schedule(); + } + + return 0; +} + +/* + * write data in device eeprom + */ +static int pcan_write_eeprom(struct pcan_pccard *card, u16 addr, u8 v) +{ + u8 status; + int err, i; + + /* write instruction enabling write */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WREN); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* wait until write enabled */ + for (i = 0; i < PCC_WRITE_MAX_LOOP; i++) { + /* write instruction reading the status register */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_RDSR); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* get status register value and check write enable bit */ + status = pcan_read_reg(card, PCC_SPI_DIR); + if (status & PCC_EEP_SR_WEN) + break; + } + + if (i >= PCC_WRITE_MAX_LOOP) { + dev_err(&card->pdev->dev, + "stop waiting to be allowed to write in eeprom\n"); + return -EIO; + } + + /* set address and data */ + pcan_write_reg(card, PCC_SPI_ADR, addr & 0xff); + pcan_write_reg(card, PCC_SPI_DOR, v); + + /* + * write instruction with bit[3] set according to address value: + * if addr refers to upper half of the memory array: bit[3] = 1 + */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WRITE(addr)); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* wait while write in progress */ + for (i = 0; i < PCC_WRITE_MAX_LOOP; i++) { + /* write instruction reading the status register */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_RDSR); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + /* get status register value and check write in progress bit */ + status = pcan_read_reg(card, PCC_SPI_DIR); + if (!(status & PCC_EEP_SR_WIP)) + break; + } + + if (i >= PCC_WRITE_MAX_LOOP) { + dev_err(&card->pdev->dev, + "stop waiting for write in eeprom to complete\n"); + return -EIO; + } + + /* write instruction disabling write */ + pcan_write_reg(card, PCC_SPI_IR, PCC_EEP_WRDI); + err = pcan_wait_spi_busy(card); + if (err) + goto we_spi_err; + + return 0; + +we_spi_err: + dev_err(&card->pdev->dev, + "stop waiting (spi engine always busy) err %d\n", err); + + return err; +} + +static void pcan_set_leds(struct pcan_pccard *card, u8 led_mask, u8 state) +{ + u8 ccr = card->ccr; + int i; + + for (i = 0; i < card->chan_count; i++) + if (led_mask & PCC_LED(i)) { + /* clear corresponding led bits in ccr */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + /* then set new bits */ + ccr |= PCC_CCR_LED_CHAN(state, i); + } + + /* real write only if something has changed in ccr */ + pcan_write_reg(card, PCC_CCR, ccr); +} + +/* + * enable/disable CAN connectors power + */ +static inline void pcan_set_can_power(struct pcan_pccard *card, int onoff) +{ + int err; + + err = pcan_write_eeprom(card, 0, !!onoff); + if (err) + dev_err(&card->pdev->dev, + "failed setting power %s to can connectors (err %d)\n", + (onoff) ? "on" : "off", err); +} + +/* + * set leds state according to channel activity + */ +static void pcan_led_timer(unsigned long arg) +{ + struct pcan_pccard *card = (struct pcan_pccard *)arg; + struct net_device *netdev; + int i, up_count = 0; + u8 ccr; + + ccr = card->ccr; + for (i = 0; i < card->chan_count; i++) { + /* default is: not configured */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_ON_CHAN(i); + + netdev = card->channel[i].netdev; + if (!netdev || !(netdev->flags & IFF_UP)) + continue; + + up_count++; + + /* no activity (but configured) */ + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_SLOW_CHAN(i); + + /* if bytes counters changed, set fast blinking led */ + if (netdev->stats.rx_bytes != card->channel[i].prev_rx_bytes) { + card->channel[i].prev_rx_bytes = netdev->stats.rx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + if (netdev->stats.tx_bytes != card->channel[i].prev_tx_bytes) { + card->channel[i].prev_tx_bytes = netdev->stats.tx_bytes; + ccr &= ~PCC_CCR_LED_MASK_CHAN(i); + ccr |= PCC_CCR_LED_FAST_CHAN(i); + } + } + + /* write the new leds state */ + pcan_write_reg(card, PCC_CCR, ccr); + + /* restart timer (except if no more configured channels) */ + if (up_count) + mod_timer(&card->led_timer, jiffies + HZ); +} + +/* + * interrupt service routine + */ +static irqreturn_t pcan_isr(int irq, void *dev_id) +{ + struct pcan_pccard *card = dev_id; + int irq_handled; + + /* prevent from infinite loop */ + for (irq_handled = 0; irq_handled < PCC_ISR_MAX_LOOP; irq_handled++) { + /* handle shared interrupt and next loop */ + int nothing_to_handle = 1; + int i; + + /* check interrupt for each channel */ + for (i = 0; i < card->chan_count; i++) { + struct net_device *netdev; + + /* + * check whether the card is present before calling + * sja1000_interrupt() to speed up hotplug detection + */ + if (!pcan_pccard_present(card)) { + /* card unplugged during isr */ + return IRQ_NONE; + } + + /* + * should check whether all or SJA1000_MAX_IRQ + * interrupts have been handled: loop again to be sure. + */ + netdev = card->channel[i].netdev; + if (netdev && + sja1000_interrupt(irq, netdev) == IRQ_HANDLED) + nothing_to_handle = 0; + } + + if (nothing_to_handle) + break; + } + + return (irq_handled) ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * free all resources used by the channels and switch off leds and can power + */ +static void pcan_free_channels(struct pcan_pccard *card) +{ + int i; + u8 led_mask = 0; + + for (i = 0; i < card->chan_count; i++) { + struct net_device *netdev; + char name[IFNAMSIZ]; + + led_mask |= PCC_LED(i); + + netdev = card->channel[i].netdev; + if (!netdev) + continue; + + strncpy(name, netdev->name, IFNAMSIZ); + + unregister_sja1000dev(netdev); + + free_sja1000dev(netdev); + + dev_info(&card->pdev->dev, "%s removed\n", name); + } + + /* do it only if device not removed */ + if (pcan_pccard_present(card)) { + pcan_set_leds(card, led_mask, PCC_LED_OFF); + pcan_set_can_power(card, 0); + } +} + +/* + * check if a CAN controller is present at the specified location + */ +static inline int pcan_channel_present(struct sja1000_priv *priv) +{ + /* make sure SJA1000 is in reset mode */ + pcan_write_canreg(priv, REG_MOD, 1); + pcan_write_canreg(priv, REG_CDR, CDR_PELICAN); + + /* read reset-values */ + if (pcan_read_canreg(priv, REG_CDR) == CDR_PELICAN) + return 1; + + return 0; +} + +static int pcan_add_channels(struct pcan_pccard *card) +{ + struct pcmcia_device *pdev = card->pdev; + int i, err = 0; + u8 ccr = PCC_CCR_INIT; + + /* init common registers (reset channels and leds off) */ + card->ccr = ~ccr; + pcan_write_reg(card, PCC_CCR, ccr); + + /* wait 2ms before unresetting channels */ + mdelay(2); + + ccr &= ~PCC_CCR_RST_ALL; + pcan_write_reg(card, PCC_CCR, ccr); + + /* create one network device per channel detected */ + for (i = 0; i < ARRAY_SIZE(card->channel); i++) { + struct net_device *netdev; + struct sja1000_priv *priv; + + netdev = alloc_sja1000dev(0); + if (!netdev) { + err = -ENOMEM; + break; + } + + /* update linkages */ + priv = netdev_priv(netdev); + priv->priv = card; + SET_NETDEV_DEV(netdev, &pdev->dev); + + priv->irq_flags = IRQF_SHARED; + netdev->irq = pdev->irq; + priv->reg_base = card->ioport_addr + PCC_CHAN_OFF(i); + + /* check if channel is present */ + if (!pcan_channel_present(priv)) { + dev_err(&pdev->dev, "channel %d not present\n", i); + free_sja1000dev(netdev); + continue; + } + + priv->read_reg = pcan_read_canreg; + priv->write_reg = pcan_write_canreg; + priv->can.clock.freq = PCC_CAN_CLOCK; + priv->ocr = PCC_OCR; + priv->cdr = PCC_CDR; + + /* Neither a slave device distributes the clock */ + if (i > 0) + priv->cdr |= CDR_CLK_OFF; + + priv->flags |= SJA1000_CUSTOM_IRQ_HANDLER; + + /* register SJA1000 device */ + err = register_sja1000dev(netdev); + if (err) { + free_sja1000dev(netdev); + continue; + } + + card->channel[i].netdev = netdev; + card->chan_count++; + + /* set corresponding led on in the new ccr */ + ccr &= ~PCC_CCR_LED_OFF_CHAN(i); + + dev_info(&pdev->dev, + "%s on channel %d at 0x%p irq %d\n", + netdev->name, i, priv->reg_base, pdev->irq); + } + + /* write new ccr (change leds state) */ + pcan_write_reg(card, PCC_CCR, ccr); + + return err; +} + +static int pcan_conf_check(struct pcmcia_device *pdev, void *priv_data) +{ + pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; /* only */ + pdev->io_lines = 10; + + /* This reserves IO space but doesn't actually enable it */ + return pcmcia_request_io(pdev); +} + +/* + * free all resources used by the device + */ +static void pcan_free(struct pcmcia_device *pdev) +{ + struct pcan_pccard *card = pdev->priv; + + if (!card) + return; + + free_irq(pdev->irq, card); + pcan_stop_led_timer(card); + + pcan_free_channels(card); + + ioport_unmap(card->ioport_addr); + + kfree(card); + pdev->priv = NULL; +} + +/* + * setup PCMCIA socket and probe for PEAK-System PC-CARD + */ +static int __devinit pcan_probe(struct pcmcia_device *pdev) +{ + struct pcan_pccard *card; + int err; + + pdev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + err = pcmcia_loop_config(pdev, pcan_conf_check, NULL); + if (err) { + dev_err(&pdev->dev, "pcmcia_loop_config() error %d\n", err); + goto probe_err_1; + } + + if (!pdev->irq) { + dev_err(&pdev->dev, "no irq assigned\n"); + err = -ENODEV; + goto probe_err_1; + } + + err = pcmcia_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "pcmcia_enable_device failed err=%d\n", + err); + goto probe_err_1; + } + + card = kzalloc(sizeof(struct pcan_pccard), GFP_KERNEL); + if (!card) { + dev_err(&pdev->dev, "couldn't allocate card memory\n"); + err = -ENOMEM; + goto probe_err_2; + } + + card->pdev = pdev; + pdev->priv = card; + + /* sja1000 api uses iomem */ + card->ioport_addr = ioport_map(pdev->resource[0]->start, + resource_size(pdev->resource[0])); + if (!card->ioport_addr) { + dev_err(&pdev->dev, "couldn't map io port into io memory\n"); + err = -ENOMEM; + goto probe_err_3; + } + card->fw_major = pcan_read_reg(card, PCC_FW_MAJOR); + card->fw_minor = pcan_read_reg(card, PCC_FW_MINOR); + + /* display board name and firware version */ + dev_info(&pdev->dev, "PEAK-System pcmcia card %s fw %d.%d\n", + pdev->prod_id[1] ? pdev->prod_id[1] : "PCAN-PC Card", + card->fw_major, card->fw_minor); + + /* detect available channels */ + pcan_add_channels(card); + if (!card->chan_count) + goto probe_err_4; + + /* init the timer which controls the leds */ + init_timer(&card->led_timer); + card->led_timer.function = pcan_led_timer; + card->led_timer.data = (unsigned long)card; + + /* request the given irq */ + err = request_irq(pdev->irq, &pcan_isr, IRQF_SHARED, PCC_NAME, card); + if (err) { + dev_err(&pdev->dev, "couldn't request irq%d\n", pdev->irq); + goto probe_err_5; + } + + /* power on the connectors */ + pcan_set_can_power(card, 1); + + return 0; + +probe_err_5: + /* unregister can devices from network */ + pcan_free_channels(card); + +probe_err_4: + ioport_unmap(card->ioport_addr); + +probe_err_3: + kfree(card); + pdev->priv = NULL; + +probe_err_2: + pcmcia_disable_device(pdev); + +probe_err_1: + return err; +} + +/* + * release claimed resources + */ +static void pcan_remove(struct pcmcia_device *pdev) +{ + pcan_free(pdev); + pcmcia_disable_device(pdev); +} + +static struct pcmcia_driver pcan_driver = { + .name = PCC_NAME, + .probe = pcan_probe, + .remove = pcan_remove, + .id_table = pcan_table, +}; + +static int __init pcan_init(void) +{ + return pcmcia_register_driver(&pcan_driver); +} +module_init(pcan_init); + +static void __exit pcan_exit(void) +{ + pcmcia_unregister_driver(&pcan_driver); +} +module_exit(pcan_exit); diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 04525495b15b..0a6876841c20 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -13,4 +13,10 @@ config CAN_ESD_USB2 This driver supports the CAN-USB/2 interface from esd electronic system design gmbh (http://www.esd.eu). +config CAN_PEAK_USB + tristate "PEAK PCAN-USB/USB Pro interfaces" + ---help--- + This driver supports the PCAN-USB and PCAN-USB Pro adapters + from PEAK-System Technik (http://www.peak-system.com). + endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index fce3cf11719f..da6d1d3b2969 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile new file mode 100644 index 000000000000..1aefbc88d643 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o +peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c new file mode 100644 index 000000000000..8a7982e18e4e --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -0,0 +1,901 @@ +/* + * CAN driver for PEAK System PCAN-USB adapter + * Derived from the PCAN project file driver/src/pcan_usb.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean + * + * Many thanks to Klaus Hitschler + * + * 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; version 2 of the License. + * + * 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. + */ +#include +#include +#include + +#include +#include +#include + +#include "pcan_usb_core.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter"); + +/* PCAN-USB Endpoints */ +#define PCAN_USB_EP_CMDOUT 1 +#define PCAN_USB_EP_CMDIN (PCAN_USB_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USB_EP_MSGOUT 2 +#define PCAN_USB_EP_MSGIN (PCAN_USB_EP_MSGOUT | USB_DIR_IN) + +/* PCAN-USB command struct */ +#define PCAN_USB_CMD_FUNC 0 +#define PCAN_USB_CMD_NUM 1 +#define PCAN_USB_CMD_ARGS 2 +#define PCAN_USB_CMD_ARGS_LEN 14 +#define PCAN_USB_CMD_LEN (PCAN_USB_CMD_ARGS + \ + PCAN_USB_CMD_ARGS_LEN) + +/* PCAN-USB command timeout (ms.) */ +#define PCAN_USB_COMMAND_TIMEOUT 1000 + +/* PCAN-USB startup timeout (ms.) */ +#define PCAN_USB_STARTUP_TIMEOUT 10 + +/* PCAN-USB rx/tx buffers size */ +#define PCAN_USB_RX_BUFFER_SIZE 64 +#define PCAN_USB_TX_BUFFER_SIZE 64 + +#define PCAN_USB_MSG_HEADER_LEN 2 + +/* PCAN-USB adapter internal clock (MHz) */ +#define PCAN_USB_CRYSTAL_HZ 16000000 + +/* PCAN-USB USB message record status/len field */ +#define PCAN_USB_STATUSLEN_TIMESTAMP (1 << 7) +#define PCAN_USB_STATUSLEN_INTERNAL (1 << 6) +#define PCAN_USB_STATUSLEN_EXT_ID (1 << 5) +#define PCAN_USB_STATUSLEN_RTR (1 << 4) +#define PCAN_USB_STATUSLEN_DLC (0xf) + +/* PCAN-USB error flags */ +#define PCAN_USB_ERROR_TXFULL 0x01 +#define PCAN_USB_ERROR_RXQOVR 0x02 +#define PCAN_USB_ERROR_BUS_LIGHT 0x04 +#define PCAN_USB_ERROR_BUS_HEAVY 0x08 +#define PCAN_USB_ERROR_BUS_OFF 0x10 +#define PCAN_USB_ERROR_RXQEMPTY 0x20 +#define PCAN_USB_ERROR_QOVR 0x40 +#define PCAN_USB_ERROR_TXQFULL 0x80 + +/* SJA1000 modes */ +#define SJA1000_MODE_NORMAL 0x00 +#define SJA1000_MODE_INIT 0x01 + +/* + * tick duration = 42.666 us => + * (tick_number * 44739243) >> 20 ~ (tick_number * 42666) / 1000 + * accuracy = 10^-7 + */ +#define PCAN_USB_TS_DIV_SHIFTER 20 +#define PCAN_USB_TS_US_PER_TICK 44739243 + +/* PCAN-USB messages record types */ +#define PCAN_USB_REC_ERROR 1 +#define PCAN_USB_REC_ANALOG 2 +#define PCAN_USB_REC_BUSLOAD 3 +#define PCAN_USB_REC_TS 4 +#define PCAN_USB_REC_BUSEVT 5 + +/* private to PCAN-USB adapter */ +struct pcan_usb { + struct peak_usb_device dev; + struct peak_time_ref time_ref; + struct timer_list restart_timer; +}; + +/* incoming message context for decoding */ +struct pcan_usb_msg_context { + u16 ts16; + u8 prev_ts8; + u8 *ptr; + u8 *end; + u8 rec_cnt; + u8 rec_idx; + u8 rec_data_idx; + struct net_device *netdev; + struct pcan_usb *pdev; +}; + +/* + * send a command + */ +static int pcan_usb_send_cmd(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + dev->cmd_buf[PCAN_USB_CMD_FUNC] = f; + dev->cmd_buf[PCAN_USB_CMD_NUM] = n; + + if (p) + memcpy(dev->cmd_buf + PCAN_USB_CMD_ARGS, + p, PCAN_USB_CMD_ARGS_LEN); + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "sending cmd f=0x%x n=0x%x failure: %d\n", + f, n, err); + return err; +} + +/* + * send a command then wait for its response + */ +static int pcan_usb_wait_rsp(struct peak_usb_device *dev, u8 f, u8 n, u8 *p) +{ + int err; + int actual_length; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + /* first, send command */ + err = pcan_usb_send_cmd(dev, f, n, NULL); + if (err) + return err; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USB_EP_CMDIN), + dev->cmd_buf, PCAN_USB_CMD_LEN, &actual_length, + PCAN_USB_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, + "waiting rsp f=0x%x n=0x%x failure: %d\n", f, n, err); + else if (p) + memcpy(p, dev->cmd_buf + PCAN_USB_CMD_ARGS, + PCAN_USB_CMD_ARGS_LEN); + + return err; +} + +static int pcan_usb_set_sja1000(struct peak_usb_device *dev, u8 mode) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [1] = mode, + }; + + return pcan_usb_send_cmd(dev, 9, 2, args); +} + +static int pcan_usb_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 3, 2, args); +} + +static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 3, 3, args); +} + +static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN] = { + [0] = !!onoff, + }; + + return pcan_usb_send_cmd(dev, 10, 2, args); +} + +/* + * set bittiming value to can + */ +static int pcan_usb_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + netdev_info(dev->netdev, "setting BTR0=0x%02x BTR1=0x%02x\n", + btr0, btr1); + + args[0] = btr1; + args[1] = btr0; + + return pcan_usb_send_cmd(dev, 1, 2, args); +} + +/* + * init/reset can + */ +static int pcan_usb_write_mode(struct peak_usb_device *dev, u8 onoff) +{ + int err; + + err = pcan_usb_set_bus(dev, onoff); + if (err) + return err; + + if (!onoff) { + err = pcan_usb_set_sja1000(dev, SJA1000_MODE_INIT); + } else { + /* the PCAN-USB needs time to init */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + } + + return err; +} + +/* + * handle end of waiting for the device to reset + */ +static void pcan_usb_restart(unsigned long arg) +{ + /* notify candev and netdev */ + peak_usb_restart_complete((struct peak_usb_device *)arg); +} + +/* + * handle the submission of the restart urb + */ +static void pcan_usb_restart_pending(struct urb *urb) +{ + struct pcan_usb *pdev = urb->context; + + /* the PCAN-USB needs time to restart */ + mod_timer(&pdev->restart_timer, + jiffies + msecs_to_jiffies(PCAN_USB_STARTUP_TIMEOUT)); + + /* can delete usb resources */ + peak_usb_async_complete(urb); +} + +/* + * handle asynchronous restart + */ +static int pcan_usb_restart_async(struct peak_usb_device *dev, struct urb *urb, + u8 *buf) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + if (timer_pending(&pdev->restart_timer)) + return -EBUSY; + + /* set bus on */ + buf[PCAN_USB_CMD_FUNC] = 3; + buf[PCAN_USB_CMD_NUM] = 2; + buf[PCAN_USB_CMD_ARGS] = 1; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USB_EP_CMDOUT), + buf, PCAN_USB_CMD_LEN, + pcan_usb_restart_pending, pdev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * read serial number from device + */ +static int pcan_usb_get_serial(struct peak_usb_device *dev, u32 *serial_number) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, 6, 1, args); + if (err) { + netdev_err(dev->netdev, "getting serial failure: %d\n", err); + } else if (serial_number) { + u32 tmp32; + + memcpy(&tmp32, args, 4); + *serial_number = le32_to_cpu(tmp32); + } + + return err; +} + +/* + * read device id from device + */ +static int pcan_usb_get_device_id(struct peak_usb_device *dev, u32 *device_id) +{ + u8 args[PCAN_USB_CMD_ARGS_LEN]; + int err; + + err = pcan_usb_wait_rsp(dev, 4, 1, args); + if (err) + netdev_err(dev->netdev, "getting device id failure: %d\n", err); + else if (device_id) + *device_id = args[0]; + + return err; +} + +/* + * update current time ref with received timestamp + */ +static int pcan_usb_update_ts(struct pcan_usb_msg_context *mc) +{ + u16 tmp16; + + if ((mc->ptr+2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + + mc->ts16 = le16_to_cpu(tmp16); + + if (mc->rec_idx > 0) + peak_usb_update_ts_now(&mc->pdev->time_ref, mc->ts16); + else + peak_usb_set_ts_now(&mc->pdev->time_ref, mc->ts16); + + return 0; +} + +/* + * decode received timestamp + */ +static int pcan_usb_decode_ts(struct pcan_usb_msg_context *mc, u8 first_packet) +{ + /* only 1st packet supplies a word timestamp */ + if (first_packet) { + u16 tmp16; + + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + mc->ts16 = le16_to_cpu(tmp16); + mc->prev_ts8 = mc->ts16 & 0x00ff; + } else { + u8 ts8; + + if ((mc->ptr + 1) > mc->end) + return -EINVAL; + + ts8 = *mc->ptr++; + + if (ts8 < mc->prev_ts8) + mc->ts16 += 0x100; + + mc->ts16 &= 0xff00; + mc->ts16 |= ts8; + mc->prev_ts8 = ts8; + } + + return 0; +} + +static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, + u8 status_len) +{ + struct sk_buff *skb; + struct can_frame *cf; + struct timeval tv; + enum can_state new_state; + + /* ignore this error until 1st ts received */ + if (n == PCAN_USB_ERROR_QOVR) + if (!mc->pdev->time_ref.tick_count) + return 0; + + new_state = mc->pdev->dev.can.state; + + switch (mc->pdev->dev.can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + + case CAN_STATE_ERROR_WARNING: + if (n & PCAN_USB_ERROR_BUS_HEAVY) { + new_state = CAN_STATE_ERROR_PASSIVE; + break; + } + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + if ((n & PCAN_USB_ERROR_BUS_LIGHT) == 0) { + /* no error (back to active state) */ + mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + break; + + case CAN_STATE_ERROR_PASSIVE: + if (n & PCAN_USB_ERROR_BUS_OFF) { + new_state = CAN_STATE_BUS_OFF; + break; + } + if (n & PCAN_USB_ERROR_BUS_LIGHT) { + new_state = CAN_STATE_ERROR_WARNING; + break; + } + if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) { + /* + * trick to bypass next comparison and process other + * errors + */ + new_state = CAN_STATE_MAX; + break; + } + + if ((n & PCAN_USB_ERROR_BUS_HEAVY) == 0) { + /* no error (back to active state) */ + mc->pdev->dev.can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + break; + + default: + /* do nothing waiting for restart */ + return 0; + } + + /* donot post any error if current state didn't change */ + if (mc->pdev->dev.can.state == new_state) + return 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_BUS_OFF: + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(mc->netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE | + CAN_ERR_CRTL_RX_PASSIVE; + mc->pdev->dev.can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_TX_WARNING | + CAN_ERR_CRTL_RX_WARNING; + mc->pdev->dev.can.can_stats.error_warning++; + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + mc->netdev->stats.rx_over_errors++; + mc->netdev->stats.rx_errors++; + + new_state = mc->pdev->dev.can.state; + break; + } + + mc->pdev->dev.can.state = new_state; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); + skb->tstamp = timeval_to_ktime(tv); + } + + netif_rx(skb); + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + + return 0; +} + +/* + * decode non-data usb message + */ +static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc, + u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + u8 f, n; + int err; + + /* check whether function and number can be read */ + if ((mc->ptr + 2) > mc->end) + return -EINVAL; + + f = mc->ptr[PCAN_USB_CMD_FUNC]; + n = mc->ptr[PCAN_USB_CMD_NUM]; + mc->ptr += PCAN_USB_CMD_ARGS; + + if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + int err = pcan_usb_decode_ts(mc, !mc->rec_idx); + + if (err) + return err; + } + + switch (f) { + case PCAN_USB_REC_ERROR: + err = pcan_usb_decode_error(mc, n, status_len); + if (err) + return err; + break; + + case PCAN_USB_REC_ANALOG: + /* analog values (ignored) */ + rec_len = 2; + break; + + case PCAN_USB_REC_BUSLOAD: + /* bus load (ignored) */ + rec_len = 1; + break; + + case PCAN_USB_REC_TS: + /* only timestamp */ + if (pcan_usb_update_ts(mc)) + return -EINVAL; + break; + + case PCAN_USB_REC_BUSEVT: + /* error frame/bus event */ + if (n & PCAN_USB_ERROR_TXQFULL) + netdev_dbg(mc->netdev, "device Tx queue full)\n"); + break; + default: + netdev_err(mc->netdev, "unexpected function %u\n", f); + break; + } + + if ((mc->ptr + rec_len) > mc->end) + return -EINVAL; + + mc->ptr += rec_len; + + return 0; +} + +/* + * decode data usb message + */ +static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len) +{ + u8 rec_len = status_len & PCAN_USB_STATUSLEN_DLC; + struct sk_buff *skb; + struct can_frame *cf; + struct timeval tv; + + skb = alloc_can_skb(mc->netdev, &cf); + if (!skb) + return -ENOMEM; + + if (status_len & PCAN_USB_STATUSLEN_EXT_ID) { + u32 tmp32; + + if ((mc->ptr + 4) > mc->end) + goto decode_failed; + + memcpy(&tmp32, mc->ptr, 4); + mc->ptr += 4; + + cf->can_id = le32_to_cpu(tmp32 >> 3) | CAN_EFF_FLAG; + } else { + u16 tmp16; + + if ((mc->ptr + 2) > mc->end) + goto decode_failed; + + memcpy(&tmp16, mc->ptr, 2); + mc->ptr += 2; + + cf->can_id = le16_to_cpu(tmp16 >> 5); + } + + cf->can_dlc = get_can_dlc(rec_len); + + /* first data packet timestamp is a word */ + if (pcan_usb_decode_ts(mc, !mc->rec_data_idx)) + goto decode_failed; + + /* read data */ + memset(cf->data, 0x0, sizeof(cf->data)); + if (status_len & PCAN_USB_STATUSLEN_RTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + if ((mc->ptr + rec_len) > mc->end) + goto decode_failed; + + memcpy(cf->data, mc->ptr, rec_len); + mc->ptr += rec_len; + } + + /* convert timestamp into kernel time */ + peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); + skb->tstamp = timeval_to_ktime(tv); + + /* push the skb */ + netif_rx(skb); + + /* update statistics */ + mc->netdev->stats.rx_packets++; + mc->netdev->stats.rx_bytes += cf->can_dlc; + + return 0; + +decode_failed: + dev_kfree_skb(skb); + return -EINVAL; +} + +/* + * process incoming message + */ +static int pcan_usb_decode_msg(struct peak_usb_device *dev, u8 *ibuf, u32 lbuf) +{ + struct pcan_usb_msg_context mc = { + .rec_cnt = ibuf[1], + .ptr = ibuf + PCAN_USB_MSG_HEADER_LEN, + .end = ibuf + lbuf, + .netdev = dev->netdev, + .pdev = container_of(dev, struct pcan_usb, dev), + }; + int err; + + for (err = 0; mc.rec_idx < mc.rec_cnt && !err; mc.rec_idx++) { + u8 sl = *mc.ptr++; + + /* handle status and error frames here */ + if (sl & PCAN_USB_STATUSLEN_INTERNAL) { + err = pcan_usb_decode_status(&mc, sl); + /* handle normal can frames here */ + } else { + err = pcan_usb_decode_data(&mc, sl); + mc.rec_data_idx++; + } + } + + return err; +} + +/* + * process any incoming buffer + */ +static int pcan_usb_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + int err = 0; + + if (urb->actual_length > PCAN_USB_MSG_HEADER_LEN) { + err = pcan_usb_decode_msg(dev, urb->transfer_buffer, + urb->actual_length); + + } else if (urb->actual_length > 0) { + netdev_err(dev->netdev, "usb message length error (%u)\n", + urb->actual_length); + err = -EINVAL; + } + + return err; +} + +/* + * process outgoing packet + */ +static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size) +{ + struct net_device *netdev = dev->netdev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 *pc; + + obuf[0] = 2; + obuf[1] = 1; + + pc = obuf + PCAN_USB_MSG_HEADER_LEN; + + /* status/len byte */ + *pc = cf->can_dlc; + if (cf->can_id & CAN_RTR_FLAG) + *pc |= PCAN_USB_STATUSLEN_RTR; + + /* can id */ + if (cf->can_id & CAN_EFF_FLAG) { + __le32 tmp32 = cpu_to_le32(cf->can_id & CAN_ERR_MASK); + + tmp32 <<= 3; + *pc |= PCAN_USB_STATUSLEN_EXT_ID; + memcpy(++pc, &tmp32, 4); + pc += 4; + } else { + __le16 tmp16 = cpu_to_le32(cf->can_id & CAN_ERR_MASK); + + tmp16 <<= 5; + memcpy(++pc, &tmp16, 2); + pc += 2; + } + + /* can data */ + if (!(cf->can_id & CAN_RTR_FLAG)) { + memcpy(pc, cf->data, cf->can_dlc); + pc += cf->can_dlc; + } + + obuf[(*size)-1] = (u8)(stats->tx_packets & 0xff); + + return 0; +} + +/* + * start interface + */ +static int pcan_usb_start(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + + /* number of bits used in timestamps read from adapter struct */ + peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb); + + /* if revision greater than 3, can put silent mode on/off */ + if (dev->device_rev > 3) { + int err; + + err = pcan_usb_set_silent(dev, + dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); + if (err) + return err; + } + + return pcan_usb_set_ext_vcc(dev, 0); +} + +static int pcan_usb_init(struct peak_usb_device *dev) +{ + struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev); + u32 serial_number; + int err; + + /* initialize a timer needed to wait for hardware restart */ + init_timer(&pdev->restart_timer); + pdev->restart_timer.function = pcan_usb_restart; + pdev->restart_timer.data = (unsigned long)dev; + + /* + * explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx netdevice. + */ + err = pcan_usb_get_serial(dev, &serial_number); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s serial number (err %d)\n", + pcan_usb.name, err); + return err; + } + + dev_info(dev->netdev->dev.parent, + "PEAK-System %s adapter hwrev %u serial %08X (%u channel)\n", + pcan_usb.name, dev->device_rev, serial_number, + pcan_usb.ctrl_count); + + return 0; +} + +/* + * probe function for new PCAN-USB usb interface + */ +static int pcan_usb_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + switch (ep->bEndpointAddress) { + case PCAN_USB_EP_CMDOUT: + case PCAN_USB_EP_CMDIN: + case PCAN_USB_EP_MSGOUT: + case PCAN_USB_EP_MSGIN: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB adapter + */ +struct peak_usb_adapter pcan_usb = { + .name = "PCAN-USB", + .device_id = PCAN_USB_PRODUCT_ID, + .ctrl_count = 1, + .clock = { + .freq = PCAN_USB_CRYSTAL_HZ / 2 , + }, + .bittiming_const = { + .name = "pcan_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb), + + /* timestamps usage */ + .ts_used_bits = 16, + .ts_period = 24575, /* calibration period in ts. */ + .us_per_ts_scale = PCAN_USB_TS_US_PER_TICK, /* us=(ts*scale) */ + .us_per_ts_shift = PCAN_USB_TS_DIV_SHIFTER, /* >> shift */ + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USB_EP_MSGIN, + .ep_msg_out = {PCAN_USB_EP_MSGOUT}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USB_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USB_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_probe, + .dev_init = pcan_usb_init, + .dev_set_bus = pcan_usb_write_mode, + .dev_set_bittiming = pcan_usb_set_bittiming, + .dev_get_device_id = pcan_usb_get_device_id, + .dev_decode_buf = pcan_usb_decode_buf, + .dev_encode_msg = pcan_usb_encode_msg, + .dev_start = pcan_usb_start, + .dev_restart_async = pcan_usb_restart_async, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c new file mode 100644 index 000000000000..d2f91f737871 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -0,0 +1,951 @@ +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean + * + * Many thanks to Klaus Hitschler + * + * 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; version 2 of the License. + * + * 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. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pcan_usb_core.h" + +MODULE_AUTHOR("Stephane Grosjean "); +MODULE_DESCRIPTION("CAN driver for PEAK-System USB adapters"); +MODULE_LICENSE("GPL v2"); + +/* Table of devices that work with this driver */ +static struct usb_device_id peak_usb_table[] = { + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, peak_usb_table); + +/* List of supported PCAN-USB adapters (NULL terminated list) */ +static struct peak_usb_adapter *peak_usb_adapters_list[] = { + &pcan_usb, + &pcan_usb_pro, + NULL, +}; + +/* + * dump memory + */ +#define DUMP_WIDTH 16 +void dump_mem(char *prompt, void *p, int l) +{ + pr_info("%s dumping %s (%d bytes):\n", + PCAN_USB_DRIVER_NAME, prompt ? prompt : "memory", l); + print_hex_dump(KERN_INFO, PCAN_USB_DRIVER_NAME " ", DUMP_PREFIX_NONE, + DUMP_WIDTH, 1, p, l, false); +} + +/* + * initialize a time_ref object with usb adapter own settings + */ +void peak_usb_init_time_ref(struct peak_time_ref *time_ref, + struct peak_usb_adapter *adapter) +{ + if (time_ref) { + memset(time_ref, 0, sizeof(struct peak_time_ref)); + time_ref->adapter = adapter; + } +} + +static void peak_usb_add_us(struct timeval *tv, u32 delta_us) +{ + /* number of s. to add to final time */ + u32 delta_s = delta_us / 1000000; + + delta_us -= delta_s * 1000000; + + tv->tv_usec += delta_us; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + delta_s++; + } + tv->tv_sec += delta_s; +} + +/* + * sometimes, another now may be more recent than current one... + */ +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + time_ref->ts_dev_2 = ts_now; + + /* should wait at least two passes before computing */ + if (time_ref->tv_host.tv_sec > 0) { + u32 delta_ts = time_ref->ts_dev_2 - time_ref->ts_dev_1; + + if (time_ref->ts_dev_2 < time_ref->ts_dev_1) + delta_ts &= (1 << time_ref->adapter->ts_used_bits) - 1; + + time_ref->ts_total += delta_ts; + } +} + +/* + * register device timestamp as now + */ +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now) +{ + if (time_ref->tv_host_0.tv_sec == 0) { + /* use monotonic clock to correctly compute further deltas */ + time_ref->tv_host_0 = ktime_to_timeval(ktime_get()); + time_ref->tv_host.tv_sec = 0; + } else { + /* + * delta_us should not be >= 2^32 => delta_s should be < 4294 + * handle 32-bits wrapping here: if count of s. reaches 4200, + * reset counters and change time base + */ + if (time_ref->tv_host.tv_sec != 0) { + u32 delta_s = time_ref->tv_host.tv_sec + - time_ref->tv_host_0.tv_sec; + if (delta_s > 4200) { + time_ref->tv_host_0 = time_ref->tv_host; + time_ref->ts_total = 0; + } + } + + time_ref->tv_host = ktime_to_timeval(ktime_get()); + time_ref->tick_count++; + } + + time_ref->ts_dev_1 = time_ref->ts_dev_2; + peak_usb_update_ts_now(time_ref, ts_now); +} + +/* + * compute timeval according to current ts and time_ref data + */ +void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, + struct timeval *tv) +{ + /* protect from getting timeval before setting now */ + if (time_ref->tv_host.tv_sec > 0) { + u64 delta_us; + + delta_us = ts - time_ref->ts_dev_2; + if (ts < time_ref->ts_dev_2) + delta_us &= (1 << time_ref->adapter->ts_used_bits) - 1; + + delta_us += time_ref->ts_total; + + delta_us *= time_ref->adapter->us_per_ts_scale; + delta_us >>= time_ref->adapter->us_per_ts_shift; + + *tv = time_ref->tv_host_0; + peak_usb_add_us(tv, (u32)delta_us); + } else { + *tv = ktime_to_timeval(ktime_get()); + } +} + +/* + * callback for bulk Rx urb + */ +static void peak_usb_read_bulk_callback(struct urb *urb) +{ + struct peak_usb_device *dev = urb->context; + struct net_device *netdev; + int err; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + /* check reception status */ + switch (urb->status) { + case 0: + /* success */ + break; + + case -EILSEQ: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + + default: + if (net_ratelimit()) + netdev_err(netdev, + "Rx urb aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + /* protect from any incoming empty msgs */ + if ((urb->actual_length > 0) && (dev->adapter->dev_decode_buf)) { + /* handle these kinds of msgs only if _start callback called */ + if (dev->state & PCAN_USB_STATE_STARTED) { + err = dev->adapter->dev_decode_buf(dev, urb); + if (err) + dump_mem("received usb message", + urb->transfer_buffer, + urb->transfer_buffer_length); + } + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), + urb->transfer_buffer, dev->adapter->rx_buffer_size, + peak_usb_read_bulk_callback, dev); + + usb_anchor_urb(urb, &dev->rx_submitted); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (!err) + return; + + usb_unanchor_urb(urb); + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_err(netdev, "failed resubmitting read bulk urb: %d\n", + err); +} + +/* + * callback for bulk Tx urb + */ +static void peak_usb_write_bulk_callback(struct urb *urb) +{ + struct peak_tx_urb_context *context = urb->context; + struct peak_usb_device *dev; + struct net_device *netdev; + + BUG_ON(!context); + + dev = context->dev; + netdev = dev->netdev; + + atomic_dec(&dev->active_tx_urbs); + + if (!netif_device_present(netdev)) + return; + + /* check tx status */ + switch (urb->status) { + case 0: + /* transmission complete */ + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += context->dlc; + + /* prevent tx timeout */ + netdev->trans_start = jiffies; + break; + + default: + if (net_ratelimit()) + netdev_err(netdev, "Tx urb aborted (%d)\n", + urb->status); + case -EPROTO: + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + + break; + } + + /* should always release echo skb and corresponding context */ + can_get_echo_skb(netdev, context->echo_index); + context->echo_index = PCAN_USB_MAX_TX_URBS; + + /* do wakeup tx queue in case of success only */ + if (!urb->status) + netif_wake_queue(netdev); +} + +/* + * called by netdev to send one skb on the CAN interface. + */ +static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct peak_tx_urb_context *context = NULL; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct urb *urb; + u8 *obuf; + int i, err; + size_t size = dev->adapter->tx_buffer_size; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + if (dev->tx_contexts[i].echo_index == PCAN_USB_MAX_TX_URBS) { + context = dev->tx_contexts + i; + break; + } + + if (!context) { + /* should not occur except during restart */ + return NETDEV_TX_BUSY; + } + + urb = context->urb; + obuf = urb->transfer_buffer; + + err = dev->adapter->dev_encode_msg(dev, skb, obuf, &size); + if (err) { + if (net_ratelimit()) + netdev_err(netdev, "packet dropped\n"); + dev_kfree_skb(skb); + stats->tx_dropped++; + return NETDEV_TX_OK; + } + + context->echo_index = i; + context->dlc = cf->can_dlc; + + usb_anchor_urb(urb, &dev->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&dev->active_tx_urbs); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + can_free_echo_skb(netdev, context->echo_index); + + usb_unanchor_urb(urb); + + /* this context is not used in fact */ + context->echo_index = PCAN_USB_MAX_TX_URBS; + + atomic_dec(&dev->active_tx_urbs); + + switch (err) { + case -ENODEV: + netif_device_detach(netdev); + break; + default: + netdev_warn(netdev, "tx urb submitting failed err=%d\n", + err); + case -ENOENT: + /* cable unplugged */ + stats->tx_dropped++; + } + } else { + netdev->trans_start = jiffies; + + /* slow down tx path */ + if (atomic_read(&dev->active_tx_urbs) >= PCAN_USB_MAX_TX_URBS) + netif_stop_queue(netdev); + } + + return NETDEV_TX_OK; +} + +/* + * start the CAN interface. + * Rx and Tx urbs are allocated here. Rx urbs are submitted here. + */ +static int peak_usb_start(struct peak_usb_device *dev) +{ + struct net_device *netdev = dev->netdev; + int err, i; + + for (i = 0; i < PCAN_USB_MAX_RX_URBS; i++) { + struct urb *urb; + u8 *buf; + + /* create a URB, and a buffer for it, to receive usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->rx_buffer_size, GFP_KERNEL); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->ep_msg_in), + buf, dev->adapter->rx_buffer_size, + peak_usb_read_bulk_callback, dev); + + /* ask last usb_free_urb() to also kfree() transfer_buffer */ + urb->transfer_flags |= URB_FREE_BUFFER; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + usb_unanchor_urb(urb); + kfree(buf); + usb_free_urb(urb); + break; + } + + /* drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* did we submit any URBs? Warn if we was not able to submit all urbs */ + if (i < PCAN_USB_MAX_RX_URBS) { + if (i == 0) { + netdev_err(netdev, "couldn't setup any rx URB\n"); + return err; + } + + netdev_warn(netdev, "rx performance may be slow\n"); + } + + /* pre-alloc tx buffers and corresponding urbs */ + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + struct peak_tx_urb_context *context; + struct urb *urb; + u8 *buf; + + /* create a URB and a buffer for it, to transmit usb messages */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = kmalloc(dev->adapter->tx_buffer_size, GFP_KERNEL); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + context = dev->tx_contexts + i; + context->dev = dev; + context->urb = urb; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->ep_msg_out), + buf, dev->adapter->tx_buffer_size, + peak_usb_write_bulk_callback, context); + + /* ask last usb_free_urb() to also kfree() transfer_buffer */ + urb->transfer_flags |= URB_FREE_BUFFER; + } + + /* warn if we were not able to allocate enough tx contexts */ + if (i < PCAN_USB_MAX_TX_URBS) { + if (i == 0) { + netdev_err(netdev, "couldn't setup any tx URB\n"); + return err; + } + + netdev_warn(netdev, "tx performance may be slow\n"); + } + + if (dev->adapter->dev_start) { + err = dev->adapter->dev_start(dev); + if (err) + goto failed; + } + + dev->state |= PCAN_USB_STATE_STARTED; + + /* can set bus on now */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 1); + if (err) + goto failed; + } + + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +failed: + if (err == -ENODEV) + netif_device_detach(dev->netdev); + + netdev_warn(netdev, "couldn't submit control: %d\n", err); + + return err; +} + +/* + * called by netdev to open the corresponding CAN interface. + */ +static int peak_usb_ndo_open(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + /* finally start device */ + err = peak_usb_start(dev); + if (err) { + netdev_err(netdev, "couldn't start device: %d\n", err); + close_candev(netdev); + return err; + } + + dev->open_time = jiffies; + netif_start_queue(netdev); + + return 0; +} + +/* + * unlink in-flight Rx and Tx urbs and free their memory. + */ +static void peak_usb_unlink_all_urbs(struct peak_usb_device *dev) +{ + int i; + + /* free all Rx (submitted) urbs */ + usb_kill_anchored_urbs(&dev->rx_submitted); + + /* free unsubmitted Tx urbs first */ + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) { + struct urb *urb = dev->tx_contexts[i].urb; + + if (!urb || + dev->tx_contexts[i].echo_index != PCAN_USB_MAX_TX_URBS) { + /* + * this urb is already released or always submitted, + * let usb core free by itself + */ + continue; + } + + usb_free_urb(urb); + dev->tx_contexts[i].urb = NULL; + } + + /* then free all submitted Tx urbs */ + usb_kill_anchored_urbs(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); +} + +/* + * called by netdev to close the corresponding CAN interface. + */ +static int peak_usb_ndo_stop(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + + dev->state &= ~PCAN_USB_STATE_STARTED; + netif_stop_queue(netdev); + + /* unlink all pending urbs and free used memory */ + peak_usb_unlink_all_urbs(dev); + + if (dev->adapter->dev_stop) + dev->adapter->dev_stop(dev); + + close_candev(netdev); + + dev->open_time = 0; + dev->can.state = CAN_STATE_STOPPED; + + /* can set bus off now */ + if (dev->adapter->dev_set_bus) { + int err = dev->adapter->dev_set_bus(dev, 0); + if (err) + return err; + } + + return 0; +} + +/* + * handle end of waiting for the device to reset + */ +void peak_usb_restart_complete(struct peak_usb_device *dev) +{ + /* finally MUST update can state */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + + /* netdev queue can be awaken now */ + netif_wake_queue(dev->netdev); +} + +void peak_usb_async_complete(struct urb *urb) +{ + kfree(urb->transfer_buffer); + usb_free_urb(urb); +} + +/* + * device (auto-)restart mechanism runs in a timer context => + * MUST handle restart with asynchronous usb transfers + */ +static int peak_usb_restart(struct peak_usb_device *dev) +{ + struct urb *urb; + int err; + u8 *buf; + + /* + * if device doesn't define any asynchronous restart handler, simply + * wake the netdev queue up + */ + if (!dev->adapter->dev_restart_async) { + peak_usb_restart_complete(dev); + return 0; + } + + /* first allocate a urb to handle the asynchronous steps */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(dev->netdev, "no memory left for urb\n"); + return -ENOMEM; + } + + /* also allocate enough space for the commands to send */ + buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_ATOMIC); + if (!buf) { + netdev_err(dev->netdev, "no memory left for async cmd\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + /* call the device specific handler for the restart */ + err = dev->adapter->dev_restart_async(dev, urb, buf); + if (!err) + return 0; + + kfree(buf); + usb_free_urb(urb); + + return err; +} + +/* + * candev callback used to change CAN mode. + * Warning: this is called from a timer context! + */ +static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + int err = 0; + + if (!dev->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + err = peak_usb_restart(dev); + if (err) + netdev_err(netdev, "couldn't start device (err %d)\n", + err); + break; + + default: + return -EOPNOTSUPP; + } + + return err; +} + +/* + * candev callback used to set device bitrate. + */ +static int peak_usb_set_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct can_bittiming *bt = &dev->can.bittiming; + + if (dev->adapter->dev_set_bittiming) { + int err = dev->adapter->dev_set_bittiming(dev, bt); + + if (err) + netdev_info(netdev, "couldn't set bitrate (err %d)\n", + err); + return err; + } + + return 0; +} + +static const struct net_device_ops peak_usb_netdev_ops = { + .ndo_open = peak_usb_ndo_open, + .ndo_stop = peak_usb_ndo_stop, + .ndo_start_xmit = peak_usb_ndo_start_xmit, +}; + +/* + * create one device which is attached to CAN controller #ctrl_idx of the + * usb adapter. + */ +static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter, + struct usb_interface *intf, int ctrl_idx) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + int sizeof_candev = peak_usb_adapter->sizeof_dev_private; + struct peak_usb_device *dev; + struct net_device *netdev; + int i, err; + u16 tmp16; + + if (sizeof_candev < sizeof(struct peak_usb_device)) + sizeof_candev = sizeof(struct peak_usb_device); + + netdev = alloc_candev(sizeof_candev, PCAN_USB_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "%s: couldn't alloc candev\n", + PCAN_USB_DRIVER_NAME); + return -ENOMEM; + } + + dev = netdev_priv(netdev); + + /* allocate a buffer large enough to send commands */ + dev->cmd_buf = kmalloc(PCAN_USB_MAX_CMD_LEN, GFP_KERNEL); + if (!dev->cmd_buf) { + dev_err(&intf->dev, "%s: couldn't alloc cmd buffer\n", + PCAN_USB_DRIVER_NAME); + err = -ENOMEM; + goto lbl_set_intf_data; + } + + dev->udev = usb_dev; + dev->netdev = netdev; + dev->adapter = peak_usb_adapter; + dev->ctrl_idx = ctrl_idx; + dev->state = PCAN_USB_STATE_CONNECTED; + + dev->ep_msg_in = peak_usb_adapter->ep_msg_in; + dev->ep_msg_out = peak_usb_adapter->ep_msg_out[ctrl_idx]; + + dev->can.clock = peak_usb_adapter->clock; + dev->can.bittiming_const = &peak_usb_adapter->bittiming_const; + dev->can.do_set_bittiming = peak_usb_set_bittiming; + dev->can.do_set_mode = peak_usb_set_mode; + dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LISTENONLY; + + netdev->netdev_ops = &peak_usb_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + init_usb_anchor(&dev->rx_submitted); + + init_usb_anchor(&dev->tx_submitted); + atomic_set(&dev->active_tx_urbs, 0); + + for (i = 0; i < PCAN_USB_MAX_TX_URBS; i++) + dev->tx_contexts[i].echo_index = PCAN_USB_MAX_TX_URBS; + + dev->prev_siblings = usb_get_intfdata(intf); + usb_set_intfdata(intf, dev); + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + dev_err(&intf->dev, "couldn't register CAN device: %d\n", err); + goto lbl_free_cmd_buf; + } + + if (dev->prev_siblings) + (dev->prev_siblings)->next_siblings = dev; + + /* keep hw revision into the netdevice */ + tmp16 = le16_to_cpu(usb_dev->descriptor.bcdDevice); + dev->device_rev = tmp16 >> 8; + + if (dev->adapter->dev_init) { + err = dev->adapter->dev_init(dev); + if (err) + goto lbl_free_cmd_buf; + } + + /* set bus off */ + if (dev->adapter->dev_set_bus) { + err = dev->adapter->dev_set_bus(dev, 0); + if (err) + goto lbl_free_cmd_buf; + } + + /* get device number early */ + if (dev->adapter->dev_get_device_id) + dev->adapter->dev_get_device_id(dev, &dev->device_number); + + netdev_info(netdev, "attached to %s channel %u (device %u)\n", + peak_usb_adapter->name, ctrl_idx, dev->device_number); + + return 0; + +lbl_free_cmd_buf: + kfree(dev->cmd_buf); + +lbl_set_intf_data: + usb_set_intfdata(intf, dev->prev_siblings); + free_candev(netdev); + + return err; +} + +/* + * called by the usb core when the device is unplugged from the system + */ +static void peak_usb_disconnect(struct usb_interface *intf) +{ + struct peak_usb_device *dev; + + /* unregister as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + struct net_device *netdev = dev->netdev; + char name[IFNAMSIZ]; + + dev->state &= ~PCAN_USB_STATE_CONNECTED; + strncpy(name, netdev->name, IFNAMSIZ); + + unregister_netdev(netdev); + free_candev(netdev); + + kfree(dev->cmd_buf); + dev->next_siblings = NULL; + if (dev->adapter->dev_free) + dev->adapter->dev_free(dev); + + dev_info(&intf->dev, "%s removed\n", name); + } + + usb_set_intfdata(intf, NULL); +} + +/* + * probe function for new PEAK-System devices + */ +static int peak_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct peak_usb_adapter *peak_usb_adapter, **pp; + int i, err = -ENOMEM; + + usb_dev = interface_to_usbdev(intf); + + /* get corresponding PCAN-USB adapter */ + for (pp = peak_usb_adapters_list; *pp; pp++) + if ((*pp)->device_id == usb_dev->descriptor.idProduct) + break; + + peak_usb_adapter = *pp; + if (!peak_usb_adapter) { + /* should never come except device_id bad usage in this file */ + pr_err("%s: didn't find device id. 0x%x in devices list\n", + PCAN_USB_DRIVER_NAME, usb_dev->descriptor.idProduct); + return -ENODEV; + } + + /* got corresponding adapter: check if it handles current interface */ + if (peak_usb_adapter->intf_probe) { + err = peak_usb_adapter->intf_probe(intf); + if (err) + return err; + } + + for (i = 0; i < peak_usb_adapter->ctrl_count; i++) { + err = peak_usb_create_dev(peak_usb_adapter, intf, i); + if (err) { + /* deregister already created devices */ + peak_usb_disconnect(intf); + break; + } + } + + return err; +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver peak_usb_driver = { + .name = PCAN_USB_DRIVER_NAME, + .disconnect = peak_usb_disconnect, + .probe = peak_usb_probe, + .id_table = peak_usb_table, +}; + +static int __init peak_usb_init(void) +{ + int err; + + /* register this driver with the USB subsystem */ + err = usb_register(&peak_usb_driver); + if (err) + pr_err("%s: usb_register failed (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + return err; +} + +static int peak_usb_do_device_exit(struct device *d, void *arg) +{ + struct usb_interface *intf = to_usb_interface(d); + struct peak_usb_device *dev; + + /* stop as many netdev devices as siblings */ + for (dev = usb_get_intfdata(intf); dev; dev = dev->prev_siblings) { + struct net_device *netdev = dev->netdev; + + if (netif_device_present(netdev)) + if (dev->adapter->dev_exit) + dev->adapter->dev_exit(dev); + } + + return 0; +} + +static void __exit peak_usb_exit(void) +{ + int err; + + /* last chance do send any synchronous commands here */ + err = driver_for_each_device(&peak_usb_driver.drvwrap.driver, NULL, + NULL, peak_usb_do_device_exit); + if (err) + pr_err("%s: failed to stop all can devices (err %d)\n", + PCAN_USB_DRIVER_NAME, err); + + /* deregister this driver with the USB subsystem */ + usb_deregister(&peak_usb_driver); + + pr_info("%s: PCAN-USB interfaces driver unloaded\n", + PCAN_USB_DRIVER_NAME); +} + +module_init(peak_usb_init); +module_exit(peak_usb_exit); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h new file mode 100644 index 000000000000..a948c5a89401 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -0,0 +1,146 @@ +/* + * CAN driver for PEAK System USB adapters + * Derived from the PCAN project file driver/src/pcan_usb_core.c + * + * Copyright (C) 2003-2010 PEAK System-Technik GmbH + * Copyright (C) 2010-2012 Stephane Grosjean + * + * Many thanks to Klaus Hitschler + * + * 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; version 2 of the License. + * + * 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. + */ +#ifndef PCAN_USB_CORE_H +#define PCAN_USB_CORE_H + +/* PEAK-System vendor id. */ +#define PCAN_USB_VENDOR_ID 0x0c72 + +/* supported device ids. */ +#define PCAN_USB_PRODUCT_ID 0x000c +#define PCAN_USBPRO_PRODUCT_ID 0x000d + +#define PCAN_USB_DRIVER_NAME "peak_usb" + +/* number of urbs that are submitted for rx/tx per channel */ +#define PCAN_USB_MAX_RX_URBS 4 +#define PCAN_USB_MAX_TX_URBS 10 + +/* usb adapters maximum channels per usb interface */ +#define PCAN_USB_MAX_CHANNEL 2 + +/* maximum length of the usb commands sent to/received from the devices */ +#define PCAN_USB_MAX_CMD_LEN 32 + +struct peak_usb_device; + +/* PEAK-System USB adapter descriptor */ +struct peak_usb_adapter { + char *name; + u32 device_id; + struct can_clock clock; + struct can_bittiming_const bittiming_const; + unsigned int ctrl_count; + + int (*intf_probe)(struct usb_interface *intf); + + int (*dev_init)(struct peak_usb_device *dev); + void (*dev_exit)(struct peak_usb_device *dev); + void (*dev_free)(struct peak_usb_device *dev); + int (*dev_open)(struct peak_usb_device *dev); + int (*dev_close)(struct peak_usb_device *dev); + int (*dev_set_bittiming)(struct peak_usb_device *dev, + struct can_bittiming *bt); + int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff); + int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id); + int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb); + int (*dev_encode_msg)(struct peak_usb_device *dev, struct sk_buff *skb, + u8 *obuf, size_t *size); + int (*dev_start)(struct peak_usb_device *dev); + int (*dev_stop)(struct peak_usb_device *dev); + int (*dev_restart_async)(struct peak_usb_device *dev, struct urb *urb, + u8 *buf); + u8 ep_msg_in; + u8 ep_msg_out[PCAN_USB_MAX_CHANNEL]; + u8 ts_used_bits; + u32 ts_period; + u8 us_per_ts_shift; + u32 us_per_ts_scale; + + int rx_buffer_size; + int tx_buffer_size; + int sizeof_dev_private; +}; + +extern struct peak_usb_adapter pcan_usb; +extern struct peak_usb_adapter pcan_usb_pro; + +struct peak_time_ref { + struct timeval tv_host_0, tv_host; + u32 ts_dev_1, ts_dev_2; + u64 ts_total; + u32 tick_count; + struct peak_usb_adapter *adapter; +}; + +struct peak_tx_urb_context { + struct peak_usb_device *dev; + u32 echo_index; + u8 dlc; + struct urb *urb; +}; + +#define PCAN_USB_STATE_CONNECTED 0x00000001 +#define PCAN_USB_STATE_STARTED 0x00000002 + +/* PEAK-System USB device */ +struct peak_usb_device { + struct can_priv can; + struct peak_usb_adapter *adapter; + unsigned int ctrl_idx; + int open_time; + u32 state; + + struct sk_buff *echo_skb[PCAN_USB_MAX_TX_URBS]; + + struct usb_device *udev; + struct net_device *netdev; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct peak_tx_urb_context tx_contexts[PCAN_USB_MAX_TX_URBS]; + + u8 *cmd_buf; + struct usb_anchor rx_submitted; + + u32 device_number; + u8 device_rev; + + u8 ep_msg_in; + u8 ep_msg_out; + + u16 bus_load; + + struct peak_usb_device *prev_siblings; + struct peak_usb_device *next_siblings; +}; + +void dump_mem(char *prompt, void *p, int l); + +/* common timestamp management */ +void peak_usb_init_time_ref(struct peak_time_ref *time_ref, + struct peak_usb_adapter *adapter); +void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now); +void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, + struct timeval *tv); + +void peak_usb_async_complete(struct urb *urb); +void peak_usb_restart_complete(struct peak_usb_device *dev); +#endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c new file mode 100644 index 000000000000..5234586dff15 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -0,0 +1,1036 @@ +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro.c + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean + * + * 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; version 2 of the License. + * + * 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. + */ +#include +#include +#include + +#include +#include +#include + +#include "pcan_usb_core.h" +#include "pcan_usb_pro.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro adapter"); + +/* PCAN-USB Pro Endpoints */ +#define PCAN_USBPRO_EP_CMDOUT 1 +#define PCAN_USBPRO_EP_CMDIN (PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_0 2 +#define PCAN_USBPRO_EP_MSGIN (PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_1 3 +#define PCAN_USBPRO_EP_UNUSED (PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN) + +#define PCAN_USBPRO_CHANNEL_COUNT 2 + +/* PCAN-USB Pro adapter internal clock (MHz) */ +#define PCAN_USBPRO_CRYSTAL_HZ 56000000 + +/* PCAN-USB Pro command timeout (ms.) */ +#define PCAN_USBPRO_COMMAND_TIMEOUT 1000 + +/* PCAN-USB Pro rx/tx buffers size */ +#define PCAN_USBPRO_RX_BUFFER_SIZE 1024 +#define PCAN_USBPRO_TX_BUFFER_SIZE 64 + +#define PCAN_USBPRO_MSG_HEADER_LEN 4 + +/* some commands responses need to be re-submitted */ +#define PCAN_USBPRO_RSP_SUBMIT_MAX 2 + +#define PCAN_USBPRO_RTR 0x01 +#define PCAN_USBPRO_EXT 0x02 + +#define PCAN_USBPRO_CMD_BUFFER_SIZE 512 + +/* handle device specific info used by the netdevices */ +struct pcan_usb_pro_interface { + struct peak_usb_device *dev[PCAN_USBPRO_CHANNEL_COUNT]; + struct peak_time_ref time_ref; + int cm_ignore_count; + int dev_opened_count; +}; + +/* device information */ +struct pcan_usb_pro_device { + struct peak_usb_device dev; + struct pcan_usb_pro_interface *usb_if; + u32 cached_ccbt; +}; + +/* internal structure used to handle messages sent to bulk urb */ +struct pcan_usb_pro_msg { + u8 *rec_ptr; + int rec_buffer_size; + int rec_buffer_len; + union { + u16 *rec_cnt_rd; + u32 *rec_cnt; + u8 *rec_buffer; + } u; +}; + +/* records sizes table indexed on message id. (8-bits value) */ +static u16 pcan_usb_pro_sizeof_rec[256] = { + [PCAN_USBPRO_SETBTR] = sizeof(struct pcan_usb_pro_btr), + [PCAN_USBPRO_SETBUSACT] = sizeof(struct pcan_usb_pro_busact), + [PCAN_USBPRO_SETSILENT] = sizeof(struct pcan_usb_pro_silent), + [PCAN_USBPRO_SETFILTR] = sizeof(struct pcan_usb_pro_filter), + [PCAN_USBPRO_SETTS] = sizeof(struct pcan_usb_pro_setts), + [PCAN_USBPRO_GETDEVID] = sizeof(struct pcan_usb_pro_devid), + [PCAN_USBPRO_SETLED] = sizeof(struct pcan_usb_pro_setled), + [PCAN_USBPRO_RXMSG8] = sizeof(struct pcan_usb_pro_rxmsg), + [PCAN_USBPRO_RXMSG4] = sizeof(struct pcan_usb_pro_rxmsg) - 4, + [PCAN_USBPRO_RXMSG0] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXRTR] = sizeof(struct pcan_usb_pro_rxmsg) - 8, + [PCAN_USBPRO_RXSTATUS] = sizeof(struct pcan_usb_pro_rxstatus), + [PCAN_USBPRO_RXTS] = sizeof(struct pcan_usb_pro_rxts), + [PCAN_USBPRO_TXMSG8] = sizeof(struct pcan_usb_pro_txmsg), + [PCAN_USBPRO_TXMSG4] = sizeof(struct pcan_usb_pro_txmsg) - 4, + [PCAN_USBPRO_TXMSG0] = sizeof(struct pcan_usb_pro_txmsg) - 8, +}; + +/* + * initialize PCAN-USB Pro message data structure + */ +static u8 *pcan_msg_init(struct pcan_usb_pro_msg *pm, void *buffer_addr, + int buffer_size) +{ + if (buffer_size < PCAN_USBPRO_MSG_HEADER_LEN) + return NULL; + + pm->u.rec_buffer = (u8 *)buffer_addr; + pm->rec_buffer_size = pm->rec_buffer_len = buffer_size; + pm->rec_ptr = pm->u.rec_buffer + PCAN_USBPRO_MSG_HEADER_LEN; + + return pm->rec_ptr; +} + +static u8 *pcan_msg_init_empty(struct pcan_usb_pro_msg *pm, + void *buffer_addr, int buffer_size) +{ + u8 *pr = pcan_msg_init(pm, buffer_addr, buffer_size); + + if (pr) { + pm->rec_buffer_len = PCAN_USBPRO_MSG_HEADER_LEN; + *pm->u.rec_cnt = 0; + } + return pr; +} + +/* + * add one record to a message being built + */ +static int pcan_msg_add_rec(struct pcan_usb_pro_msg *pm, u8 id, ...) +{ + int len, i; + u8 *pc; + va_list ap; + + va_start(ap, id); + + pc = pm->rec_ptr + 1; + + i = 0; + switch (id) { + case PCAN_USBPRO_TXMSG8: + i += 4; + case PCAN_USBPRO_TXMSG4: + i += 4; + case PCAN_USBPRO_TXMSG0: + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *pc++ = va_arg(ap, int); + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + memcpy(pc, va_arg(ap, int *), i); + pc += i; + break; + + case PCAN_USBPRO_SETBTR: + case PCAN_USBPRO_GETDEVID: + *pc++ = va_arg(ap, int); + pc += 2; + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETFILTR: + case PCAN_USBPRO_SETBUSACT: + case PCAN_USBPRO_SETSILENT: + *pc++ = va_arg(ap, int); + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + case PCAN_USBPRO_SETLED: + *pc++ = va_arg(ap, int); + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + *(u32 *)pc = cpu_to_le32(va_arg(ap, u32)); + pc += 4; + break; + + case PCAN_USBPRO_SETTS: + pc++; + *(u16 *)pc = cpu_to_le16(va_arg(ap, int)); + pc += 2; + break; + + default: + pr_err("%s: %s(): unknown data type %02Xh (%d)\n", + PCAN_USB_DRIVER_NAME, __func__, id, id); + pc--; + break; + } + + len = pc - pm->rec_ptr; + if (len > 0) { + *pm->u.rec_cnt = cpu_to_le32(*pm->u.rec_cnt+1); + *pm->rec_ptr = id; + + pm->rec_ptr = pc; + pm->rec_buffer_len += len; + } + + va_end(ap); + + return len; +} + +/* + * send PCAN-USB Pro command synchronously + */ +static int pcan_usb_pro_send_cmd(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + int actual_length; + int err; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) + netdev_err(dev->netdev, "sending command failure: %d\n", err); + + return err; +} + +/* + * wait for PCAN-USB Pro command response + */ +static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev, + struct pcan_usb_pro_msg *pum) +{ + u8 req_data_type, req_channel; + int actual_length; + int i, err = 0; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + req_data_type = pum->u.rec_buffer[4]; + req_channel = pum->u.rec_buffer[5]; + + *pum->u.rec_cnt = 0; + for (i = 0; !err && i < PCAN_USBPRO_RSP_SUBMIT_MAX; i++) { + struct pcan_usb_pro_msg rsp; + union pcan_usb_pro_rec *pr; + u32 r, rec_cnt; + u16 rec_len; + u8 *pc; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDIN), + pum->u.rec_buffer, pum->rec_buffer_len, + &actual_length, PCAN_USBPRO_COMMAND_TIMEOUT); + if (err) { + netdev_err(dev->netdev, "waiting rsp error %d\n", err); + break; + } + + if (actual_length == 0) + continue; + + err = -EBADMSG; + if (actual_length < PCAN_USBPRO_MSG_HEADER_LEN) { + netdev_err(dev->netdev, + "got abnormal too small rsp (len=%d)\n", + actual_length); + break; + } + + pc = pcan_msg_init(&rsp, pum->u.rec_buffer, + actual_length); + + rec_cnt = le32_to_cpu(*rsp.u.rec_cnt); + + /* loop on records stored into message */ + for (r = 0; r < rec_cnt; r++) { + pr = (union pcan_usb_pro_rec *)pc; + rec_len = pcan_usb_pro_sizeof_rec[pr->data_type]; + if (!rec_len) { + netdev_err(dev->netdev, + "got unprocessed record in msg\n"); + dump_mem("rcvd rsp msg", pum->u.rec_buffer, + actual_length); + break; + } + + /* check if response corresponds to request */ + if (pr->data_type != req_data_type) + netdev_err(dev->netdev, + "got unwanted rsp %xh: ignored\n", + pr->data_type); + + /* check if channel in response corresponds too */ + else if ((req_channel != 0xff) && \ + (pr->bus_act.channel != req_channel)) + netdev_err(dev->netdev, + "got rsp %xh but on chan%u: ignored\n", + req_data_type, pr->bus_act.channel); + + /* got the response */ + else + return 0; + + /* otherwise, go on with next record in message */ + pc += rec_len; + } + } + + return (i >= PCAN_USBPRO_RSP_SUBMIT_MAX) ? -ERANGE : err; +} + +static int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size) +{ + int err; + u8 req_type; + unsigned int p; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + memset(req_addr, '\0', req_size); + + req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER; + + switch (req_id) { + case PCAN_USBPRO_REQ_FCT: + p = usb_sndctrlpipe(dev->udev, 0); + break; + + default: + p = usb_rcvctrlpipe(dev->udev, 0); + req_type |= USB_DIR_IN; + break; + } + + err = usb_control_msg(dev->udev, p, req_id, req_type, req_value, 0, + req_addr, req_size, 2 * USB_CTRL_GET_TIMEOUT); + if (err < 0) { + netdev_info(dev->netdev, + "unable to request usb[type=%d value=%d] err=%d\n", + req_id, req_value, err); + return err; + } + + return 0; +} + +static int pcan_usb_pro_set_ts(struct peak_usb_device *dev, u16 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETTS, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bitrate(struct peak_usb_device *dev, u32 ccbt) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt); + + /* cache the CCBT value to reuse it before next buson */ + pdev->cached_ccbt = ccbt; + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + /* if bus=on, be sure the bitrate being set before! */ + if (onoff) { + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + pcan_usb_pro_set_bitrate(dev, pdev->cached_ccbt); + } + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_silent(struct peak_usb_device *dev, u8 onoff) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETSILENT, dev->ctrl_idx, onoff); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_filter(struct peak_usb_device *dev, u16 filter_mode) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETFILTR, dev->ctrl_idx, filter_mode); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_set_led(struct peak_usb_device *dev, u8 mode, + u32 timeout) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETLED, dev->ctrl_idx, mode, timeout); + + return pcan_usb_pro_send_cmd(dev, &um); +} + +static int pcan_usb_pro_get_device_id(struct peak_usb_device *dev, + u32 *device_id) +{ + struct pcan_usb_pro_devid *pdn; + struct pcan_usb_pro_msg um; + int err; + u8 *pc; + + pc = pcan_msg_init_empty(&um, dev->cmd_buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_GETDEVID, dev->ctrl_idx); + + err = pcan_usb_pro_send_cmd(dev, &um); + if (err) + return err; + + err = pcan_usb_pro_wait_rsp(dev, &um); + if (err) + return err; + + pdn = (struct pcan_usb_pro_devid *)pc; + if (device_id) + *device_id = le32_to_cpu(pdn->serial_num); + + return err; +} + +static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + u32 ccbt; + + ccbt = (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 0x00800000 : 0; + ccbt |= (bt->sjw - 1) << 24; + ccbt |= (bt->phase_seg2 - 1) << 20; + ccbt |= (bt->prop_seg + bt->phase_seg1 - 1) << 16; /* = tseg1 */ + ccbt |= bt->brp - 1; + + netdev_info(dev->netdev, "setting ccbt=0x%08x\n", ccbt); + + return pcan_usb_pro_set_bitrate(dev, ccbt); +} + +static void pcan_usb_pro_restart_complete(struct urb *urb) +{ + /* can delete usb resources */ + peak_usb_async_complete(urb); + + /* notify candev and netdev */ + peak_usb_restart_complete(urb->context); +} + +/* + * handle restart but in asynchronously way + */ +static int pcan_usb_pro_restart_async(struct peak_usb_device *dev, + struct urb *urb, u8 *buf) +{ + struct pcan_usb_pro_msg um; + + pcan_msg_init_empty(&um, buf, PCAN_USB_MAX_CMD_LEN); + pcan_msg_add_rec(&um, PCAN_USBPRO_SETBUSACT, dev->ctrl_idx, 1); + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + buf, PCAN_USB_MAX_CMD_LEN, + pcan_usb_pro_restart_complete, dev); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static void pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded) +{ + u8 buffer[16]; + + buffer[0] = 0; + buffer[1] = !!loaded; + + pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_FCT, + PCAN_USBPRO_FCT_DRVLD, buffer, sizeof(buffer)); +} + +static inline +struct pcan_usb_pro_interface *pcan_usb_pro_dev_if(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + return pdev->usb_if; +} + +static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxmsg *rx) +{ + const unsigned int ctrl_idx = (rx->len >> 4) & 0x0f; + struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + struct sk_buff *skb; + struct timeval tv; + + skb = alloc_can_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + can_frame->can_id = le32_to_cpu(rx->id); + can_frame->can_dlc = rx->len & 0x0f; + + if (rx->flags & PCAN_USBPRO_EXT) + can_frame->can_id |= CAN_EFF_FLAG; + + if (rx->flags & PCAN_USBPRO_RTR) + can_frame->can_id |= CAN_RTR_FLAG; + else + memcpy(can_frame->data, rx->data, can_frame->can_dlc); + + peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(rx->ts32), &tv); + skb->tstamp = timeval_to_ktime(tv); + + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + + return 0; +} + +static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxstatus *er) +{ + const u32 raw_status = le32_to_cpu(er->status); + const unsigned int ctrl_idx = (er->channel >> 4) & 0x0f; + struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; + struct net_device *netdev = dev->netdev; + struct can_frame *can_frame; + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; + u8 err_mask = 0; + struct sk_buff *skb; + struct timeval tv; + + /* nothing should be sent while in BUS_OFF state */ + if (dev->can.state == CAN_STATE_BUS_OFF) + return 0; + + if (!raw_status) { + /* no error bit (back to active state) */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; + } + + if (raw_status & (PCAN_USBPRO_STATUS_OVERRUN | + PCAN_USBPRO_STATUS_QOVERRUN)) { + /* trick to bypass next comparison and process other errors */ + new_state = CAN_STATE_MAX; + } + + if (raw_status & PCAN_USBPRO_STATUS_BUS) { + new_state = CAN_STATE_BUS_OFF; + } else if (raw_status & PCAN_USBPRO_STATUS_ERROR) { + u32 rx_err_cnt = (le32_to_cpu(er->err_frm) & 0x00ff0000) >> 16; + u32 tx_err_cnt = (le32_to_cpu(er->err_frm) & 0xff000000) >> 24; + + if (rx_err_cnt > 127) + err_mask |= CAN_ERR_CRTL_RX_PASSIVE; + else if (rx_err_cnt > 96) + err_mask |= CAN_ERR_CRTL_RX_WARNING; + + if (tx_err_cnt > 127) + err_mask |= CAN_ERR_CRTL_TX_PASSIVE; + else if (tx_err_cnt > 96) + err_mask |= CAN_ERR_CRTL_TX_WARNING; + + if (err_mask & (CAN_ERR_CRTL_RX_WARNING | + CAN_ERR_CRTL_TX_WARNING)) + new_state = CAN_STATE_ERROR_WARNING; + else if (err_mask & (CAN_ERR_CRTL_RX_PASSIVE | + CAN_ERR_CRTL_TX_PASSIVE)) + new_state = CAN_STATE_ERROR_PASSIVE; + } + + /* donot post any error if current state didn't change */ + if (dev->can.state == new_state) + return 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &can_frame); + if (!skb) + return -ENOMEM; + + switch (new_state) { + case CAN_STATE_BUS_OFF: + can_frame->can_id |= CAN_ERR_BUSOFF; + can_bus_off(netdev); + break; + + case CAN_STATE_ERROR_PASSIVE: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_passive++; + break; + + case CAN_STATE_ERROR_WARNING: + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= err_mask; + dev->can.can_stats.error_warning++; + break; + + case CAN_STATE_ERROR_ACTIVE: + break; + + default: + /* CAN_STATE_MAX (trick to handle other errors) */ + if (raw_status & PCAN_USBPRO_STATUS_OVERRUN) { + can_frame->can_id |= CAN_ERR_PROT; + can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + if (raw_status & PCAN_USBPRO_STATUS_QOVERRUN) { + can_frame->can_id |= CAN_ERR_CRTL; + can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + } + + new_state = CAN_STATE_ERROR_ACTIVE; + break; + } + + dev->can.state = new_state; + + peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(er->ts32), &tv); + skb->tstamp = timeval_to_ktime(tv); + netif_rx(skb); + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += can_frame->can_dlc; + + return 0; +} + +static void pcan_usb_pro_handle_ts(struct pcan_usb_pro_interface *usb_if, + struct pcan_usb_pro_rxts *ts) +{ + /* should wait until clock is stabilized */ + if (usb_if->cm_ignore_count > 0) + usb_if->cm_ignore_count--; + else + peak_usb_set_ts_now(&usb_if->time_ref, + le32_to_cpu(ts->ts64[1])); +} + +/* + * callback for bulk IN urb + */ +static int pcan_usb_pro_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + struct pcan_usb_pro_interface *usb_if = pcan_usb_pro_dev_if(dev); + struct net_device *netdev = dev->netdev; + struct pcan_usb_pro_msg usb_msg; + u8 *rec_ptr, *msg_end; + u16 rec_cnt; + int err = 0; + + rec_ptr = pcan_msg_init(&usb_msg, urb->transfer_buffer, + urb->actual_length); + if (!rec_ptr) { + netdev_err(netdev, "bad msg hdr len %d\n", urb->actual_length); + return -EINVAL; + } + + /* loop reading all the records from the incoming message */ + msg_end = urb->transfer_buffer + urb->actual_length; + rec_cnt = le16_to_cpu(*usb_msg.u.rec_cnt_rd); + for (; rec_cnt > 0; rec_cnt--) { + union pcan_usb_pro_rec *pr = (union pcan_usb_pro_rec *)rec_ptr; + u16 sizeof_rec = pcan_usb_pro_sizeof_rec[pr->data_type]; + + if (!sizeof_rec) { + netdev_err(netdev, + "got unsupported rec in usb msg:\n"); + err = -ENOTSUPP; + break; + } + + /* check if the record goes out of current packet */ + if (rec_ptr + sizeof_rec > msg_end) { + netdev_err(netdev, + "got frag rec: should inc usb rx buf size\n"); + err = -EBADMSG; + break; + } + + switch (pr->data_type) { + case PCAN_USBPRO_RXMSG8: + case PCAN_USBPRO_RXMSG4: + case PCAN_USBPRO_RXMSG0: + case PCAN_USBPRO_RXRTR: + err = pcan_usb_pro_handle_canmsg(usb_if, &pr->rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXSTATUS: + err = pcan_usb_pro_handle_error(usb_if, &pr->rx_status); + if (err < 0) + goto fail; + break; + + case PCAN_USBPRO_RXTS: + pcan_usb_pro_handle_ts(usb_if, &pr->rx_ts); + break; + + default: + netdev_err(netdev, + "unhandled rec type 0x%02x (%d): ignored\n", + pr->data_type, pr->data_type); + break; + } + + rec_ptr += sizeof_rec; + } + +fail: + if (err) + dump_mem("received msg", + urb->transfer_buffer, urb->actual_length); + + return err; +} + +static int pcan_usb_pro_encode_msg(struct peak_usb_device *dev, + struct sk_buff *skb, u8 *obuf, size_t *size) +{ + struct can_frame *cf = (struct can_frame *)skb->data; + u8 data_type, len, flags; + struct pcan_usb_pro_msg usb_msg; + + pcan_msg_init_empty(&usb_msg, obuf, *size); + + if ((cf->can_id & CAN_RTR_FLAG) || (cf->can_dlc == 0)) + data_type = PCAN_USBPRO_TXMSG0; + else if (cf->can_dlc <= 4) + data_type = PCAN_USBPRO_TXMSG4; + else + data_type = PCAN_USBPRO_TXMSG8; + + len = (dev->ctrl_idx << 4) | (cf->can_dlc & 0x0f); + + flags = 0; + if (cf->can_id & CAN_EFF_FLAG) + flags |= 0x02; + if (cf->can_id & CAN_RTR_FLAG) + flags |= 0x01; + + pcan_msg_add_rec(&usb_msg, data_type, 0, flags, len, cf->can_id, + cf->data); + + *size = usb_msg.rec_buffer_len; + + return 0; +} + +static int pcan_usb_pro_start(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + int err; + + err = pcan_usb_pro_set_silent(dev, + dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY); + if (err) + return err; + + /* filter mode: 0-> All OFF; 1->bypass */ + err = pcan_usb_pro_set_filter(dev, 1); + if (err) + return err; + + /* opening first device: */ + if (pdev->usb_if->dev_opened_count == 0) { + /* reset time_ref */ + peak_usb_init_time_ref(&pdev->usb_if->time_ref, &pcan_usb_pro); + + /* ask device to send ts messages */ + err = pcan_usb_pro_set_ts(dev, 1); + } + + pdev->usb_if->dev_opened_count++; + + return err; +} + +/* + * stop interface + * (last chance before set bus off) + */ +static int pcan_usb_pro_stop(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* turn off ts msgs for that interface if no other dev opened */ + if (pdev->usb_if->dev_opened_count == 1) + pcan_usb_pro_set_ts(dev, 0); + + pdev->usb_if->dev_opened_count--; + + return 0; +} + +/* + * called when probing to initialize a device object. + */ +static int pcan_usb_pro_init(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_interface *usb_if; + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* do this for 1st channel only */ + if (!dev->prev_siblings) { + struct pcan_usb_pro_fwinfo fi; + struct pcan_usb_pro_blinfo bi; + int err; + + /* allocate netdevices common structure attached to first one */ + usb_if = kzalloc(sizeof(struct pcan_usb_pro_interface), + GFP_KERNEL); + if (!usb_if) + return -ENOMEM; + + /* number of ts msgs to ignore before taking one into account */ + usb_if->cm_ignore_count = 5; + + /* + * explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx netdevices. + */ + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_FW, + &fi, sizeof(fi)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s firmware info (err %d)\n", + pcan_usb_pro.name, err); + return err; + } + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_BL, + &bi, sizeof(bi)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s bootloader info (err %d)\n", + pcan_usb_pro.name, err); + return err; + } + + dev_info(dev->netdev->dev.parent, + "PEAK-System %s hwrev %u serial %08X.%08X (%u channels)\n", + pcan_usb_pro.name, + bi.hw_rev, bi.serial_num_hi, bi.serial_num_lo, + pcan_usb_pro.ctrl_count); + + /* tell the device the can driver is running */ + pcan_usb_pro_drv_loaded(dev, 1); + } else { + usb_if = pcan_usb_pro_dev_if(dev->prev_siblings); + } + + pdev->usb_if = usb_if; + usb_if->dev[dev->ctrl_idx] = dev; + + /* set LED in default state (end of init phase) */ + pcan_usb_pro_set_led(dev, 0, 1); + + return 0; +} + +static void pcan_usb_pro_exit(struct peak_usb_device *dev) +{ + struct pcan_usb_pro_device *pdev = + container_of(dev, struct pcan_usb_pro_device, dev); + + /* + * when rmmod called before unplug and if down, should reset things + * before leaving + */ + if (dev->can.state != CAN_STATE_STOPPED) { + /* set bus off on the corresponding channel */ + pcan_usb_pro_set_bus(dev, 0); + } + + /* if channel #0 (only) */ + if (dev->ctrl_idx == 0) { + /* turn off calibration message if any device were opened */ + if (pdev->usb_if->dev_opened_count > 0) + pcan_usb_pro_set_ts(dev, 0); + + /* tell the PCAN-USB Pro device the driver is being unloaded */ + pcan_usb_pro_drv_loaded(dev, 0); + } +} + +/* + * called when PCAN-USB Pro adapter is unplugged + */ +static void pcan_usb_pro_free(struct peak_usb_device *dev) +{ + /* last device: can free pcan_usb_pro_interface object now */ + if (!dev->prev_siblings && !dev->next_siblings) + kfree(pcan_usb_pro_dev_if(dev)); +} + +/* + * probe function for new PCAN-USB Pro usb interface + */ +static int pcan_usb_pro_probe(struct usb_interface *intf) +{ + struct usb_host_interface *if_desc; + int i; + + if_desc = intf->altsetting; + + /* check interface endpoint addresses */ + for (i = 0; i < if_desc->desc.bNumEndpoints; i++) { + struct usb_endpoint_descriptor *ep = &if_desc->endpoint[i].desc; + + /* + * below is the list of valid ep addreses. Any other ep address + * is considered as not-CAN interface address => no dev created + */ + switch (ep->bEndpointAddress) { + case PCAN_USBPRO_EP_CMDOUT: + case PCAN_USBPRO_EP_CMDIN: + case PCAN_USBPRO_EP_MSGOUT_0: + case PCAN_USBPRO_EP_MSGOUT_1: + case PCAN_USBPRO_EP_MSGIN: + case PCAN_USBPRO_EP_UNUSED: + break; + default: + return -ENODEV; + } + } + + return 0; +} + +/* + * describe the PCAN-USB Pro adapter + */ +struct peak_usb_adapter pcan_usb_pro = { + .name = "PCAN-USB Pro", + .device_id = PCAN_USBPRO_PRODUCT_ID, + .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT, + .clock = { + .freq = PCAN_USBPRO_CRYSTAL_HZ, + }, + .bittiming_const = { + .name = "pcan_usb_pro", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_pro_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_USBPRO_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_USBPRO_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, + .dev_init = pcan_usb_pro_init, + .dev_exit = pcan_usb_pro_exit, + .dev_free = pcan_usb_pro_free, + .dev_set_bus = pcan_usb_pro_set_bus, + .dev_set_bittiming = pcan_usb_pro_set_bittiming, + .dev_get_device_id = pcan_usb_pro_get_device_id, + .dev_decode_buf = pcan_usb_pro_decode_buf, + .dev_encode_msg = pcan_usb_pro_encode_msg, + .dev_start = pcan_usb_pro_start, + .dev_stop = pcan_usb_pro_stop, + .dev_restart_async = pcan_usb_pro_restart_async, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h new file mode 100644 index 000000000000..a869918c5620 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -0,0 +1,178 @@ +/* + * CAN driver for PEAK System PCAN-USB Pro adapter + * Derived from the PCAN project file driver/src/pcan_usbpro_fw.h + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2012 Stephane Grosjean + * + * 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; version 2 of the License. + * + * 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. + */ +#ifndef PCAN_USB_PRO_H +#define PCAN_USB_PRO_H + +/* + * USB Vendor request data types + */ +#define PCAN_USBPRO_REQ_INFO 0 +#define PCAN_USBPRO_REQ_FCT 2 + +/* Vendor Request value for XXX_INFO */ +#define PCAN_USBPRO_INFO_BL 0 +#define PCAN_USBPRO_INFO_FW 1 + +/* Vendor Request value for XXX_FCT */ +#define PCAN_USBPRO_FCT_DRVLD 5 /* tell device driver is loaded */ + +/* PCAN_USBPRO_INFO_BL vendor request record type */ +struct __packed pcan_usb_pro_blinfo { + u32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + u32 serial_num_hi; + u32 serial_num_lo; + u32 hw_type; + u32 hw_rev; +}; + +/* PCAN_USBPRO_INFO_FW vendor request record type */ +struct __packed pcan_usb_pro_fwinfo { + u32 ctrl_type; + u8 version[4]; + u8 day; + u8 month; + u8 year; + u8 dummy; + u32 fw_type; +}; + +/* + * USB Command record types + */ +#define PCAN_USBPRO_SETBTR 0x02 +#define PCAN_USBPRO_SETBUSACT 0x04 +#define PCAN_USBPRO_SETSILENT 0x05 +#define PCAN_USBPRO_SETFILTR 0x0a +#define PCAN_USBPRO_SETTS 0x10 +#define PCAN_USBPRO_GETDEVID 0x12 +#define PCAN_USBPRO_SETLED 0x1C +#define PCAN_USBPRO_RXMSG8 0x80 +#define PCAN_USBPRO_RXMSG4 0x81 +#define PCAN_USBPRO_RXMSG0 0x82 +#define PCAN_USBPRO_RXRTR 0x83 +#define PCAN_USBPRO_RXSTATUS 0x84 +#define PCAN_USBPRO_RXTS 0x85 +#define PCAN_USBPRO_TXMSG8 0x41 +#define PCAN_USBPRO_TXMSG4 0x42 +#define PCAN_USBPRO_TXMSG0 0x43 + +/* record structures */ +struct __packed pcan_usb_pro_btr { + u8 data_type; + u8 channel; + u16 dummy; + u32 CCBT; +}; + +struct __packed pcan_usb_pro_busact { + u8 data_type; + u8 channel; + u16 onoff; +}; + +struct __packed pcan_usb_pro_silent { + u8 data_type; + u8 channel; + u16 onoff; +}; + +struct __packed pcan_usb_pro_filter { + u8 data_type; + u8 dummy; + u16 filter_mode; +}; + +struct __packed pcan_usb_pro_setts { + u8 data_type; + u8 dummy; + u16 mode; +}; + +struct __packed pcan_usb_pro_devid { + u8 data_type; + u8 channel; + u16 dummy; + u32 serial_num; +}; + +struct __packed pcan_usb_pro_setled { + u8 data_type; + u8 channel; + u16 mode; + u32 timeout; +}; + +struct __packed pcan_usb_pro_rxmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + u32 ts32; + u32 id; + + u8 data[8]; +}; + +#define PCAN_USBPRO_STATUS_ERROR 0x0001 +#define PCAN_USBPRO_STATUS_BUS 0x0002 +#define PCAN_USBPRO_STATUS_OVERRUN 0x0004 +#define PCAN_USBPRO_STATUS_QOVERRUN 0x0008 + +struct __packed pcan_usb_pro_rxstatus { + u8 data_type; + u8 channel; + u16 status; + u32 ts32; + u32 err_frm; +}; + +struct __packed pcan_usb_pro_rxts { + u8 data_type; + u8 dummy[3]; + u32 ts64[2]; +}; + +struct __packed pcan_usb_pro_txmsg { + u8 data_type; + u8 client; + u8 flags; + u8 len; + u32 id; + u8 data[8]; +}; + +union pcan_usb_pro_rec { + u8 data_type; + struct pcan_usb_pro_btr btr; + struct pcan_usb_pro_busact bus_act; + struct pcan_usb_pro_silent silent_mode; + struct pcan_usb_pro_filter filter_mode; + struct pcan_usb_pro_setts ts; + struct pcan_usb_pro_devid dev_id; + struct pcan_usb_pro_setled set_led; + struct pcan_usb_pro_rxmsg rx_msg; + struct pcan_usb_pro_rxstatus rx_status; + struct pcan_usb_pro_rxts rx_ts; + struct pcan_usb_pro_txmsg tx_msg; +}; + +#endif