linux-can.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Add support for PEAK System PCAN-USB Pro adapter
@ 2011-12-22 13:14 Stephane Grosjean
  2011-12-23 19:57 ` Sebastian Haas
  2012-01-10 11:21 ` Wolfgang Grandegger
  0 siblings, 2 replies; 4+ messages in thread
From: Stephane Grosjean @ 2011-12-22 13:14 UTC (permalink / raw)
  To: Oliver Hartkopp; +Cc: Linux CAN mailing list

[-- Attachment #1: Type: text/plain, Size: 36722 bytes --]

From a1b285074b883edc3dccc6aacafb18407e98e18a Mon Sep 17 00:00:00 2001
From: Stephane Grosjean <s.grosjean@peak-system.com>
Date: Thu, 22 Dec 2011 13:59:00 +0100
Subject: [PATCH] Add support for PEAK System PCAN-USB Pro adapter

---
 drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 1092 +++++++++++++++++++++++++++
 drivers/net/can/usb/peak_usb/pcan_usb_pro.h |  352 +++++++++
 2 files changed, 1444 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.c
 create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.h

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 0000000..0747395
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
@@ -0,0 +1,1092 @@
+/*
+ * CAN driver for PEAK System PCAN-USB Pro adapter
+ *
+ * Copyright (C) 2011-2012 PEAK-System GmbH
+ *
+ * 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 <linux/netdevice.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include "peak_usb.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_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
+
+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;
+};
+
+struct pcan_usb_pro_device {
+	struct peak_usb_device peak_usb_device; /* must be the first member */
+	struct pcan_usb_pro_interface *usb_if;
+};
+
+/* 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;
+};
+
+static int pcan_usb_pro_sizeof_rec(u8 data_type)
+{
+	switch (data_type) {
+	case PCAN_USBPRO_RXMSG8:
+		return sizeof(struct pcan_usb_pro_rxmsg);
+	case PCAN_USBPRO_RXMSG4:
+		return sizeof(struct pcan_usb_pro_rxmsg) - 4;
+	case PCAN_USBPRO_RXMSG0:
+	case PCAN_USBPRO_RXRTR:
+		return sizeof(struct pcan_usb_pro_rxmsg) - 8;
+	case PCAN_USBPRO_RXSTATUS:
+		return sizeof(struct pcan_usb_pro_rxstatus);
+	case PCAN_USBPRO_RXTS:
+		return sizeof(struct pcan_usb_pro_rxts);
+	case PCAN_USBPRO_TXMSG8:
+		return sizeof(struct pcan_usb_pro_txmsg);
+	case PCAN_USBPRO_TXMSG4:
+		return sizeof(struct pcan_usb_pro_txmsg) - 4;
+	case PCAN_USBPRO_TXMSG0:
+		return sizeof(struct pcan_usb_pro_txmsg) - 8;
+	case PCAN_USBPRO_GETBTR:
+	case PCAN_USBPRO_SETBTR:
+		return sizeof(struct pcan_usb_pro_btr);
+	case PCAN_USBPRO_SETSILENT:
+		return sizeof(struct pcan_usb_pro_silent);
+	case PCAN_USBPRO_SETDEVID:
+	case PCAN_USBPRO_GETDEVID:
+		return sizeof(struct pcan_usb_pro_devid);
+	case PCAN_USBPRO_GETBUSACT:
+	case PCAN_USBPRO_SETBUSACT:
+		return sizeof(struct pcan_usb_pro_busact);
+	case PCAN_USBPRO_SETWARNLIM:
+		return sizeof(struct pcan_usb_pro_warnlim);
+	case PCAN_USBPRO_SETLKUP:
+		return sizeof(struct pcan_usb_pro_lkup);
+	case PCAN_USBPRO_SETLKUPGRP:
+		return sizeof(struct pcan_usb_pro_lkupgrp);
+	case PCAN_USBPRO_SETFILTR:
+		return sizeof(struct pcan_usb_pro_filter);
+	case PCAN_USBPRO_SETRST:
+		return sizeof(struct pcan_usb_pro_reset);
+	case PCAN_USBPRO_SETERRFRM:
+		return sizeof(struct pcan_usb_pro_errfrm);
+	case PCAN_USBPRO_GETBUSSTAT:
+		return sizeof(struct pcan_usb_pro_busstat);
+	case PCAN_USBPRO_SETREG:
+		return sizeof(struct pcan_usb_pro_setreg);
+	case PCAN_USBPRO_GETREG:
+		return sizeof(struct pcan_usb_pro_getreg);
+	case PCAN_USBPRO_SETTS:
+		return sizeof(struct pcan_usb_pro_setts);
+	case PCAN_USBPRO_SETSTR:
+		return sizeof(struct pcan_usb_pro_setstr);
+	case PCAN_USBPRO_GETSTR:
+		return sizeof(struct pcan_usb_pro_getstr);
+	case PCAN_USBPRO_STRING:
+		return sizeof(struct pcan_usb_pro_str);
+	case PCAN_USBPRO_SAVE:
+		return sizeof(struct pcan_usb_pro_save);
+	case PCAN_USBPRO_PDELAY:
+		return sizeof(struct pcan_usb_pro_pdelay);
+	case PCAN_USBPRO_TSARG:
+		return sizeof(struct pcan_usb_pro_tsarg);
+	case PCAN_USBPRO_ERRID:
+		return sizeof(struct pcan_usb_pro_errid);
+	case PCAN_USBPRO_ERRNOW:
+		return sizeof(struct pcan_usb_pro_errnow);
+	case PCAN_USBPRO_SETSFILER:
+		return sizeof(struct pcan_usb_pro_sfiller);
+	case PCAN_USBPRO_SETLED:
+		return sizeof(struct pcan_usb_pro_setled);
+	default:
+		pr_info("%s: %s(%d): unsupported data type\n",
+			PCAN_USB_DRIVER_NAME, __func__, data_type);
+		break;
+	}
+
+	return -1;
+}
+
+/*
+ * initialize PCAN-USB Pro message data structure
+ */
+static u8 *pcan_usb_pro_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_usb_pro_msg_init_empty(struct pcan_usb_pro_msg *pm,
+	void *buffer_addr, int buffer_size)
+{
+	u8 *pr = pcan_usb_pro_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_usb_pro_add_rec(struct pcan_usb_pro_msg *pm, int id, ...)
+{
+	int l, 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++ = (u8)va_arg(ap, int);
+		*pc++ = (u8)va_arg(ap, int);
+		*pc++ = (u8)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_GETBUSACT:
+	case PCAN_USBPRO_GETBUSSTAT:
+		*pc++ = (u8)va_arg(ap, int);
+		break;
+
+	case PCAN_USBPRO_SETBTR:
+	case PCAN_USBPRO_GETBTR:
+	case PCAN_USBPRO_SETDEVID:
+	case PCAN_USBPRO_GETDEVID:
+		*pc++ = (u8)va_arg(ap, int);
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	case PCAN_USBPRO_SETBUSACT:
+	case PCAN_USBPRO_SETSILENT:
+	case PCAN_USBPRO_SETWARNLIM:
+	case PCAN_USBPRO_SETFILTR:
+	case PCAN_USBPRO_SETRST:
+	case PCAN_USBPRO_SETERRFRM:
+	case PCAN_USBPRO_TSARG:
+	case PCAN_USBPRO_ERRNOW:
+		*pc++ = (u8)va_arg(ap, int);
+		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
+		pc += 2;
+		break;
+
+	case PCAN_USBPRO_SETLKUP:
+	case PCAN_USBPRO_SETLED:
+		*pc++ = (u8)va_arg(ap, int);
+		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	case PCAN_USBPRO_SETLKUPGRP:
+		*pc++ = (u8)va_arg(ap, int);
+		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	case PCAN_USBPRO_SETREG:
+		*pc++ = (u8)va_arg(ap, int);
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	case PCAN_USBPRO_GETREG:
+		*pc++ = (u8)va_arg(ap, int);
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	case PCAN_USBPRO_SETTS:
+	case PCAN_USBPRO_PDELAY:
+		pc++;
+		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
+		pc += 2;
+		break;
+
+	case PCAN_USBPRO_SETBUSLAST:
+		*pc++ = (u8)va_arg(ap, int);
+		pc++;
+		*pc++ = (u8)va_arg(ap, int);
+		pc += 2;
+		*(u16 *)pc = cpu_to_le16(4096);
+		pc += 2;
+		break;
+
+	case PCAN_USBPRO_SETSTR:
+		*pc++ = (u8)va_arg(ap, int);
+		*pc++ = (u8)va_arg(ap, int);
+		*pc++ = (u8)va_arg(ap, int);
+		memcpy(pc, va_arg(ap, u8 *), 60);
+		pc += 60;
+		break;
+
+	case PCAN_USBPRO_GETSTR:
+	case PCAN_USBPRO_SAVE:
+		*pc++ = (u8)va_arg(ap, int);
+		pc += 2;
+		break;
+
+	case PCAN_USBPRO_STRING:
+		*pc++ = (u8)va_arg(ap, int);
+		pc += 2;
+		memcpy(pc, va_arg(ap, u8 *), 250);
+		pc += 250;
+		break;
+
+	case PCAN_USBPRO_ERRID:
+		*pc++ = (u8)va_arg(ap, int);
+		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
+		pc += 2;
+		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
+		pc += 2;
+		break;
+
+	case PCAN_USBPRO_SETSFILER:
+		*pc++ = (u8)va_arg(ap, int);
+		pc += 2;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
+		pc += 4;
+		break;
+
+	default:
+		pr_info("%s: %s(): unknown data type %02Xh (%d)\n",
+			PCAN_USB_DRIVER_NAME, __func__, id, id);
+		pc--;
+		break;
+	}
+
+	l = pc - pm->rec_ptr;
+	if (l > 0) {
+		*pm->u.rec_cnt = cpu_to_le32(*pm->u.rec_cnt+1);
+		*(pm->rec_ptr) = (u8)id;
+
+		pm->rec_ptr = pc;
+		pm->rec_buffer_len += l;
+	}
+
+	va_end(ap);
+
+	return l;
+}
+
+/*
+ * 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;
+		int 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;
+
+		if (actual_length < PCAN_USBPRO_MSG_HEADER_LEN) {
+			netdev_err(dev->netdev,
+				"got abnormal too small rsp (len=%d)\n",
+				actual_length);
+			err = -EBADMSG;
+			break;
+		}
+
+		pc = pcan_usb_pro_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 <= 0) {
+				netdev_err(dev->netdev,
+					"got unprocessed record in msg\n");
+				dump_mem("rcvd rsp msg", pum->u.rec_buffer,
+					actual_length);
+				err = -EBADMSG;
+				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;
+
+	case PCAN_USBPRO_REQ_INFO:
+	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);
+	else
+		err = 0;
+
+	return err;
+}
+
+static int pcan_usb_pro_set_ts(struct peak_usb_device *dev, u16 onoff)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETTS, onoff);
+
+	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;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_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;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_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;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_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;
+	u8 tmp[32];
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_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_msg um;
+	struct pcan_usb_pro_devid *pdn;
+	u8 tmp[32], *pc;
+	int err;
+
+	pc = pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_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)
+{
+	struct pcan_usb_pro_msg um;
+	u8 tmp[32];
+	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_dbg(dev->netdev, "ccbt=0x%08x\n", ccbt);
+
+	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
+	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt);
+
+	return pcan_usb_pro_send_cmd(dev, &um);
+}
+
+static void pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded)
+{
+	u8 buffer[16];
+
+	buffer[0] = 0;
+	buffer[1] = loaded ? 1 : 0;
+
+	pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_FCT,
+		PCAN_USBPRO_FCT_DRIVER_LOADED, buffer, sizeof(buffer));
+}
+
+static inline
+struct pcan_usb_pro_interface *pcan_usb_pro_dev_if(struct peak_usb_device *dev)
+{
+	return ((struct pcan_usb_pro_device *)dev)->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_RTR)
+		can_frame->can_id |= CAN_RTR_FLAG;
+	if (rx->flags & PCAN_USBPRO_EXT)
+		can_frame->can_id |= CAN_EFF_FLAG;
+
+	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;
+	struct sk_buff *skb;
+	struct timeval tv;
+
+	skb = alloc_can_err_skb(netdev, &can_frame);
+	if (!skb)
+		return -ENOMEM;
+
+	memset(can_frame, '\0', sizeof(struct can_frame));
+
+	if (raw_status & FW_USBPRO_STATUS_BUS_S) {
+		can_frame->can_id |= CAN_ERR_BUSOFF;
+		can_bus_off(netdev);
+
+	} else if (raw_status & FW_USBPRO_STATUS_ERROR_S) {
+		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) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+			dev->can.can_stats.error_passive++;
+
+		} else if (rx_err_cnt > 96) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
+			dev->can.can_stats.error_warning++;
+		}
+
+		if (tx_err_cnt > 127) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+			dev->can.can_stats.error_passive++;
+
+		} else if (tx_err_cnt > 96) {
+			can_frame->can_id |= CAN_ERR_CRTL;
+			can_frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
+			dev->can.can_stats.error_warning++;
+		}
+	}
+
+	if (raw_status & FW_USBPRO_STATUS_OVERRUN_S) {
+		can_frame->can_id |= CAN_ERR_PROT;
+		can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD;
+	}
+
+	if (raw_status & FW_USBPRO_STATUS_QOVERRUN_S) {
+		can_frame->can_id |= CAN_ERR_CRTL;
+		can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+	}
+
+	if (can_frame->can_id != CAN_ERR_FLAG) {
+		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++;
+	} else {
+		kfree_skb(skb);
+	}
+
+	return 0;
+}
+
+static int 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]));
+
+	return 0;
+}
+
+/*
+ * 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_usb_pro_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;
+		int sizeof_rec = pcan_usb_pro_sizeof_rec(pr->data_type);
+
+		if (sizeof_rec <= 0) {
+			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:
+			err = 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;
+	int err = 0;
+
+	pcan_usb_pro_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;
+
+	err = pcan_usb_pro_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 = (struct pcan_usb_pro_device *)dev;
+	int err;
+
+	err = pcan_usb_pro_set_silent(dev,
+		(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY));
+	if (err)
+		goto start_failed;
+
+	/* Set filter mode: 0-> All OFF; 1->bypass */
+	err = pcan_usb_pro_set_filter(dev, 1);
+	if (err)
+		goto start_failed;
+
+	/* 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++;
+
+start_failed:
+	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 = (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 = (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;
+
+		/* tell the device the can driver is running */
+		pcan_usb_pro_drv_loaded(dev, 1);
+
+		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
+			PCAN_USBPRO_INFO_FIRMWARE, &fi, sizeof(fi)) >= 0)
+			netdev_info(dev->netdev,
+				"%s fw v%d.%d.%d (%02d/%02d/%02d) fw 0x%08x\n",
+				pcan_usb_pro.name,
+				fi.version[0], fi.version[1], fi.version[2],
+				fi.day, fi.month, fi.year, fi.fw_type);
+
+		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
+			PCAN_USBPRO_INFO_BOOTLOADER, &bi, sizeof(bi)) >= 0) {
+			netdev_info(dev->netdev,
+				"bootloader v%d.%d.%d (%02d/%02d/%02d)\n",
+				bi.version[0], bi.version[1], bi.version[2],
+				bi.day, bi.month, bi.year);
+
+			netdev_info(dev->netdev,
+				"serial %08X.%08X hw 0x%08x rev 0x%08x\n",
+				bi.serial_num_hi, bi.serial_num_lo,
+				bi.hw_type, bi.hw_rev);
+
+			dev->device_rev = (u8)bi.hw_rev;
+		}
+
+		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;
+
+	} 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 = (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 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. All 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:
+			/* CAN usb interface unused ep */
+		case 0x83:
+			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 commands/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,
+};
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 0000000..66952cb
--- /dev/null
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
@@ -0,0 +1,352 @@
+/*
+ * CAN driver for PEAK System PCAN-USB Pro adapter
+ *
+ * Copyright (C) 2011-2012 PEAK-System GmbH
+ *
+ * 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 wValue for XXX_INFO */
+#define PCAN_USBPRO_INFO_BOOTLOADER	0
+#define PCAN_USBPRO_INFO_FIRMWARE	1
+
+/* Vendor Request wValue for XXX_FCT */
+#define PCAN_USBPRO_FCT_DRIVER_LOADED	5
+
+/* PCAN_USBPRO_INFO_BOOTLOADER 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_FIRMWARE 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_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_RXBUSLAST	0x86
+#define PCAN_USBPRO_TXMSG8	0x41
+#define PCAN_USBPRO_TXMSG4	0x42
+#define PCAN_USBPRO_TXMSG0	0x43
+#define PCAN_USBPRO_GETBTR	0x01
+#define PCAN_USBPRO_SETBTR	0x02
+#define PCAN_USBPRO_GETBUSACT	0x03
+#define PCAN_USBPRO_SETBUSACT	0x04
+#define PCAN_USBPRO_SETSILENT	0x05
+#define PCAN_USBPRO_SETDEVID	0x06
+#define PCAN_USBPRO_SETWARNLIM	0x07
+#define PCAN_USBPRO_SETLKUP	0x08
+#define PCAN_USBPRO_SETLKUPGRP	0x09
+#define PCAN_USBPRO_SETFILTR	0x0a
+#define PCAN_USBPRO_SETRST	0x0b
+#define PCAN_USBPRO_SETERRFRM	0x0c
+#define PCAN_USBPRO_GETBUSSTAT	0x0D
+#define PCAN_USBPRO_SETREG	0x0e
+#define PCAN_USBPRO_GETREG	0x0f
+#define PCAN_USBPRO_SETTS	0x10
+#define PCAN_USBPRO_SETBUSLAST	0x11
+#define PCAN_USBPRO_GETDEVID	0x12
+#define PCAN_USBPRO_SETSTR	0x13
+#define PCAN_USBPRO_GETSTR	0x14
+#define PCAN_USBPRO_STRING	0x15
+#define PCAN_USBPRO_SAVE	0x16
+#define PCAN_USBPRO_PDELAY	0x17
+#define PCAN_USBPRO_TSARG	0x18
+#define PCAN_USBPRO_ERRID	0x19
+#define PCAN_USBPRO_ERRNOW	0x1A
+#define PCAN_USBPRO_SETSFILER	0x1B
+#define PCAN_USBPRO_SETLED	0x1C
+
+/* record structures */
+struct __packed pcan_usb_pro_rxmsg {
+	u8  data_type;
+	u8  client;
+	u8  flags;
+	u8  len;
+	u32 ts32;
+	u32 id;
+
+	u8  data[8];
+};
+
+#define FW_USBPRO_STATUS_ERROR_S	0x0001
+#define FW_USBPRO_STATUS_BUS_S	0x0002
+#define FW_USBPRO_STATUS_OVERRUN_S	0x0004
+#define FW_USBPRO_STATUS_QOVERRUN_S	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_buslast {
+	u8  data_type;
+	u8  channel;
+	u16 buslast_val;
+	u32 ts32;
+};
+
+struct __packed pcan_usb_pro_txmsg {
+	u8  data_type;
+	u8  client;
+	u8  flags;
+	u8  len;
+	u32 id;
+	u8  data[8];
+};
+
+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_devid {
+	u8  data_type;
+	u8  channel;
+	u16 dummy;
+	u32 serial_num;
+};
+
+struct __packed pcan_usb_pro_warnlim {
+	u8  data_type;
+	u8  channel;
+	u16 warning_limit;
+};
+
+struct __packed pcan_usb_pro_lkup {
+	u8  data_type;
+	u8  channel;
+	u16 id_type;
+	u32 id;
+};
+
+struct __packed pcan_usb_pro_lkupgrp {
+	u8  data_type;
+	u8  channel;
+	u16 id_type;
+	u32 id_start;
+	u32 id_end;
+};
+
+struct __packed pcan_usb_pro_filter {
+	u8  data_type;
+	u8  dummy;
+	u16 filter_mode;
+};
+
+struct __packed pcan_usb_pro_reset {
+	u8  data_type;
+	u8  channel;
+	u16 reset;
+};
+
+struct __packed pcan_usb_pro_errfrm {
+	u8  data_type;
+	u8  channel;
+	u16 mode;
+};
+
+struct __packed pcan_usb_pro_busstat {
+	u8  data_type;
+	u8  channel;
+	u16 status;
+};
+
+struct __packed pcan_usb_pro_setreg {
+	u8  data_type;
+	u8  irq_off;
+	u16 dummy;
+	u32 address;
+	u32 value;
+	u32 mask;
+};
+
+struct __packed pcan_usb_pro_getreg {
+	u8  data_type;
+	u8  irq_off;
+	u16 dummy;
+	u32 address;
+	u32 value;
+};
+
+struct __packed pcan_usb_pro_setts {
+	u8  data_type;
+	u8  dummy;
+	u16 mode;
+};
+
+struct __packed pcan_usb_pro_setbl {
+	u8  data_type;
+	u8  channel;
+	u8  dummy;
+	u8  mode;
+	u16 prescaler;
+	u16 sampletimequanta;
+};
+
+struct __packed pcan_usb_pro_setstr {
+	u8  data_type;
+	u8  channel;
+	u8  offset;
+	u8  len;
+	u8  data[60];
+};
+
+struct __packed pcan_usb_pro_getstr {
+	u8  data_type;
+	u8  channel;
+	u16 dummy;
+};
+
+struct __packed pcan_usb_pro_str {
+	u8  data_type;
+	u8  channel;
+	u16 dummy;
+	u8  data[250];
+};
+
+struct __packed pcan_usb_pro_save {
+	u8  data_type;
+	u8  channel;
+	u16 dummy;
+};
+
+struct __packed pcan_usb_pro_pdelay {
+	u8  data_type;
+	u8  dummy;
+	u16 delay;
+};
+
+struct __packed pcan_usb_pro_tsarg {
+	u8  data_type;
+	u8  channel;
+	u16 start_or_end;
+};
+
+struct __packed pcan_usb_pro_errid {
+	u8  data_type;
+	u8  channel;
+	u16 bit_pos;
+	u32 id;
+	u16 ok_counter;
+	u16 error_counter;
+};
+
+struct __packed pcan_usb_pro_errnow {
+	u8  data_type;
+	u8  channel;
+	u16 bit_pos;
+};
+
+struct __packed pcan_usb_pro_sfiller {
+	u8  data_type;
+	u8  channel;
+	u16 dummy;
+	u32 accmask;
+	u32 acccode;
+};
+
+struct __packed pcan_usb_pro_setled {
+	u8  data_type;
+	u8  channel;
+	u16 mode;
+	u32 timeout;
+};
+
+union pcan_usb_pro_rec {
+	u8	data_type;
+	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_buslast	rx_buslast;
+	struct pcan_usb_pro_txmsg	tx_msg;
+	struct pcan_usb_pro_btr	btr;
+	struct pcan_usb_pro_busact	bus_act;
+	struct pcan_usb_pro_silent	silent_mode;
+	struct pcan_usb_pro_devid	dev_id;
+	struct pcan_usb_pro_warnlim	warn_lim;
+	struct pcan_usb_pro_lkup	lkup_expl;
+	struct pcan_usb_pro_lkupgrp	lkup_group;
+	struct pcan_usb_pro_filter	filter_mode;
+	struct pcan_usb_pro_reset	rst_mode;
+	struct pcan_usb_pro_errfrm	err_frame;
+	struct pcan_usb_pro_busstat	bus_status;
+	struct pcan_usb_pro_setreg	set_reg;
+	struct pcan_usb_pro_getreg	get_reg;
+	struct pcan_usb_pro_setts	ts;
+	struct pcan_usb_pro_setbl	bus_last;
+	struct pcan_usb_pro_setstr	set_str;
+	struct pcan_usb_pro_getstr	get_str;
+	struct pcan_usb_pro_str	str;
+	struct pcan_usb_pro_save	save_eeprom;
+	struct pcan_usb_pro_pdelay	p_delay;
+	struct pcan_usb_pro_tsarg	ts_arg;
+	struct pcan_usb_pro_errid	err_id;
+	struct pcan_usb_pro_errnow	err_now;
+	struct pcan_usb_pro_sfiller	soft_filler;
+	struct pcan_usb_pro_setled	set_can_led;
+};
+
+#endif
-- 
1.7.1



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] Add support for PEAK System PCAN-USB Pro adapter
  2011-12-22 13:14 [PATCH] Add support for PEAK System PCAN-USB Pro adapter Stephane Grosjean
@ 2011-12-23 19:57 ` Sebastian Haas
  2011-12-26 10:55   ` Grosjean Stephane
  2012-01-10 11:21 ` Wolfgang Grandegger
  1 sibling, 1 reply; 4+ messages in thread
From: Sebastian Haas @ 2011-12-23 19:57 UTC (permalink / raw)
  To: Stephane Grosjean; +Cc: Oliver Hartkopp, Linux CAN mailing list

Hi Stéphane,

just some minor annotations.

Cheers and Merry Christmas,
  Sebastian

Am 22.12.2011 14:14, schrieb Stephane Grosjean:
> +static int pcan_usb_pro_sizeof_rec(u8 data_type)
> +{
> +	switch (data_type) {
> +	case PCAN_USBPRO_RXMSG8:
> +		return sizeof(struct pcan_usb_pro_rxmsg);
> +	case PCAN_USBPRO_RXMSG4:
> +		return sizeof(struct pcan_usb_pro_rxmsg) - 4;
> +	case PCAN_USBPRO_RXMSG0:
> +	case PCAN_USBPRO_RXRTR:
> +		return sizeof(struct pcan_usb_pro_rxmsg) - 8;
> +	case PCAN_USBPRO_RXSTATUS:
> +		return sizeof(struct pcan_usb_pro_rxstatus);
...
I wonder if it is possible to use some kind of a table here to improve 
readability and reduce code.
...
> +	case PCAN_USBPRO_SETLED:
> +		return sizeof(struct pcan_usb_pro_setled);
> +	default:
> +		pr_info("%s: %s(%d): unsupported data type\n",
> +			PCAN_USB_DRIVER_NAME, __func__, data_type);
> +		break;
> +	}
> +
> +	return -1;
> +}
> +static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev,
> +	struct can_bittiming *bt)
> +{
> +	struct pcan_usb_pro_msg um;
> +	u8 tmp[32];
> +	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;
Does the PCAN-USB uses a NXP LPC21xx controller?
> +
> +	netdev_dbg(dev->netdev, "ccbt=0x%08x\n", ccbt);
> +
> +	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt);
> +
> +	return pcan_usb_pro_send_cmd(dev,&um);
> +}
> +/*
> + * 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_usb_pro_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;
> +		int sizeof_rec = pcan_usb_pro_sizeof_rec(pr->data_type);
> +
> +		if (sizeof_rec<= 0) {
> +			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:
> +			err = pcan_usb_pro_handle_ts(usb_if,&pr->rx_ts);
No handling in error case, is that right?
> +			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;
> +	int err = 0;
> +
> +	pcan_usb_pro_msg_init_empty(&usb_msg, obuf, *size);
> +
> +	if ((cf->can_id&  CAN_RTR_FLAG) || (cf->can_dlc == 0))
> +		data_type = PCAN_USBPRO_TXMSG0;
> +
Remove empty line.
> +	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;
> +
> +	err = pcan_usb_pro_add_rec(&usb_msg, data_type, 0, flags, len,
> +		cf->can_id, cf->data);
\err\ is not checked.
> +
> +	*size = usb_msg.rec_buffer_len;
> +
> +	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 = (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;
> +
> +		/* tell the device the can driver is running */
> +		pcan_usb_pro_drv_loaded(dev, 1);
> +
> +		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
> +			PCAN_USBPRO_INFO_FIRMWARE,&fi, sizeof(fi))>= 0)
> +			netdev_info(dev->netdev,
> +				"%s fw v%d.%d.%d (%02d/%02d/%02d) fw 0x%08x\n",
> +				pcan_usb_pro.name,
> +				fi.version[0], fi.version[1], fi.version[2],
> +				fi.day, fi.month, fi.year, fi.fw_type);
> +
> +		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
> +			PCAN_USBPRO_INFO_BOOTLOADER,&bi, sizeof(bi))>= 0) {
> +			netdev_info(dev->netdev,
> +				"bootloader v%d.%d.%d (%02d/%02d/%02d)\n",
> +				bi.version[0], bi.version[1], bi.version[2],
> +				bi.day, bi.month, bi.year);
> +
> +			netdev_info(dev->netdev,
> +				"serial %08X.%08X hw 0x%08x rev 0x%08x\n",
> +				bi.serial_num_hi, bi.serial_num_lo,
> +				bi.hw_type, bi.hw_rev);
> +
> +			dev->device_rev = (u8)bi.hw_rev;
> +		}
> +
> +		usb_if = kzalloc(sizeof(struct pcan_usb_pro_interface),
> +			GFP_KERNEL);
> +		if (!usb_if)
> +			return -ENOMEM;
Is it necessary to call pcan_usb_pro_drv_loaded(dev, 0) here?
> +
> +		/* number of ts msgs to ignore before taking one into account */
> +		usb_if->cm_ignore_count = 5;
> +
Remove empty line.
> +	} 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 = (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)
> +
Remove empty line. Brackets may increase readability here.
> +		/* 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 driver is being unloaded */
> +		pcan_usb_pro_drv_loaded(dev, 0);
> +	}
> +}

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Add support for PEAK System PCAN-USB Pro adapter
  2011-12-23 19:57 ` Sebastian Haas
@ 2011-12-26 10:55   ` Grosjean Stephane
  0 siblings, 0 replies; 4+ messages in thread
From: Grosjean Stephane @ 2011-12-26 10:55 UTC (permalink / raw)
  To: Sebastian Haas; +Cc: Oliver Hartkopp, Linux CAN mailing list

Hello Sebastian,

Le 23/12/2011 20:57, Sebastian Haas a écrit :
> Hi Stéphane,
>
> just some minor annotations.
>
> Cheers and Merry Christmas,
>  Sebastian
>
> Am 22.12.2011 14:14, schrieb Stephane Grosjean:
>> +static int pcan_usb_pro_sizeof_rec(u8 data_type)
>> +{
>> +    switch (data_type) {
>> +    case PCAN_USBPRO_RXMSG8:
>> +        return sizeof(struct pcan_usb_pro_rxmsg);
>> +    case PCAN_USBPRO_RXMSG4:
>> +        return sizeof(struct pcan_usb_pro_rxmsg) - 4;
>> +    case PCAN_USBPRO_RXMSG0:
>> +    case PCAN_USBPRO_RXRTR:
>> +        return sizeof(struct pcan_usb_pro_rxmsg) - 8;
>> +    case PCAN_USBPRO_RXSTATUS:
>> +        return sizeof(struct pcan_usb_pro_rxstatus);
> ...
> I wonder if it is possible to use some kind of a table here to improve 
> readability and reduce code.
> ...
Yes it is... You all are right: I changed that ugly function with 
something like that:

/* record size array */
static u16 pcan_usb_pro_sizeof_rec[256] = {
         [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),
...

and changed the (2) function calls from:

rec_len = pcan_usb_pro_sizeof_rec(x);
if (rec_len <= 0) {

to:

rec_len = pcan_usb_pro_sizeof_rec[x];
if (!rec_len) {

(knowing that "x" is always a BYTE value)

>> +    case PCAN_USBPRO_SETLED:
>> +        return sizeof(struct pcan_usb_pro_setled);
>> +    default:
>> +        pr_info("%s: %s(%d): unsupported data type\n",
>> +            PCAN_USB_DRIVER_NAME, __func__, data_type);
>> +        break;
>> +    }
>> +
>> +    return -1;
>> +}
>> +static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev,
>> +    struct can_bittiming *bt)
>> +{
>> +    struct pcan_usb_pro_msg um;
>> +    u8 tmp[32];
>> +    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;
> Does the PCAN-USB uses a NXP LPC21xx controller?
I suppose you ask for the "PCAN-USB Pro" adapter, right? Yes it does.
>> +
>> +    netdev_dbg(dev->netdev, "ccbt=0x%08x\n", ccbt);
>> +
>> +    pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
>> +    pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt);
>> +
>> +    return pcan_usb_pro_send_cmd(dev,&um);
>> +}
>> +/*
>> + * 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_usb_pro_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;
>> +        int sizeof_rec = pcan_usb_pro_sizeof_rec(pr->data_type);
>> +
>> +        if (sizeof_rec<= 0) {
>> +            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:
>> +            err = pcan_usb_pro_handle_ts(usb_if,&pr->rx_ts);
> No handling in error case, is that right?
to be coherent, all the "pcan_usb_pro_handle_xxx()" functions return an 
int... But this one always returns 0... So, I changed it into "void 
pcan_usb_pro_handle_ts()" now.
>> +            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;
>> +    int err = 0;
>> +
>> +    pcan_usb_pro_msg_init_empty(&usb_msg, obuf, *size);
>> +
>> +    if ((cf->can_id&  CAN_RTR_FLAG) || (cf->can_dlc == 0))
>> +        data_type = PCAN_USBPRO_TXMSG0;
>> +
> Remove empty line.
Ok.
>> +    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;
>> +
>> +    err = pcan_usb_pro_add_rec(&usb_msg, data_type, 0, flags, len,
>> +        cf->can_id, cf->data);
> \err\ is not checked.
Yes you're right, and it does not need to be. So I removed the "err" 
local variable from the function.
>> +
>> +    *size = usb_msg.rec_buffer_len;
>> +
>> +    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 = (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;
>> +
>> +        /* tell the device the can driver is running */
>> +        pcan_usb_pro_drv_loaded(dev, 1);
>> +
>> +        if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
>> +            PCAN_USBPRO_INFO_FIRMWARE,&fi, sizeof(fi))>= 0)
>> +            netdev_info(dev->netdev,
>> +                "%s fw v%d.%d.%d (%02d/%02d/%02d) fw 0x%08x\n",
>> +                pcan_usb_pro.name,
>> +                fi.version[0], fi.version[1], fi.version[2],
>> +                fi.day, fi.month, fi.year, fi.fw_type);
>> +
>> +        if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
>> +            PCAN_USBPRO_INFO_BOOTLOADER,&bi, sizeof(bi))>= 0) {
>> +            netdev_info(dev->netdev,
>> +                "bootloader v%d.%d.%d (%02d/%02d/%02d)\n",
>> +                bi.version[0], bi.version[1], bi.version[2],
>> +                bi.day, bi.month, bi.year);
>> +
>> +            netdev_info(dev->netdev,
>> +                "serial %08X.%08X hw 0x%08x rev 0x%08x\n",
>> +                bi.serial_num_hi, bi.serial_num_lo,
>> +                bi.hw_type, bi.hw_rev);
>> +
>> +            dev->device_rev = (u8)bi.hw_rev;
>> +        }
>> +
>> +        usb_if = kzalloc(sizeof(struct pcan_usb_pro_interface),
>> +            GFP_KERNEL);
>> +        if (!usb_if)
>> +            return -ENOMEM;
> Is it necessary to call pcan_usb_pro_drv_loaded(dev, 0) here?
I think you're right too... But I preferred to move the (dev, 1) call 
from the beginning of the block to its end (immediately after 
cm_ignore_count = 5).
>> +
>> +        /* number of ts msgs to ignore before taking one into 
>> account */
>> +        usb_if->cm_ignore_count = 5;
>> +
> Remove empty line.
Ok.

>> +    } 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 = (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)
>> +
> Remove empty line. Brackets may increase readability here.
Ok and brackets added.
>> +        /* 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 driver is being unloaded */
>> +        pcan_usb_pro_drv_loaded(dev, 0);
>> +    }
>> +}
Thanks!


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] Add support for PEAK System PCAN-USB Pro adapter
  2011-12-22 13:14 [PATCH] Add support for PEAK System PCAN-USB Pro adapter Stephane Grosjean
  2011-12-23 19:57 ` Sebastian Haas
@ 2012-01-10 11:21 ` Wolfgang Grandegger
  1 sibling, 0 replies; 4+ messages in thread
From: Wolfgang Grandegger @ 2012-01-10 11:21 UTC (permalink / raw)
  To: Stephane Grosjean; +Cc: Oliver Hartkopp, Linux CAN mailing list

On 12/22/2011 02:14 PM, Stephane Grosjean wrote:
>>From a1b285074b883edc3dccc6aacafb18407e98e18a Mon Sep 17 00:00:00 2001
> From: Stephane Grosjean <s.grosjean@peak-system.com>
> Date: Thu, 22 Dec 2011 13:59:00 +0100
> Subject: [PATCH] Add support for PEAK System PCAN-USB Pro adapter
> 
> ---
>  drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 1092 +++++++++++++++++++++++++++
>  drivers/net/can/usb/peak_usb/pcan_usb_pro.h |  352 +++++++++
>  2 files changed, 1444 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.c
>  create mode 100644 drivers/net/can/usb/peak_usb/pcan_usb_pro.h
> 
> 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 0000000..0747395
> --- /dev/null
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c
> @@ -0,0 +1,1092 @@
> +/*
> + * CAN driver for PEAK System PCAN-USB Pro adapter
> + *
> + * Copyright (C) 2011-2012 PEAK-System GmbH
> + *
> + * 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 <linux/netdevice.h>
> +#include <linux/usb.h>
> +#include <linux/module.h>
> +
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +
> +#include "peak_usb.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_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
> +
> +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;
> +};
> +
> +struct pcan_usb_pro_device {
> +	struct peak_usb_device peak_usb_device; /* must be the first member */

This construction is deprecated. Using container_of() is usually the
better and safer solution. Is it an option?

> +	struct pcan_usb_pro_interface *usb_if;
> +};
> +
> +/* 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;
> +};
> +
> +static int pcan_usb_pro_sizeof_rec(u8 data_type)
> +{
> +	switch (data_type) {
> +	case PCAN_USBPRO_RXMSG8:
> +		return sizeof(struct pcan_usb_pro_rxmsg);
> +	case PCAN_USBPRO_RXMSG4:
> +		return sizeof(struct pcan_usb_pro_rxmsg) - 4;
> +	case PCAN_USBPRO_RXMSG0:
> +	case PCAN_USBPRO_RXRTR:
> +		return sizeof(struct pcan_usb_pro_rxmsg) - 8;
> +	case PCAN_USBPRO_RXSTATUS:
> +		return sizeof(struct pcan_usb_pro_rxstatus);
> +	case PCAN_USBPRO_RXTS:
> +		return sizeof(struct pcan_usb_pro_rxts);
> +	case PCAN_USBPRO_TXMSG8:
> +		return sizeof(struct pcan_usb_pro_txmsg);
> +	case PCAN_USBPRO_TXMSG4:
> +		return sizeof(struct pcan_usb_pro_txmsg) - 4;
> +	case PCAN_USBPRO_TXMSG0:
> +		return sizeof(struct pcan_usb_pro_txmsg) - 8;
> +	case PCAN_USBPRO_GETBTR:
> +	case PCAN_USBPRO_SETBTR:
> +		return sizeof(struct pcan_usb_pro_btr);
> +	case PCAN_USBPRO_SETSILENT:
> +		return sizeof(struct pcan_usb_pro_silent);
> +	case PCAN_USBPRO_SETDEVID:
> +	case PCAN_USBPRO_GETDEVID:
> +		return sizeof(struct pcan_usb_pro_devid);
> +	case PCAN_USBPRO_GETBUSACT:
> +	case PCAN_USBPRO_SETBUSACT:
> +		return sizeof(struct pcan_usb_pro_busact);
> +	case PCAN_USBPRO_SETWARNLIM:
> +		return sizeof(struct pcan_usb_pro_warnlim);
> +	case PCAN_USBPRO_SETLKUP:
> +		return sizeof(struct pcan_usb_pro_lkup);
> +	case PCAN_USBPRO_SETLKUPGRP:
> +		return sizeof(struct pcan_usb_pro_lkupgrp);
> +	case PCAN_USBPRO_SETFILTR:
> +		return sizeof(struct pcan_usb_pro_filter);
> +	case PCAN_USBPRO_SETRST:
> +		return sizeof(struct pcan_usb_pro_reset);
> +	case PCAN_USBPRO_SETERRFRM:
> +		return sizeof(struct pcan_usb_pro_errfrm);
> +	case PCAN_USBPRO_GETBUSSTAT:
> +		return sizeof(struct pcan_usb_pro_busstat);
> +	case PCAN_USBPRO_SETREG:
> +		return sizeof(struct pcan_usb_pro_setreg);
> +	case PCAN_USBPRO_GETREG:
> +		return sizeof(struct pcan_usb_pro_getreg);
> +	case PCAN_USBPRO_SETTS:
> +		return sizeof(struct pcan_usb_pro_setts);
> +	case PCAN_USBPRO_SETSTR:
> +		return sizeof(struct pcan_usb_pro_setstr);
> +	case PCAN_USBPRO_GETSTR:
> +		return sizeof(struct pcan_usb_pro_getstr);
> +	case PCAN_USBPRO_STRING:
> +		return sizeof(struct pcan_usb_pro_str);
> +	case PCAN_USBPRO_SAVE:
> +		return sizeof(struct pcan_usb_pro_save);
> +	case PCAN_USBPRO_PDELAY:
> +		return sizeof(struct pcan_usb_pro_pdelay);
> +	case PCAN_USBPRO_TSARG:
> +		return sizeof(struct pcan_usb_pro_tsarg);
> +	case PCAN_USBPRO_ERRID:
> +		return sizeof(struct pcan_usb_pro_errid);
> +	case PCAN_USBPRO_ERRNOW:
> +		return sizeof(struct pcan_usb_pro_errnow);
> +	case PCAN_USBPRO_SETSFILER:
> +		return sizeof(struct pcan_usb_pro_sfiller);
> +	case PCAN_USBPRO_SETLED:
> +		return sizeof(struct pcan_usb_pro_setled);
> +	default:
> +		pr_info("%s: %s(%d): unsupported data type\n",
> +			PCAN_USB_DRIVER_NAME, __func__, data_type);
> +		break;
> +	}
> +
> +	return -1;
> +}

Please use a jump table, which you already did, IIRC.

> +/*
> + * initialize PCAN-USB Pro message data structure
> + */
> +static u8 *pcan_usb_pro_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_usb_pro_msg_init_empty(struct pcan_usb_pro_msg *pm,
> +	void *buffer_addr, int buffer_size)
> +{
> +	u8 *pr = pcan_usb_pro_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_usb_pro_add_rec(struct pcan_usb_pro_msg *pm, int id, ...)
> +{
> +	int l, i;

l or 1? Depending on your font, it will be hard to distinguish.
Therefore, please use "j", "k", or "len" instead.

> +	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++ = (u8)va_arg(ap, int);
> +		*pc++ = (u8)va_arg(ap, int);
> +		*pc++ = (u8)va_arg(ap, int);

Are the casts (u8) and (u16) here and below really needed?

> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		memcpy(pc, va_arg(ap, int *), i);
> +		pc += i;
> +		break;
> +
> +	case PCAN_USBPRO_GETBUSACT:
> +	case PCAN_USBPRO_GETBUSSTAT:
> +		*pc++ = (u8)va_arg(ap, int);
> +		break;
> +
> +	case PCAN_USBPRO_SETBTR:
> +	case PCAN_USBPRO_GETBTR:
> +	case PCAN_USBPRO_SETDEVID:
> +	case PCAN_USBPRO_GETDEVID:
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc += 2;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		break;
> +
> +	case PCAN_USBPRO_SETBUSACT:
> +	case PCAN_USBPRO_SETSILENT:
> +	case PCAN_USBPRO_SETWARNLIM:
> +	case PCAN_USBPRO_SETFILTR:
> +	case PCAN_USBPRO_SETRST:
> +	case PCAN_USBPRO_SETERRFRM:
> +	case PCAN_USBPRO_TSARG:
> +	case PCAN_USBPRO_ERRNOW:
> +		*pc++ = (u8)va_arg(ap, int);
> +		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
> +		pc += 2;
> +		break;
> +
> +	case PCAN_USBPRO_SETLKUP:
> +	case PCAN_USBPRO_SETLED:
> +		*pc++ = (u8)va_arg(ap, int);
> +		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
> +		pc += 2;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		break;
> +
> +	case PCAN_USBPRO_SETLKUPGRP:
> +		*pc++ = (u8)va_arg(ap, int);
> +		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
> +		pc += 2;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		break;
> +
> +	case PCAN_USBPRO_SETREG:
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc += 2;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		break;
> +
> +	case PCAN_USBPRO_GETREG:
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc += 2;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		break;
> +
> +	case PCAN_USBPRO_SETTS:
> +	case PCAN_USBPRO_PDELAY:
> +		pc++;
> +		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
> +		pc += 2;
> +		break;
> +
> +	case PCAN_USBPRO_SETBUSLAST:
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc++;
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc += 2;
> +		*(u16 *)pc = cpu_to_le16(4096);
> +		pc += 2;
> +		break;
> +
> +	case PCAN_USBPRO_SETSTR:
> +		*pc++ = (u8)va_arg(ap, int);
> +		*pc++ = (u8)va_arg(ap, int);
> +		*pc++ = (u8)va_arg(ap, int);
> +		memcpy(pc, va_arg(ap, u8 *), 60);
> +		pc += 60;
> +		break;
> +
> +	case PCAN_USBPRO_GETSTR:
> +	case PCAN_USBPRO_SAVE:
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc += 2;
> +		break;
> +
> +	case PCAN_USBPRO_STRING:
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc += 2;
> +		memcpy(pc, va_arg(ap, u8 *), 250);
> +		pc += 250;
> +		break;
> +
> +	case PCAN_USBPRO_ERRID:
> +		*pc++ = (u8)va_arg(ap, int);
> +		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
> +		pc += 2;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
> +		pc += 2;
> +		*(u16 *)pc = cpu_to_le16((u16)va_arg(ap, int));
> +		pc += 2;
> +		break;
> +
> +	case PCAN_USBPRO_SETSFILER:
> +		*pc++ = (u8)va_arg(ap, int);
> +		pc += 2;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		*(u32 *)pc = cpu_to_le32(va_arg(ap, u32));
> +		pc += 4;
> +		break;
> +
> +	default:
> +		pr_info("%s: %s(): unknown data type %02Xh (%d)\n",
> +			PCAN_USB_DRIVER_NAME, __func__, id, id);

info or error?

> +		pc--;
> +		break;
> +	}

A jump table would make sense here as well combining it with the one
used above.

> +	l = pc - pm->rec_ptr;
> +	if (l > 0) {
> +		*pm->u.rec_cnt = cpu_to_le32(*pm->u.rec_cnt+1);
> +		*(pm->rec_ptr) = (u8)id;
> +
> +		pm->rec_ptr = pc;
> +		pm->rec_buffer_len += l;
> +	}
> +
> +	va_end(ap);
> +
> +	return l;
> +}
> +
> +/*
> + * 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;
> +		int 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;
> +
> +		if (actual_length < PCAN_USBPRO_MSG_HEADER_LEN) {
> +			netdev_err(dev->netdev,
> +				"got abnormal too small rsp (len=%d)\n",
> +				actual_length);
> +			err = -EBADMSG;
> +			break;
> +		}
> +
> +		pc = pcan_usb_pro_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 <= 0) {
> +				netdev_err(dev->netdev,
> +					"got unprocessed record in msg\n");
> +				dump_mem("rcvd rsp msg", pum->u.rec_buffer,
> +					actual_length);
> +				err = -EBADMSG;
> +				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);

err = ?

> +
> +			/* 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;
> +
> +	case PCAN_USBPRO_REQ_INFO:

Not needed if it belongs to the default cases.

> +	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);

Spaces around "*".

> +	if (err < 0)
> +		netdev_info(dev->netdev,
> +			"unable to request usb[type=%d value=%d] err=%d\n",
> +			req_id, req_value, err);
> +	else
> +		err = 0;
> +
> +	return err;

	if (err < 0) {
		...
		return err;
	}

	return 0;

look better.


> +}
> +
> +static int pcan_usb_pro_set_ts(struct peak_usb_device *dev, u16 onoff)
> +{
> +	struct pcan_usb_pro_msg um;
> +	u8 tmp[32];
> +
> +	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETTS, onoff);
> +
> +	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;
> +	u8 tmp[32];
> +
> +	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_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;
> +	u8 tmp[32];
> +
> +	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_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;
> +	u8 tmp[32];
> +
> +	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_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;
> +	u8 tmp[32];
> +
> +	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_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_msg um;
> +	struct pcan_usb_pro_devid *pdn;
> +	u8 tmp[32], *pc;
> +	int err;
> +
> +	pc = pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_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)
> +{
> +	struct pcan_usb_pro_msg um;
> +	u8 tmp[32];
> +	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_dbg(dev->netdev, "ccbt=0x%08x\n", ccbt);

Please netdev_info like all other drivers.

> +
> +	pcan_usb_pro_msg_init_empty(&um, tmp, sizeof(tmp));
> +	pcan_usb_pro_add_rec(&um, PCAN_USBPRO_SETBTR, dev->ctrl_idx, ccbt);
> +
> +	return pcan_usb_pro_send_cmd(dev, &um);
> +}
> +
> +static void pcan_usb_pro_drv_loaded(struct peak_usb_device *dev, int loaded)
> +{
> +	u8 buffer[16];
> +
> +	buffer[0] = 0;
> +	buffer[1] = loaded ? 1 : 0;
> +
> +	pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_FCT,
> +		PCAN_USBPRO_FCT_DRIVER_LOADED, buffer, sizeof(buffer));
> +}
> +
> +static inline
> +struct pcan_usb_pro_interface *pcan_usb_pro_dev_if(struct peak_usb_device *dev)
> +{
> +	return ((struct pcan_usb_pro_device *)dev)->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_RTR)
> +		can_frame->can_id |= CAN_RTR_FLAG;
> +	if (rx->flags & PCAN_USBPRO_EXT)
> +		can_frame->can_id |= CAN_EFF_FLAG;
> +
> +	memcpy(can_frame->data, rx->data, can_frame->can_dlc);

In case of RTR, we should not copy data, IIRC.

> +	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;
> +	struct sk_buff *skb;
> +	struct timeval tv;
> +
> +	skb = alloc_can_err_skb(netdev, &can_frame);
> +	if (!skb)
> +		return -ENOMEM;
> +
> +	memset(can_frame, '\0', sizeof(struct can_frame));

Not necessary because already done by alloc_can_err_skb().

> +
> +	if (raw_status & FW_USBPRO_STATUS_BUS_S) {
> +		can_frame->can_id |= CAN_ERR_BUSOFF;
> +		can_bus_off(netdev);
> +
> +	} else if (raw_status & FW_USBPRO_STATUS_ERROR_S) {
> +		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) {
> +			can_frame->can_id |= CAN_ERR_CRTL;
> +			can_frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
> +			dev->can.can_stats.error_passive++;
> +
> +		} else if (rx_err_cnt > 96) {
> +			can_frame->can_id |= CAN_ERR_CRTL;
> +			can_frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
> +			dev->can.can_stats.error_warning++;
> +		}
> +
> +		if (tx_err_cnt > 127) {
> +			can_frame->can_id |= CAN_ERR_CRTL;
> +			can_frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
> +			dev->can.can_stats.error_passive++;
> +
> +		} else if (tx_err_cnt > 96) {
> +			can_frame->can_id |= CAN_ERR_CRTL;
> +			can_frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
> +			dev->can.can_stats.error_warning++;
> +		}
> +	}
> +
> +	if (raw_status & FW_USBPRO_STATUS_OVERRUN_S) {
> +		can_frame->can_id |= CAN_ERR_PROT;
> +		can_frame->data[2] |= CAN_ERR_PROT_OVERLOAD;
> +	}
> +
> +	if (raw_status & FW_USBPRO_STATUS_QOVERRUN_S) {
> +		can_frame->can_id |= CAN_ERR_CRTL;
> +		can_frame->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> +	}
> +
> +	if (can_frame->can_id != CAN_ERR_FLAG) {
> +		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++;

rx_bytes?

> +	} else {
> +		kfree_skb(skb);
> +	}
> +
> +	return 0;
> +}
> +
> +static int 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]));
> +
> +	return 0;
> +}
> +
> +/*
> + * 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_usb_pro_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;
> +		int sizeof_rec = pcan_usb_pro_sizeof_rec(pr->data_type);
> +
> +		if (sizeof_rec <= 0) {
> +			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:
> +			err = 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;
> +	int err = 0;
> +
> +	pcan_usb_pro_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;
> +
> +	err = pcan_usb_pro_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 = (struct pcan_usb_pro_device *)dev;
> +	int err;
> +
> +	err = pcan_usb_pro_set_silent(dev,
> +		(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY));

"()" not needed.

> +	if (err)
> +		goto start_failed;
> +
> +	/* Set filter mode: 0-> All OFF; 1->bypass */
> +	err = pcan_usb_pro_set_filter(dev, 1);
> +	if (err)
> +		goto start_failed;
> +
> +	/* 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++;
> +
> +start_failed:
> +	return err;

Useless label!

> +}
> +
> +/*
> + * 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 = (struct pcan_usb_pro_device *)dev;

Hm, this cast worries me. What about using container_of?

> +	/* 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 = (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;
> +
> +		/* tell the device the can driver is running */
> +		pcan_usb_pro_drv_loaded(dev, 1);
> +
> +		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
> +			PCAN_USBPRO_INFO_FIRMWARE, &fi, sizeof(fi)) >= 0)
> +			netdev_info(dev->netdev,
> +				"%s fw v%d.%d.%d (%02d/%02d/%02d) fw 0x%08x\n",
> +				pcan_usb_pro.name,
> +				fi.version[0], fi.version[1], fi.version[2],
> +				fi.day, fi.month, fi.year, fi.fw_type);
> +
> +		if (pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO,
> +			PCAN_USBPRO_INFO_BOOTLOADER, &bi, sizeof(bi)) >= 0) {
> +			netdev_info(dev->netdev,
> +				"bootloader v%d.%d.%d (%02d/%02d/%02d)\n",
> +				bi.version[0], bi.version[1], bi.version[2],
> +				bi.day, bi.month, bi.year);
> +
> +			netdev_info(dev->netdev,
> +				"serial %08X.%08X hw 0x%08x rev 0x%08x\n",
> +				bi.serial_num_hi, bi.serial_num_lo,
> +				bi.hw_type, bi.hw_rev);
> +
> +			dev->device_rev = (u8)bi.hw_rev;
> +		}
> +
> +		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;
> +
> +	} 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 = (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)
> +

remove empty line.


> +		/* 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 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. All 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:
> +			/* CAN usb interface unused ep */
> +		case 0x83:
> +			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 commands/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,
> +};
> 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 0000000..66952cb
> --- /dev/null
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h
> @@ -0,0 +1,352 @@
> +/*
> + * CAN driver for PEAK System PCAN-USB Pro adapter
> + *
> + * Copyright (C) 2011-2012 PEAK-System GmbH
> + *
> + * 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 wValue for XXX_INFO */

Please correct message?

> +#define PCAN_USBPRO_INFO_BOOTLOADER	0
> +#define PCAN_USBPRO_INFO_FIRMWARE	1
> +
> +/* Vendor Request wValue for XXX_FCT */
> +#define PCAN_USBPRO_FCT_DRIVER_LOADED	5

Ditto.

> +
> +/* PCAN_USBPRO_INFO_BOOTLOADER 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_FIRMWARE 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_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_RXBUSLAST	0x86
> +#define PCAN_USBPRO_TXMSG8	0x41
> +#define PCAN_USBPRO_TXMSG4	0x42
> +#define PCAN_USBPRO_TXMSG0	0x43
> +#define PCAN_USBPRO_GETBTR	0x01
> +#define PCAN_USBPRO_SETBTR	0x02
> +#define PCAN_USBPRO_GETBUSACT	0x03
> +#define PCAN_USBPRO_SETBUSACT	0x04
> +#define PCAN_USBPRO_SETSILENT	0x05
> +#define PCAN_USBPRO_SETDEVID	0x06
> +#define PCAN_USBPRO_SETWARNLIM	0x07
> +#define PCAN_USBPRO_SETLKUP	0x08
> +#define PCAN_USBPRO_SETLKUPGRP	0x09
> +#define PCAN_USBPRO_SETFILTR	0x0a
> +#define PCAN_USBPRO_SETRST	0x0b
> +#define PCAN_USBPRO_SETERRFRM	0x0c
> +#define PCAN_USBPRO_GETBUSSTAT	0x0D
> +#define PCAN_USBPRO_SETREG	0x0e
> +#define PCAN_USBPRO_GETREG	0x0f
> +#define PCAN_USBPRO_SETTS	0x10
> +#define PCAN_USBPRO_SETBUSLAST	0x11
> +#define PCAN_USBPRO_GETDEVID	0x12
> +#define PCAN_USBPRO_SETSTR	0x13
> +#define PCAN_USBPRO_GETSTR	0x14
> +#define PCAN_USBPRO_STRING	0x15
> +#define PCAN_USBPRO_SAVE	0x16
> +#define PCAN_USBPRO_PDELAY	0x17
> +#define PCAN_USBPRO_TSARG	0x18
> +#define PCAN_USBPRO_ERRID	0x19
> +#define PCAN_USBPRO_ERRNOW	0x1A
> +#define PCAN_USBPRO_SETSFILER	0x1B
> +#define PCAN_USBPRO_SETLED	0x1C
> +
> +/* record structures */
> +struct __packed pcan_usb_pro_rxmsg {
> +	u8  data_type;
> +	u8  client;
> +	u8  flags;
> +	u8  len;
> +	u32 ts32;
> +	u32 id;
> +
> +	u8  data[8];
> +};
> +
> +#define FW_USBPRO_STATUS_ERROR_S	0x0001
> +#define FW_USBPRO_STATUS_BUS_S	0x0002

Check tabs!

> +#define FW_USBPRO_STATUS_OVERRUN_S	0x0004
> +#define FW_USBPRO_STATUS_QOVERRUN_S	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_buslast {
> +	u8  data_type;
> +	u8  channel;
> +	u16 buslast_val;
> +	u32 ts32;
> +};
> +
> +struct __packed pcan_usb_pro_txmsg {
> +	u8  data_type;
> +	u8  client;
> +	u8  flags;
> +	u8  len;
> +	u32 id;
> +	u8  data[8];
> +};
> +
> +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_devid {
> +	u8  data_type;
> +	u8  channel;
> +	u16 dummy;
> +	u32 serial_num;
> +};
> +
> +struct __packed pcan_usb_pro_warnlim {
> +	u8  data_type;
> +	u8  channel;
> +	u16 warning_limit;
> +};
> +
> +struct __packed pcan_usb_pro_lkup {
> +	u8  data_type;
> +	u8  channel;
> +	u16 id_type;
> +	u32 id;
> +};
> +
> +struct __packed pcan_usb_pro_lkupgrp {
> +	u8  data_type;
> +	u8  channel;
> +	u16 id_type;
> +	u32 id_start;
> +	u32 id_end;
> +};
> +
> +struct __packed pcan_usb_pro_filter {
> +	u8  data_type;
> +	u8  dummy;
> +	u16 filter_mode;
> +};
> +
> +struct __packed pcan_usb_pro_reset {
> +	u8  data_type;
> +	u8  channel;
> +	u16 reset;
> +};
> +
> +struct __packed pcan_usb_pro_errfrm {
> +	u8  data_type;
> +	u8  channel;
> +	u16 mode;
> +};
> +
> +struct __packed pcan_usb_pro_busstat {
> +	u8  data_type;
> +	u8  channel;
> +	u16 status;
> +};
> +
> +struct __packed pcan_usb_pro_setreg {
> +	u8  data_type;
> +	u8  irq_off;
> +	u16 dummy;
> +	u32 address;
> +	u32 value;
> +	u32 mask;
> +};
> +
> +struct __packed pcan_usb_pro_getreg {
> +	u8  data_type;
> +	u8  irq_off;
> +	u16 dummy;
> +	u32 address;
> +	u32 value;
> +};
> +
> +struct __packed pcan_usb_pro_setts {
> +	u8  data_type;
> +	u8  dummy;
> +	u16 mode;
> +};
> +
> +struct __packed pcan_usb_pro_setbl {
> +	u8  data_type;
> +	u8  channel;
> +	u8  dummy;
> +	u8  mode;
> +	u16 prescaler;
> +	u16 sampletimequanta;
> +};
> +
> +struct __packed pcan_usb_pro_setstr {
> +	u8  data_type;
> +	u8  channel;
> +	u8  offset;
> +	u8  len;
> +	u8  data[60];
> +};
> +
> +struct __packed pcan_usb_pro_getstr {
> +	u8  data_type;
> +	u8  channel;
> +	u16 dummy;
> +};
> +
> +struct __packed pcan_usb_pro_str {
> +	u8  data_type;
> +	u8  channel;
> +	u16 dummy;
> +	u8  data[250];
> +};
> +
> +struct __packed pcan_usb_pro_save {
> +	u8  data_type;
> +	u8  channel;
> +	u16 dummy;
> +};
> +
> +struct __packed pcan_usb_pro_pdelay {
> +	u8  data_type;
> +	u8  dummy;
> +	u16 delay;
> +};
> +
> +struct __packed pcan_usb_pro_tsarg {
> +	u8  data_type;
> +	u8  channel;
> +	u16 start_or_end;
> +};
> +
> +struct __packed pcan_usb_pro_errid {
> +	u8  data_type;
> +	u8  channel;
> +	u16 bit_pos;
> +	u32 id;
> +	u16 ok_counter;
> +	u16 error_counter;
> +};
> +
> +struct __packed pcan_usb_pro_errnow {
> +	u8  data_type;
> +	u8  channel;
> +	u16 bit_pos;
> +};
> +
> +struct __packed pcan_usb_pro_sfiller {
> +	u8  data_type;
> +	u8  channel;
> +	u16 dummy;
> +	u32 accmask;
> +	u32 acccode;
> +};
> +
> +struct __packed pcan_usb_pro_setled {
> +	u8  data_type;
> +	u8  channel;
> +	u16 mode;
> +	u32 timeout;
> +};
> +
> +union pcan_usb_pro_rec {
> +	u8	data_type;
> +	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_buslast	rx_buslast;
> +	struct pcan_usb_pro_txmsg	tx_msg;
> +	struct pcan_usb_pro_btr	btr;

Tabs?

> +	struct pcan_usb_pro_busact	bus_act;
> +	struct pcan_usb_pro_silent	silent_mode;
> +	struct pcan_usb_pro_devid	dev_id;
> +	struct pcan_usb_pro_warnlim	warn_lim;
> +	struct pcan_usb_pro_lkup	lkup_expl;
> +	struct pcan_usb_pro_lkupgrp	lkup_group;
> +	struct pcan_usb_pro_filter	filter_mode;
> +	struct pcan_usb_pro_reset	rst_mode;
> +	struct pcan_usb_pro_errfrm	err_frame;
> +	struct pcan_usb_pro_busstat	bus_status;
> +	struct pcan_usb_pro_setreg	set_reg;
> +	struct pcan_usb_pro_getreg	get_reg;
> +	struct pcan_usb_pro_setts	ts;
> +	struct pcan_usb_pro_setbl	bus_last;
> +	struct pcan_usb_pro_setstr	set_str;
> +	struct pcan_usb_pro_getstr	get_str;
> +	struct pcan_usb_pro_str	str;

Tabs?

> +	struct pcan_usb_pro_save	save_eeprom;
> +	struct pcan_usb_pro_pdelay	p_delay;
> +	struct pcan_usb_pro_tsarg	ts_arg;
> +	struct pcan_usb_pro_errid	err_id;
> +	struct pcan_usb_pro_errnow	err_now;
> +	struct pcan_usb_pro_sfiller	soft_filler;
> +	struct pcan_usb_pro_setled	set_can_led;
> +};
> +
> +#endif

Wolfgang.

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-01-10 11:21 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-22 13:14 [PATCH] Add support for PEAK System PCAN-USB Pro adapter Stephane Grosjean
2011-12-23 19:57 ` Sebastian Haas
2011-12-26 10:55   ` Grosjean Stephane
2012-01-10 11:21 ` Wolfgang Grandegger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).