-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Oliver,
like I've written on netdev I'm posting the ems_usb driver here. At the moment I'm fighting with the netlink interface. Everytime I want to set the bitrate I'm getting the following error from the IP tool: sudo ./ip link set can0 type can bitrate 500000 RTNETLINK answers: Numerical argument out of domain dmesg says: [ 1185.018724] ems_usb 2-6:1.0: bitrate error 100.0% too high Any ideas? I hope I'm using the candev interface correctly. - -- Mit freundlichen Gruessen/Best Regards, Sebastian Haas Software Entwicklung/Software Development Phone: +49-9451-9432-22 Fax : +49-9451-9432-12 Email: [email protected] Web : www.ems-wuensche.com Index: include/linux/cpc.h =================================================================== - --- include/linux/cpc.h (Revision 0) +++ include/linux/cpc.h (Revision 0) @@ -0,0 +1,440 @@ +/* + * CPC CAN Interface Definitions + * + * Copyright (C) 2000-2008 EMS Dr. Thomas Wuensche + * + * 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. + */ +#ifndef CPC_HEADER +#define CPC_HEADER + +// the maximum length of the union members within a CPC_MSG +// this value can be defined by the customer, but has to be +// >= 64 bytes +// however, if not defined before, we set a length of 64 byte +#if !defined(CPC_MSG_LEN) || (CPC_MSG_LEN < 64) +#undef CPC_MSG_LEN +#define CPC_MSG_LEN 64 +#endif + +// check the operating system used +#ifdef _WIN32 // running a Windows OS + +// define basic types on Windows platforms +#ifdef _MSC_VER // Visual Studio + typedef unsigned __int8 u8; + typedef unsigned __int16 u16; + typedef unsigned __int32 u32; +#else // Borland Compiler + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; +#endif + // on Windows OS we use a byte alignment of 1 + #pragma pack(push, 1) + + // set the calling conventions for the library function calls + #define CALL_CONV __stdcall +#else + // Kernel headers already define this types + #ifndef __KERNEL__ + // define basic types + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + #endif + + // Linux does not use this calling convention + #define CALL_CONV +#endif + +// Transmission of events from CPC interfaces to PC can be individually +// controlled per event type. Default state is: don't transmit +// Control values are constructed by bit-or of Subject and Action +// and passed to CPC_Control() + +// Control-Values for CPC_Control() Command Subject Selection +#define CONTR_CAN_Message 0x04 +#define CONTR_Busload 0x08 +#define CONTR_CAN_State 0x0C +#define CONTR_SendAck 0x10 +#define CONTR_Filter 0x14 +#define CONTR_CmdQueue 0x18 // reserved, do not use +#define CONTR_BusError 0x1C + +// Control Command Actions +#define CONTR_CONT_OFF 0 +#define CONTR_CONT_ON 1 +#define CONTR_SING_ON 2 +// CONTR_SING_ON doesn't change CONTR_CONT_ON state, so it should be +// read as: transmit at least once + +// defines for confirmed request +#define DO_NOT_CONFIRM 0 +#define DO_CONFIRM 1 + +// event flags +#define EVENT_READ 0x01 +#define EVENT_WRITE 0x02 + +// Messages from CPC to PC contain a message object type field. +// The following message types are sent by CPC and can be used in +// handlers, others should be ignored. +#define CPC_MSG_T_RESYNC 0 // Normally to be ignored +#define CPC_MSG_T_CAN 1 // CAN data frame +#define CPC_MSG_T_BUSLOAD 2 // Busload message +#define CPC_MSG_T_STRING 3 // Normally to be ignored +#define CPC_MSG_T_CONTI 4 // Normally to be ignored +#define CPC_MSG_T_MEM 7 // Normally not to be handled +#define CPC_MSG_T_RTR 8 // CAN remote frame +#define CPC_MSG_T_TXACK 9 // Send acknowledge +#define CPC_MSG_T_POWERUP 10 // Power-up message +#define CPC_MSG_T_CMD_NO 11 // Normally to be ignored +#define CPC_MSG_T_CAN_PRMS 12 // Actual CAN parameters +#define CPC_MSG_T_ABORTED 13 // Command aborted message +#define CPC_MSG_T_CANSTATE 14 // CAN state message +#define CPC_MSG_T_RESET 15 // used to reset CAN-Controller +#define CPC_MSG_T_XCAN 16 // XCAN data frame +#define CPC_MSG_T_XRTR 17 // XCAN remote frame +#define CPC_MSG_T_INFO 18 // information strings +#define CPC_MSG_T_CONTROL 19 // used for control of interface/driver behaviour +#define CPC_MSG_T_CONFIRM 20 // response type for confirmed requests +#define CPC_MSG_T_OVERRUN 21 // response type for overrun conditions +#define CPC_MSG_T_KEEPALIVE 22 // response type for keep alive conditions +#define CPC_MSG_T_CANERROR 23 // response type for bus error conditions +#define CPC_MSG_T_DISCONNECTED 24 // response type for a disconnected interface +#define CPC_MSG_T_ERR_COUNTER 25 // RX/TX error counter of CAN controller + +#define CPC_MSG_T_FIRMWARE 100 // response type for USB firmware download + +// Messages from the PC to the CPC interface contain a command field +// Most of the command types are wrapped by the library functions and have therefore +// normally not to be used. +// However, programmers who wish to circumvent the library and talk directly +// to the drivers (mainly Linux programmers) can use the following +// command types: + +#define CPC_CMD_T_CAN 1 // CAN data frame +#define CPC_CMD_T_CONTROL 3 // used for control of interface/driver behaviour +#define CPC_CMD_T_CAN_PRMS 6 // set CAN parameters +#define CPC_CMD_T_CLEARBUF 8 // clears input queue; this is depricated, use CPC_CMD_T_CLEAR_MSG_QUEUE instead +#define CPC_CMD_T_INQ_CAN_PARMS 11 // inquire actual CAN parameters +#define CPC_CMD_T_FILTER_PRMS 12 // set filter parameter +#define CPC_CMD_T_RTR 13 // CAN remote frame +#define CPC_CMD_T_CANSTATE 14 // CAN state message +#define CPC_CMD_T_XCAN 15 // XCAN data frame +#define CPC_CMD_T_XRTR 16 // XCAN remote frame +#define CPC_CMD_T_RESET 17 // used to reset CAN-Controller +#define CPC_CMD_T_INQ_INFO 18 // miscellanous information strings +#define CPC_CMD_T_OPEN_CHAN 19 // open a channel +#define CPC_CMD_T_CLOSE_CHAN 20 // close a channel +#define CPC_CMD_T_CNTBUF 21 // this is depricated, use CPC_CMD_T_INQ_MSG_QUEUE_CNT instead +#define CPC_CMD_T_CAN_EXIT 200 // exit the CAN (disable interrupts; reset bootrate; reset output_cntr; mode = 1) + +#define CPC_CMD_T_INQ_MSG_QUEUE_CNT CPC_CMD_T_CNTBUF // inquires the count of elements in the message queue +#define CPC_CMD_T_INQ_ERR_COUNTER 25 // request the CAN controllers error counter +#define CPC_CMD_T_CLEAR_MSG_QUEUE CPC_CMD_T_CLEARBUF // clear CPC_MSG queue +#define CPC_CMD_T_CLEAR_CMD_QUEUE 28 // clear CPC_CMD queue +#define CPC_CMD_T_FIRMWARE 100 // reserved, must not be used +#define CPC_CMD_T_USB_RESET 101 // reserved, must not be used +#define CPC_CMD_T_WAIT_NOTIFY 102 // reserved, must not be used +#define CPC_CMD_T_WAIT_SETUP 103 // reserved, must not be used +#define CPC_CMD_T_ABORT 255 // Normally not to be used + +// definitions for CPC_MSG_T_INFO +// information sources +#define CPC_INFOMSG_T_UNKNOWN_SOURCE 0 +#define CPC_INFOMSG_T_INTERFACE 1 +#define CPC_INFOMSG_T_DRIVER 2 +#define CPC_INFOMSG_T_LIBRARY 3 + +// information types +#define CPC_INFOMSG_T_UNKNOWN_TYPE 0 +#define CPC_INFOMSG_T_VERSION 1 +#define CPC_INFOMSG_T_SERIAL 2 + +// definitions for controller types +#define PCA82C200 1 // Philips basic CAN controller, replaced by SJA1000 +#define SJA1000 2 // Philips basic CAN controller +#define AN82527 3 // Intel full CAN controller +#define M16C_BASIC 4 // M16C controller running in basic CAN (not full CAN) mode + +// channel open error codes +#define CPC_ERR_NO_FREE_CHANNEL -1 // no more free space within the channel array +#define CPC_ERR_CHANNEL_ALREADY_OPEN -2 // the channel is already open +#define CPC_ERR_CHANNEL_NOT_ACTIVE -3 // access to a channel not active failed +#define CPC_ERR_NO_DRIVER_PRESENT -4 // no driver at the location searched by the library +#define CPC_ERR_NO_INIFILE_PRESENT -5 // the library could not find the inifile +#define CPC_ERR_WRONG_PARAMETERS -6 // wrong parameters in the inifile +#define CPC_ERR_NO_INTERFACE_PRESENT -7 // 1. The specified interface is not connected + // 2. The interface (mostly CPC-USB) was disconnected upon operation +#define CPC_ERR_NO_MATCHING_CHANNEL -8 // the driver couldn't find a matching channel +#define CPC_ERR_NO_BUFFER_AVAILABLE -9 // the driver couldn't allocate buffer for messages +#define CPC_ERR_NO_INTERRUPT -10 // the requested interrupt couldn't be claimed +#define CPC_ERR_NO_MATCHING_INTERFACE -11 // no interface type related to this channel was found +#define CPC_ERR_NO_RESOURCES -12 // the requested resources could not be claimed +#define CPC_ERR_SOCKET -13 // error concerning TCP sockets + +// init error codes +#define CPC_ERR_WRONG_CONTROLLER_TYPE -14 // wrong CAN controller type within initialization +#define CPC_ERR_NO_RESET_MODE -15 // the controller could not be set into reset mode +#define CPC_ERR_NO_CAN_ACCESS -16 // the CAN controller could not be accessed + +// transmit error codes +#define CPC_ERR_CAN_WRONG_ID -20 // the provided CAN id is too big +#define CPC_ERR_CAN_WRONG_LENGTH -21 // the provided CAN length is too long +#define CPC_ERR_CAN_NO_TRANSMIT_BUF -22 // the transmit buffer was occupied +#define CPC_ERR_CAN_TRANSMIT_TIMEOUT -23 // The message could not be sent within a + // specified time + +// other error codes +#define CPC_ERR_SERVICE_NOT_SUPPORTED -30 // the requested service is not supported by the interface +#define CPC_ERR_IO_TRANSFER -31 // a transmission error down to the driver occurred +#define CPC_ERR_TRANSMISSION_FAILED -32 // a transmission error down to the interface occurred +#define CPC_ERR_TRANSMISSION_TIMEOUT -33 // a timeout occurred within transmission to the interface +#define CPC_ERR_OP_SYS_NOT_SUPPORTED -35 // the operating system is not supported +#define CPC_ERR_UNKNOWN -40 // an unknown error ocurred (mostly IOCTL errors) + +#define CPC_ERR_LOADING_DLL -50 // the library 'cpcwin.dll' could not be loaded +#define CPC_ERR_ASSIGNING_FUNCTION -51 // the specified function could not be assigned +#define CPC_ERR_DLL_INITIALIZATION -52 // the DLL was not initialized correctly +#define CPC_ERR_MISSING_LICFILE -55 // the file containing the licenses does not exist +#define CPC_ERR_MISSING_LICENSE -56 // a required license was not found + +// CAN state bit values. Ignore any bits not listed +#define CPC_CAN_STATE_BUSOFF 0x80 +#define CPC_CAN_STATE_ERROR 0x40 + +// Mask to help ignore undefined bits +#define CPC_CAN_STATE_MASK 0xc0 + +// CAN-Message representation in a CPC_MSG +// Message object type is CPC_MSG_T_CAN or CPC_MSG_T_RTR +// or CPC_MSG_T_XCAN or CPC_MSG_T_XRTR +typedef struct CPC_CAN_MSG { + u32 id; + u8 length; + u8 msg[8]; +} CPC_CAN_MSG_T; + + +// representation of the CAN parameters for the PCA82C200 controller +typedef struct CPC_PCA82C200_PARAMS { + u8 acc_code; // Acceptance-code for receive, Standard: 0 + u8 acc_mask; // Acceptance-mask for receive, Standard: 0xff (everything) + u8 btr0; // Bus-timing register 0 + u8 btr1; // Bus-timing register 1 + u8 outp_contr; // Output-control register +} CPC_PCA82C200_PARAMS_T; + +// representation of the CAN parameters for the SJA1000 controller +typedef struct CPC_SJA1000_PARAMS { + u8 mode; // enables single or dual acceptance filtering + u8 acc_code0; // Acceptance-code for receive, Standard: 0 + u8 acc_code1; + u8 acc_code2; + u8 acc_code3; + u8 acc_mask0; // Acceptance-mask for receive, Standard: 0xff (everything) + u8 acc_mask1; + u8 acc_mask2; + u8 acc_mask3; + u8 btr0; // Bus-timing register 0 + u8 btr1; // Bus-timing register 1 + u8 outp_contr; // Output-control register +} CPC_SJA1000_PARAMS_T; + +// representation of the CAN parameters for the M16C controller +// in basic CAN mode (means no full CAN) +typedef struct CPC_M16C_BASIC_PARAMS { + u8 con0; + u8 con1; + u8 ctlr0; + u8 ctlr1; + u8 clk; + u8 acc_std_code0; + u8 acc_std_code1; + u8 acc_ext_code0; + u8 acc_ext_code1; + u8 acc_ext_code2; + u8 acc_ext_code3; + u8 acc_std_mask0; + u8 acc_std_mask1; + u8 acc_ext_mask0; + u8 acc_ext_mask1; + u8 acc_ext_mask2; + u8 acc_ext_mask3; +} CPC_M16C_BASIC_PARAMS_T; + +// CAN params message representation +typedef struct CPC_CAN_PARAMS { + u8 cc_type; // represents the controller type + union { + CPC_M16C_BASIC_PARAMS_T m16c_basic; + CPC_SJA1000_PARAMS_T sja1000; + CPC_PCA82C200_PARAMS_T pca82c200; + } cc_params; +} CPC_CAN_PARAMS_T; + +// the following structures are slightly different for Windows and Linux +// To be able to use the 'Select' mechanism with Linux the application +// needs to know the devices file desciptor. +// This mechanism is not implemented within Windows and the file descriptor +// is therefore not needed +#ifdef _WIN32 + +// CAN init params message representation +typedef struct CPC_INIT_PARAMS { + CPC_CAN_PARAMS_T canparams; +} CPC_INIT_PARAMS_T; + +#else// Linux + +// CHAN init params representation +typedef struct CPC_CHAN_PARAMS { + int fd; +} CPC_CHAN_PARAMS_T; + +// CAN init params message representation +typedef struct CPC_INIT_PARAMS { + CPC_CHAN_PARAMS_T chanparams; + CPC_CAN_PARAMS_T canparams; +} CPC_INIT_PARAMS_T; + +#endif + +// structure for confirmed message handling +typedef struct CPC_CONFIRM { + u8 result; // error code +} CPC_CONFIRM_T; + +// structure for information requests +typedef struct CPC_INFO { + u8 source; // interface, driver or library + u8 type; // version or serial number + char msg[CPC_MSG_LEN - 2]; // string holding the requested information +} CPC_INFO_T; + +// OVERRUN /////////////////////////////////////// +// In general two types of overrun may occur. +// A hardware overrun, where the CAN controller +// lost a message, because the interrupt was +// not handled before the next messgae comes in. +// Or a software overrun, where i.e. a received +// message could not be stored in the CPC_MSG +// buffer. + +// After a software overrun has occurred +// we wait until we have CPC_OVR_GAP slots +// free in the CPC_MSG buffer. +#define CPC_OVR_GAP 10 + +// Two types of software overrun may occur. +// A received CAN message or a CAN state event +// can cause an overrun. +// Note: A CPC_CMD which would normally store +// its result immediately in the CPC_MSG +// queue may fail, because the message queue is full. +// This will not generate an overrun message, but +// will halt command execution, until this command +// is able to store its message in the message queue. +#define CPC_OVR_EVENT_CAN 0x01 +#define CPC_OVR_EVENT_CANSTATE 0x02 +#define CPC_OVR_EVENT_BUSERROR 0x04 + +// If the CAN controller lost a message +// we indicate it with the highest bit +// set in the count field. +#define CPC_OVR_HW 0x80 + +// structure for overrun conditions +typedef struct { + u8 event; + u8 count; +} CPC_OVERRUN_T; + +// CAN errors //////////////////////////////////// +// Each CAN controller type has different +// registers to record errors. +// Therefor a structure containing the specific +// errors is set up for each controller here + +// SJA1000 error structure +// see the SJA1000 datasheet for detailed +// explanation of the registers +typedef struct CPC_SJA1000_CAN_ERROR { + u8 ecc; // error capture code register + u8 rxerr; // RX error counter register + u8 txerr; // TX error counter register +} CPC_SJA1000_CAN_ERROR_T; + +// M16C error structure +// see the M16C datasheet for detailed +// explanation of the registers +typedef struct CPC_M16C_CAN_ERROR { + u8 tbd; // to be defined +} CPC_M16C_CAN_ERROR_T; + +// structure for CAN error conditions +#define CPC_CAN_ECODE_ERRFRAME 0x01 +typedef struct CPC_CAN_ERROR { + u8 ecode; + struct { + u8 cc_type; // CAN controller type + union { + CPC_SJA1000_CAN_ERROR_T sja1000; + CPC_M16C_CAN_ERROR_T m16c; + } regs; + } cc; +} CPC_CAN_ERROR_T; + +// Structure containing RX/TX error counter. +// This structure is used to request the +// values of the CAN controllers TX and RX +// error counter. +typedef struct CPC_CAN_ERR_COUNTER { + u8 rx; + u8 tx; +} CPC_CAN_ERR_COUNTER_T; + +// If this flag is set, transmissions from PC to CPC are protected against loss +#define CPC_SECURE_TO_CPC 0x01 + +// If this flag is set, transmissions from CPC to PC are protected against loss +#define CPC_SECURE_TO_PC 0x02 + +// If this flag is set, the CAN-transmit buffer is checked to be free before sending a message +#define CPC_SECURE_SEND 0x04 + +// If this flag is set, the transmission complete flag is checked +// after sending a message +// THIS IS CURRENTLY ONLY IMPLEMENTED IN THE PASSIVE INTERFACE DRIVERS +#define CPC_SECURE_TRANSMIT 0x08 + +// main message type used between library and application +typedef struct CPC_MSG { + u8 type; // type of message + u8 length; // length of data within union 'msg' + u8 msgid; // confirmation handle + u32 ts_sec; // timestamp in seconds + u32 ts_nsec; // timestamp in nano seconds + union { + u8 generic[CPC_MSG_LEN]; + CPC_CAN_MSG_T canmsg; + CPC_CAN_PARAMS_T canparams; + CPC_CONFIRM_T confirmation; + CPC_INFO_T info; + CPC_OVERRUN_T overrun; + CPC_CAN_ERROR_T error; + CPC_CAN_ERR_COUNTER_T err_counter; + u8 busload; + u8 canstate; + } msg; +} CPC_MSG_T; + +#ifdef _WIN32 +#pragma pack(pop) // reset the byte alignment +#endif + +#endif // CPC_HEADER Index: Makefile =================================================================== - --- Makefile (Revision 1048) +++ Makefile (Arbeitskopie) @@ -4,34 +4,35 @@ PWD := $(shell pwd) TOPDIR := $(PWD) - -export CONFIG_CAN_VCAN=m +export CONFIG_CAN_VCAN=n export CONFIG_CAN_SLCAN=m export CONFIG_CAN_DEV=m export CONFIG_CAN_CALC_BITTIMING=y #export CONFIG_CAN_DEV_SYSFS=y #export CONFIG_CAN_SJA1000_OLD=m #export CONFIG_CAN_I82527_OLD=m - -export CONFIG_CAN_SJA1000=m - -export CONFIG_CAN_SJA1000_PLATFORM=m +export CONFIG_CAN_SJA1000=n +export CONFIG_CAN_SJA1000_PLATFORM=n #export CONFIG_CAN_SJA1000_OF_PLATFORM=m - -export CONFIG_CAN_ESD_PCI=m - -export CONFIG_CAN_IXXAT_PCI=m - -export CONFIG_CAN_PEAK_PCI=m - -export CONFIG_CAN_KVASER_PCI=m - -export CONFIG_CAN_EMS_PCI=m - -export CONFIG_CAN_EMS_PCMCIA=m - -export CONFIG_CAN_EMS_104M=m - -export CONFIG_CAN_ESD_PCI=m - -export CONFIG_CAN_ESD_331=m - -export CONFIG_CAN_PIPCAN=m - -export CONFIG_CAN_SOFTING=m - -export CONFIG_CAN_SOFTING_CS=m - -export CONFIG_CAN_MCP251X=m +export CONFIG_CAN_ESD_PCI=n +export CONFIG_CAN_IXXAT_PCI=n +export CONFIG_CAN_PEAK_PCI=n +export CONFIG_CAN_KVASER_PCI=n +export CONFIG_CAN_EMS_PCI=n +export CONFIG_CAN_EMS_USB=m +export CONFIG_CAN_EMS_PCMCIA=n +export CONFIG_CAN_EMS_104M=n +export CONFIG_CAN_ESD_PCI=n +export CONFIG_CAN_ESD_331=n +export CONFIG_CAN_PIPCAN=n +export CONFIG_CAN_SOFTING=n +export CONFIG_CAN_SOFTING_CS=n +export CONFIG_CAN_MCP251X=n export CONFIG_CAN=m export CONFIG_CAN_RAW=m - -export CONFIG_CAN_BCM=m - -export CONFIG_CAN_ISOTP=m +export CONFIG_CAN_BCM=n +export CONFIG_CAN_ISOTP=n modules modules_install clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) $@ TOPDIR=$(TOPDIR) Index: drivers/net/can/Makefile =================================================================== - --- drivers/net/can/Makefile (Revision 1048) +++ drivers/net/can/Makefile (Arbeitskopie) @@ -49,6 +49,7 @@ obj-$(CONFIG_CAN_MSCAN_OLD) += old/mscan/ obj-$(CONFIG_CAN_CCAN_OLD) += old/ccan/ obj-$(CONFIG_CAN_MCP251X) += mcp251x.o +obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o ifeq ($(CONFIG_CAN_DEBUG_DEVICES),y) EXTRA_CFLAGS += -DDEBUG Index: drivers/net/can/ems_usb.c =================================================================== - --- drivers/net/can/ems_usb.c (Revision 0) +++ drivers/net/can/ems_usb.c (Revision 0) @@ -0,0 +1,668 @@ +/* + * CPC-USB CAN Interface Kernel Driver + * + * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <linux/init.h> +#include <linux/signal.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> +#include <asm/uaccess.h> + +#include <socketcan/can.h> +#include <socketcan/can/dev.h> +#include <socketcan/can/error.h> +#include <socketcan/can/dev.h> + +#include <linux/cpc.h> + +MODULE_AUTHOR("Sebastian Haas <[email protected]>"); +MODULE_DESCRIPTION("CPC-USB Driver for Linux Kernel 2.6"); +MODULE_VERSION("0.0"); +MODULE_LICENSE("GPL v2"); + +/* Define these values to match your devices */ +#define USB_CPCUSB_VENDOR_ID 0x12D6 + +#define USB_CPCUSB_M16C_PRODUCT_ID 0x0888 +#define USB_CPCUSB_LPC2119_PRODUCT_ID 0x0444 + +/* table of devices that work with this driver */ +static struct usb_device_id ems_usb_table[] = { + {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_M16C_PRODUCT_ID)}, + {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_LPC2119_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ems_usb_table); + +#define CPC_MSG_HEADER_LEN 11 + +#define RX_BUFFER_SIZE 64 +#define HEADER_SIZE 4 + +struct ems_usb { + struct can_priv can; /* must be the first member */ + + int open_time; + + struct usb_device *udev; + struct net_device *netdev; + + struct urb *rx_urb, *tx_urb, *intr_urb; + + __u8 *rx_buffer; + + __u8 *tx_msg_buffer; /* Buffer for synchronous commands */ + + __u8 intr_in_buffer[4]; + unsigned int free_slots; /* Remember number of freely available slots */ + + CPC_MSG_T active_params; /* Current state of controller parameters */ +}; + +#define GET_PARAMS(d) d->active_params.msg.canparams.cc_params.sja1000 + +#define MOD_RM 0x01 + +static void init_params(CPC_MSG_T *msg, __u8 btr0, __u8 btr1, + __u8 outp, __u8 mod) +{ + msg->type = CPC_CMD_T_CAN_PRMS; + msg->length = CPC_MSG_HEADER_LEN + sizeof(CPC_CAN_PARAMS_T); + msg->msgid = 0; + + msg->msg.canparams.cc_type = SJA1000; + + msg->msg.canparams.cc_params.sja1000.acc_code0 = 0; + msg->msg.canparams.cc_params.sja1000.acc_code1 = 0; + msg->msg.canparams.cc_params.sja1000.acc_code2 = 0; + msg->msg.canparams.cc_params.sja1000.acc_code3 = 0; + + msg->msg.canparams.cc_params.sja1000.acc_mask0 = 0xFF; + msg->msg.canparams.cc_params.sja1000.acc_mask1 = 0xFF; + msg->msg.canparams.cc_params.sja1000.acc_mask2 = 0xFF; + msg->msg.canparams.cc_params.sja1000.acc_mask3 = 0xFF; + + msg->msg.canparams.cc_params.sja1000.btr0 = btr0; + msg->msg.canparams.cc_params.sja1000.btr1 = btr1; + + msg->msg.canparams.cc_params.sja1000.outp_contr = outp; + msg->msg.canparams.cc_params.sja1000.mode = mod; +} + +static void ems_usb_read_interrupt_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev; + + int retval; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: + dev->free_slots = dev->intr_in_buffer[1]; + break; + + case -ECONNRESET:/* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + dev_info(ND2D(netdev), "nonzero urb status %d\n", urb->status); + break; + } + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if(retval) + dev_err(ND2D(netdev), "failed resubmitting intr urb: %d\n", retval); + + return; +} + +static int inline unalign_n_copy(__u8 *out, __u8 *in) +{ + unsigned int i, j; + + for (i = 0; i < 3; i++) + out[i] = in[i]; + + for (j = 0; j < (in[1] + (CPC_MSG_HEADER_LEN - 3)); j++) + out[j + i + 1] = in[j + i]; + + return i + j; +} + +static void ems_usb_rx_canmsg(struct ems_usb *dev, CPC_MSG_T *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + int i; + + struct net_device_stats *stats = &dev->netdev->stats; + + skb = dev_alloc_skb(sizeof(struct can_frame)); + if (skb == NULL) + return; + + skb->dev = dev->netdev; + skb->protocol = htons(ETH_P_CAN); + + cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame)); + memset(cf, 0, sizeof(struct can_frame)); + + cf->can_id = msg->msg.canmsg.id; + cf->can_dlc = msg->msg.canmsg.length > 8 ? 8 : msg->msg.canmsg.length; + + if(msg->type == CPC_MSG_T_XCAN || msg->type == CPC_MSG_T_XRTR) + cf->can_id |= CAN_EFF_FLAG; + + if(msg->type == CPC_MSG_T_RTR || msg->type == CPC_MSG_T_XRTR) { + cf->can_id |= CAN_RTR_FLAG; + } else { + for (i = 0; i < cf->can_dlc; i++) + cf->data[i] = msg->msg.canmsg.msg[i]; + + /* Clear remaining bytes */ + while (i < 8) + cf->data[i++] = 0; + } + + netif_rx(skb); + + dev->netdev->last_rx = jiffies; + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_read_bulk_callback(struct urb *urb) +{ + struct ems_usb *dev = urb->context; + struct net_device *netdev; + + int retval; + + netdev = dev->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + return; + + default: + dev_warn(ND2D(netdev), "nonzero URB status %d", urb->status); + goto resubmit_urb; + } + + if (urb->actual_length > HEADER_SIZE) { + CPC_MSG_T msg; + + __u8 *ibuf = urb->transfer_buffer; + + __u8 msgCnt, again, start; + + msgCnt = ibuf[0] & ~0x80; + again = ibuf[0] & 0x80; + + start = HEADER_SIZE; + + while (msgCnt) { + start += unalign_n_copy((__u8 *)&msg, &ibuf[start]); + + switch(msg.type) { + case CPC_MSG_T_CANSTATE: + /* TODO: Process CAN state changes */ + break; + + case CPC_MSG_T_CAN: + case CPC_MSG_T_XCAN: + case CPC_MSG_T_RTR: + case CPC_MSG_T_XRTR: + ems_usb_rx_canmsg(dev, &msg); + break; + + case CPC_MSG_T_CANERROR: + /* TODO: Process errorframe */ + break; + + case CPC_MSG_T_OVERRUN: + /* TODO: Message lost while receiving */ + break; + } + + if (start > urb->transfer_buffer_length) { + err("%d > %d", start, urb->transfer_buffer_length); + break; + } + + msgCnt--; + } + } + +resubmit_urb: + usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + dev->rx_buffer, RX_BUFFER_SIZE, ems_usb_read_bulk_callback, dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if(retval) + dev_err(ND2D(netdev), "failed resubmitting bulk urb: %d\n", retval); + + return; +} + +/* + * callback for bulk IN urb + */ +static void ems_usb_write_bulk_callback(struct urb *urb) +{ + return; +} + +static void inline align_buffer(CPC_MSG_T *msg) +{ + unsigned int i; + + for (i = 0; i < msg->length + (2 * sizeof(unsigned long)); i++) { + ((__u8 *)&msg->msgid)[1 + i] = + ((__u8 *)&msg->msgid)[2 + i]; + } +} + +static void prepare_command(__u8 *buf, CPC_MSG_T *msg) +{ + memcpy(&buf[HEADER_SIZE], msg, msg->length + CPC_MSG_HEADER_LEN); + + align_buffer((CPC_MSG_T *)&buf[HEADER_SIZE]); + + /* Clear header */ + memset(&buf[HEADER_SIZE], 0, HEADER_SIZE); +} + +/** + * Send the given CPC command synchronously + */ +static int ems_usb_command_msg(struct ems_usb *dev, CPC_MSG_T *msg) +{ + int actual_length; + + prepare_command(&dev->tx_msg_buffer[0], msg); + + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + &dev->tx_msg_buffer[0], + msg->length + CPC_MSG_HEADER_LEN + HEADER_SIZE, + &actual_length, 1000); +} + +static int ems_usb_control_cmd(struct ems_usb *dev, __u8 val) +{ + CPC_MSG_T cmd; + + cmd.type = CPC_CMD_T_CONTROL; + cmd.length = CPC_MSG_HEADER_LEN + 1; + + cmd.msgid = 0; + + cmd.msg.generic[0] = val; + + return ems_usb_command_msg(dev, &cmd); +} + +static int ems_usb_open(struct net_device *netdev) +{ + struct ems_usb *dev = netdev_priv(netdev); + int result; + + dev->can.state = CAN_STATE_STOPPED; + + dev->intr_in_buffer[0] = 0; + dev->free_slots = 15; /* initial size */ + + usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2), + dev->rx_buffer, RX_BUFFER_SIZE, ems_usb_read_bulk_callback, dev); + + result = usb_submit_urb(dev->rx_urb, GFP_KERNEL); + if (result) { + if (result == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(ND2D(netdev), "rx_urb submit failed: %d\n", result); + + return result; + } + + /* Setup and start interrupt URB */ + usb_fill_int_urb(dev->intr_urb, dev->udev, + usb_rcvintpipe(dev->udev, 1), + dev->intr_in_buffer, sizeof(dev->intr_in_buffer), + ems_usb_read_interrupt_callback, dev, 10); + + result = usb_submit_urb(dev->intr_urb, GFP_KERNEL); + if (result) { + if (result == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(ND2D(netdev), "intr urb submit failed: %d\n", result); + + usb_kill_urb(dev->rx_urb); + + return result; + } + + /* CPC-USB will transfer received message to host */ + result = ems_usb_control_cmd(dev, CONTR_CAN_Message | CONTR_CONT_ON); + if(result) + goto failed; + + /* CPC-USB will transfer CAN state changes to host */ + result = ems_usb_control_cmd(dev, CONTR_CAN_State | CONTR_CONT_ON); + if(result) + goto failed; + + /* CPC-USB will transfer bus errors to host */ + result = ems_usb_control_cmd(dev, CONTR_BusError | CONTR_CONT_ON); + if(result) + goto failed; + + /* common open */ + result = open_candev(netdev); + if (result) + goto failed; + + dev->open_time = jiffies; + + netif_start_queue(netdev); + + return 0; + +failed: + if (result == -ENODEV) + netif_device_detach(dev->netdev); + + dev_warn(ND2D(netdev), "couldn't submit control: %d\n", result); + + return result; +} + +static int ems_usb_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + // TODO: Create tx buffer and send + + return 0; +} + +static int ems_usb_close(struct net_device *dev) +{ + struct ems_usb *priv = netdev_priv(dev); + + netif_stop_queue(dev); + + GET_PARAMS(priv).mode = MOD_RM; + + if(ems_usb_command_msg(priv, &priv->active_params)) + dev_warn(ND2D(dev), "couldn't stop device"); + + close_candev(dev); + + priv->open_time = 0; + + return 0; +} + +static int alloc_all_urbs(struct ems_usb *dev) +{ + dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->rx_urb) + return 0; + + dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->tx_urb) { + usb_free_urb(dev->rx_urb); + return 0; + } + + dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->intr_urb) { + usb_free_urb(dev->rx_urb); + usb_free_urb(dev->tx_urb); + return 0; + } + + return 1; +} + +static void free_all_urbs(struct ems_usb *dev) +{ + usb_free_urb(dev->rx_urb); + usb_free_urb(dev->tx_urb); + usb_free_urb(dev->intr_urb); +} + +static void unlink_all_urbs(struct ems_usb *dev) +{ + usb_unlink_urb(dev->rx_urb); + usb_unlink_urb(dev->tx_urb); + usb_unlink_urb(dev->intr_urb); +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) +static const struct net_device_ops ems_usb_netdev_ops = { + .ndo_open = ems_usb_open, + .ndo_stop = ems_usb_close, + .ndo_start_xmit = ems_usb_start_xmit, +}; +#endif + +static struct can_bittiming_const ems_usb_bittiming_const = { + .name = "ems_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +static int ems_usb_set_mode(struct net_device *dev, enum can_mode mode) +{ + struct ems_usb *priv = netdev_priv(dev); + + if (!priv->open_time) + return -EINVAL; + + switch (mode) { + case CAN_MODE_START: + GET_PARAMS(priv).mode = 0; + + if(ems_usb_command_msg(priv, &priv->active_params)) + dev_warn(ND2D(dev), "couldn't start device"); + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int ems_usb_set_bittiming(struct net_device *dev) +{ + struct ems_usb *priv = netdev_priv(dev); + struct can_bittiming *bt = &priv->can.bittiming; + u8 btr0, btr1; + + btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6); + btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) | + (((bt->phase_seg2 - 1) & 0x7) << 4); + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + btr1 |= 0x80; + + dev_info(ND2D(dev), "setting BTR0=0x%02x BTR1=0x%02x\n", btr0, btr1); + + GET_PARAMS(priv).btr0 = btr0; + GET_PARAMS(priv).btr1 = btr1; + + return ems_usb_command_msg(priv, &priv->active_params); +} + +/* + * probe function for new CPC-USB devices + */ +static int ems_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *dev; + struct ems_usb *priv; + int result; + + dev = alloc_candev(sizeof(struct ems_usb)); + if (!dev) { + err("Out of memory"); + return -ENOMEM; + } + + priv = netdev_priv(dev); + + priv->udev = interface_to_usbdev(intf); + priv->netdev = dev; + + priv->can.bittiming_const = &ems_usb_bittiming_const; + priv->can.do_set_bittiming = &ems_usb_set_bittiming; + priv->can.do_set_mode = &ems_usb_set_mode; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) + dev->netdev_ops = &ems_usb_netdev_ops; +#else + dev->open = ems_usb_open; + dev->stop = ems_usb_close; + dev->hard_start_xmit = ems_usb_start_xmit; +#endif + + // TODO: Which flags to we support? + dev->flags |= IFF_ECHO; /* we support local echo */ + + if (!alloc_all_urbs(priv)) { + err("Out of memory"); + free_candev(dev); + return -ENOMEM; + } + + priv->rx_buffer = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL); + if (!priv->rx_buffer) { + err("Out of memory"); + free_candev(dev); + free_all_urbs(priv); + return -ENOMEM; + } + + priv->tx_msg_buffer = kzalloc(sizeof(CPC_MSG_T) + HEADER_SIZE, GFP_KERNEL); + if (!priv->tx_msg_buffer) { + err("Out of memory"); + free_candev(dev); + free_all_urbs(priv); + kfree(priv->rx_buffer); + return -ENOMEM; + } + + usb_set_intfdata(intf, priv); + + SET_NETDEV_DEV(dev, &intf->dev); + + init_params(&priv->active_params, 0x00, 0x00, 0xDA, MOD_RM); + result = ems_usb_command_msg(priv, &priv->active_params); + + if(result) + dev_warn(ND2D(dev), "couldn't initialize controller: %d\n", result); + + return register_candev(dev); +} + +/* + * called by the usb core when the device is removed from the system + */ +static void ems_usb_disconnect(struct usb_interface *intf) +{ + struct ems_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (dev) { + unregister_netdev(dev->netdev); + free_candev(dev->netdev); + + unlink_all_urbs(dev); + free_all_urbs(dev); + } +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver ems_usb_driver = { + .name = "ems_usb", + .probe = ems_usb_probe, + .disconnect = ems_usb_disconnect, + .id_table = ems_usb_table, +}; + +static int __init ems_usb_init(void) +{ + int result; + + printk(KERN_INFO "CPC-USB kernel driver loaded\n"); + + /* register this driver with the USB subsystem */ + result = usb_register(&ems_usb_driver); + + if (result) { + err("usb_register failed. Error number %d", result); + return result; + } + + return 0; +} + +static void __exit ems_usb_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&ems_usb_driver); +} + +module_init(ems_usb_init); +module_exit(ems_usb_exit); -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iEYEARECAAYFAkqlKfgACgkQpqRB8PJG7XwxrQCfR4Xgdy/HPh7RmQqeYqMppClJ Q1UAn3p9W5aJL1ZW2XVd4jTl2TGsjEpP =b2M9 -----END PGP SIGNATURE----- -- EMS Dr. Thomas Wuensche e.K. Sonnenhang 3 85304 Ilmmuenster HRA Neuburg a.d. Donau, HR-Nr. 70.106 Phone: +49-8441-490260 Fax : +49-8441-81860 http://www.ems-wuensche.com _______________________________________________ Socketcan-core mailing list [email protected] https://lists.berlios.de/mailman/listinfo/socketcan-core
