📄 pcan_usb_kernel.c
字号:
//****************************************************************************// Copyright (C) 2003-2007 PEAK System-Technik GmbH//// linux@peak-system.com// www.peak-system.com //// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.//// Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)//// Major contributions by:// Oliver Hartkopp (oliver.hartkopp@volkswagen.de) socketCAN// //****************************************************************************//****************************************************************************//// pcan_usb-kernel.c - the inner parts for pcan-usb support//// $Id: pcan_usb_kernel.c 510 2007-05-29 13:07:10Z khitschler $////****************************************************************************//****************************************************************************// INCLUDES#include <src/pcan_common.h>#include <linux/sched.h>#include <src/pcan_main.h>#include <src/pcan_fifo.h>#include <src/pcan_usb_kernel.h>#include <asm/byteorder.h> // because of little / big endian#ifdef NETDEV_SUPPORT#include <src/pcan_netdev.h> // for hotplug pcan_netdev_register()#endif//****************************************************************************// DEFINES// bit masks for status/length field in a USB message#define STLN_WITH_TIMESTAMP 0x80#define STLN_INTERNAL_DATA 0x40#define STLN_EXTENDED_ID 0x20#define STLN_RTR 0x10#define STLN_DATA_LENGTH 0x0F // mask for length of data bytes// Error-Flags for PCAN-USB#define XMT_BUFFER_FULL 0x01#define CAN_RECEIVE_QUEUE_OVERRUN 0x02#define BUS_LIGHT 0x04#define BUS_HEAVY 0x08#define BUS_OFF 0x10#define QUEUE_RECEIVE_EMPTY 0x20#define QUEUE_OVERRUN 0x40#define QUEUE_XMT_FULL 0x80// timestamp calculation stuff#define FASTSCALE // use this for slow architectures without native 64 bit division support#define SCALED_MICROSECONDS_PER_TICK 44739 // error = 5,424e-4%#define SCALE_SHIFTER 20 // unscale, shift runs faster than divide#define COMPARE_OFFSET 128 // enhance wrap comparison tolerance caused by idle timestamps#define SCALE_MULTIPLIER 1024 // multiplier for direct calculation of timestamp#define SCALE_DIVISOR 24000 // divisor for direct calculation of timestamp#define TICKS(msec) ((msec * HZ) / 1000) // to calculate ticks from milliseconds#define COMMAND_TIMEOUT 1000 // msec timeout for control EP0 urb get settypedef struct // pcan-usb parameter get an set function{ u8 Function; u8 Number; u8 Param[14];} __attribute__ ((packed)) PCAN_USB_PARAM;//****************************************************************************// GLOBALS//****************************************************************************// LOCALS//****************************************************************************// CODE //****************************************************************************// functions to handle time constructionstatic void pcan_reset_timestamp(struct pcandev *dev){ PCAN_USB_TIME *t = dev->port.usb.pUSBtime; DPRINTK(KERN_DEBUG "%s: reset_timestamp()\n", DEVICE_NAME); // reset time stamp information t->ullCumulatedTicks = 0; t->ullOldCumulatedTicks = 0; t->wLastTickValue = 0; t->ucLastTickValue = 0; t->wOldLastTickValue = 0; t->StartTime.tv_sec = 0; t->StartTime.tv_usec = 0; t->wStartTicks = 0;}#if 0// TODO: take the timestamp from ticks * (42 + 2/3) usecs of PCAN-USBstatic inline void pcan_calcTimevalFromTicks(struct pcandev *dev, struct timeval *tv){ register PCAN_USB_TIME *t = dev->port.usb.pUSBtime; u64 llx; llx = t->ullCumulatedTicks - t->wStartTicks; // subtract initial offset #ifdef FASTSCALE llx *= SCALED_MICROSECONDS_PER_TICK; return (u32)(llx >>= SCALE_SHIFTER); // unscale, shift runs faster than divide #else llx *= SCALE_MULTIPLIER; return ((u32)(*(u64 *)&llx) / SCALE_DIVISOR); // trick to circumvent missing __udivid3 entry message #endif}static inline void pcan_getTimeStamp(struct pcandev *dev, struct timeval *tv){ ...}#endifstatic void pcan_updateTimeStampFromWord(struct pcandev *dev, u16 wTimeStamp, u8 ucStep){ register PCAN_USB_TIME *t = dev->port.usb.pUSBtime; // DPRINTK(KERN_DEBUG "%s: updateTimeStampFromWord() Tim = %d, Last = %d, Cum = %lld\n", DEVICE_NAME, wTimeStamp, t->wLastTickValue, t->ullCumulatedTicks); if ((!t->StartTime.tv_sec) && (!t->StartTime.tv_usec)) { get_relative_time(NULL, &t->StartTime); t->wStartTicks = wTimeStamp; t->wOldLastTickValue = wTimeStamp; t->ullCumulatedTicks = wTimeStamp; t->ullOldCumulatedTicks = wTimeStamp; } // correction for status timestamp in the same telegram which is more recent, restore old contents if (ucStep) { t->ullCumulatedTicks = t->ullOldCumulatedTicks; t->wLastTickValue = t->wOldLastTickValue; } // store current values for old ... t->ullOldCumulatedTicks = t->ullCumulatedTicks; t->wOldLastTickValue = t->wLastTickValue; if (wTimeStamp < t->wLastTickValue) // handle wrap, enhance tolerance t->ullCumulatedTicks += 0x10000LL; t->ullCumulatedTicks &= ~0xFFFFLL; // mask in new 16 bit value - do not cumulate cause of error propagation t->ullCumulatedTicks |= wTimeStamp; t->wLastTickValue = wTimeStamp; // store for wrap recognition t->ucLastTickValue = (u8)(wTimeStamp & 0xff); // each update for 16 bit tick updates the 8 bit tick, too}static void pcan_updateTimeStampFromByte(struct pcandev *dev, u8 ucTimeStamp){ register PCAN_USB_TIME *t = dev->port.usb.pUSBtime; // DPRINTK(KERN_DEBUG "%s: updateTimeStampFromByte() Tim = %d, Last = %d, Cum = %lld\n", DEVICE_NAME, ucTimeStamp, t->ucLastTickValue, t->ullCumulatedTicks); if (ucTimeStamp < t->ucLastTickValue) // handle wrap { t->ullCumulatedTicks += 0x100; t->wLastTickValue += 0x100; } t->ullCumulatedTicks &= ~0xFFLL; // mask in new 8 bit value - do not cumulate cause of error propagation t->ullCumulatedTicks |= ucTimeStamp; t->wLastTickValue &= ~0xFF; // correction for word timestamp, too t->wLastTickValue |= ucTimeStamp; t->ucLastTickValue = ucTimeStamp; // store for wrap recognition}//****************************************************************************// get and set parameters to or from device//#ifdef LINUX_26static void pcan_param_xmit_notify(struct urb *purb, struct pt_regs *regs)#elsestatic void pcan_param_xmit_notify(purb_t purb)#endif{ struct pcandev *dev = purb->context; DPRINTK(KERN_DEBUG "%s: pcan_param_xmit_notify() = %d\n", DEVICE_NAME, purb->status); // un-register outstanding urb atomic_dec(&dev->port.usb.active_urbs); atomic_set(&dev->port.usb.param_xmit_finished, 1);}static int pcan_hw_setcontrol_urb(struct pcandev *dev, u8 function, u8 number, u8 param0, u8 param1, u8 param2, u8 param3, u8 param4, u8 param5, u8 param6, u8 param7, u8 param8, u8 param9, u8 param10, u8 param11, u8 param12, u8 param13){ PCAN_USB_PARAM myParameter; int nResult = 0; register purb_t pt; // DPRINTK(KERN_DEBUG "%s: pcan_set_parameter()\n", DEVICE_NAME); // don't do anything with non-existent hardware if (!dev->ucPhysicallyInstalled) return -ENODEV; myParameter.Function = function; myParameter.Number = number; myParameter.Param[0] = param0; myParameter.Param[1] = param1; myParameter.Param[2] = param2; myParameter.Param[3] = param3; myParameter.Param[4] = param4; myParameter.Param[5] = param5; myParameter.Param[6] = param6; myParameter.Param[7] = param7; myParameter.Param[8] = param8; myParameter.Param[9] = param9; myParameter.Param[10] = param10; myParameter.Param[11] = param11; myParameter.Param[12] = param12; myParameter.Param[13] = param13; pt = dev->port.usb.param_urb; FILL_BULK_URB(pt, dev->port.usb.usb_dev, usb_sndbulkpipe(dev->port.usb.usb_dev, dev->port.usb.Endpoint[1].ucNumber), &myParameter, sizeof(myParameter), pcan_param_xmit_notify, dev); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) pt->timeout = TICKS(COMMAND_TIMEOUT); #endif if (__usb_submit_urb(pt)) { DPRINTK(KERN_ERR "%s: pcan_set_parameter() can't submit!\n",DEVICE_NAME); nResult = pt->status; goto fail; } else atomic_inc(&dev->port.usb.active_urbs); // wait until submit is finished, either normal or thru timeout while (!atomic_read(&dev->port.usb.param_xmit_finished)) schedule(); // remove urb nResult = pt->status; fail: atomic_set(&dev->port.usb.param_xmit_finished, 0); return nResult;} static int pcan_set_function(struct pcandev *dev, u8 function, u8 number){ DPRINTK(KERN_DEBUG "%s: pcan_set_function(%d, %d)\n", DEVICE_NAME, function, number); return pcan_hw_setcontrol_urb(dev, function, number, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);} // in opposition to WIN method this performs the complete write and read cycle!static int pcan_hw_getcontrol_urb(struct pcandev *dev, u8 function, u8 number, u8 *param0, u8 *param1, u8 *param2, u8 *param3, u8 *param4, u8 *param5, u8 *param6, u8 *param7){ PCAN_USB_PARAM myParameter; int nResult = 0; register purb_t pt; USB_PORT *u = &dev->port.usb; DPRINTK(KERN_DEBUG "%s: pcan_hw_getcontrol_urb(%d, %d)\n", DEVICE_NAME, function, number); // don't do anything with non-existent hardware if (!dev->ucPhysicallyInstalled) return -ENODEV; // first write function and number to device nResult = pcan_set_function(dev, function, number); // heuristic result - wait a little bit mdelay(5); if (!nResult) { u32 startTime; pt = u->param_urb; FILL_BULK_URB(pt, u->usb_dev, usb_rcvbulkpipe(u->usb_dev, u->Endpoint[0].ucNumber), &myParameter, sizeof(myParameter), pcan_param_xmit_notify, dev); myParameter.Function = function; myParameter.Number = number; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) pt->timeout = TICKS(COMMAND_TIMEOUT); #endif if (__usb_submit_urb (pt)) { printk(KERN_ERR "%s: pcan_get_parameter() can't submit!\n",DEVICE_NAME); nResult = pt->status; goto fail; } else atomic_inc(&u->active_urbs); startTime = get_mtime(); while (!atomic_read(&u->param_xmit_finished) && ((get_mtime() - startTime) < COMMAND_TIMEOUT)) schedule(); if (!atomic_read(&u->param_xmit_finished)) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) usb_kill_urb(pt); #else usb_unlink_urb(pt); /* any better solution here for Kernel 2.4 ? */ #endif if (!pt->status) { *param0 = myParameter.Param[0]; *param1 = myParameter.Param[1]; *param2 = myParameter.Param[2]; *param3 = myParameter.Param[3]; *param4 = myParameter.Param[4]; *param5 = myParameter.Param[5]; *param6 = myParameter.Param[6]; *param7 = myParameter.Param[7]; } nResult = pt->status; } fail: atomic_set(&u->param_xmit_finished, 0); return nResult;}//****************************************************************************// specialized high level hardware access functions//static int pcan_hw_setBTR0BTR1(struct pcandev *dev, u16 wBTR0BTR1){ u8 dummy = 0; u8 param0 = (u8)(wBTR0BTR1 & 0xff); u8 param1 = (u8)((wBTR0BTR1 >> 8) & 0xff); DPRINTK(KERN_DEBUG "%s: pcan_hw_setBTR0BTR1(0x%04x)\n", DEVICE_NAME, wBTR0BTR1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -