📄 pcan_usb.c
字号:
//****************************************************************************// Copyright (C) 2003-2008 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// // Contributions: Philipp Baer (philipp.baer@informatik.uni-ulm.de)// Tom Heinrich// John Privitera (JohnPrivitera@dciautomation.com)//****************************************************************************//****************************************************************************//// pcan_usb.c - the outer usb parts for pcan-usb support//// $Id: pcan_usb.c 534 2008-02-04 21:34:07Z khitschler $////****************************************************************************//****************************************************************************// INCLUDES#include <src/pcan_common.h> // must always be the 1st include#include <linux/stddef.h> // NULL#include <linux/errno.h>#include <linux/slab.h> // kmalloc()#include <linux/module.h> // MODULE_DEVICE_TABLE()#include <linux/usb.h>#include <src/pcan_main.h>#include <src/pcan_fops.h>#include <src/pcan_usb.h>#include <src/pcan_usb_kernel.h>#include <src/pcan_filter.h>#ifdef NETDEV_SUPPORT#include <src/pcan_netdev.h> // for hotplug pcan_netdev_(un)register()#endif//****************************************************************************// DEFINES#define PCAN_USB_MINOR_BASE 32 // starting point of minors for USB devices#define PCAN_USB_VENDOR_ID 0x0c72#define PCAN_USB_PRODUCT_ID 0x000c#define URB_READ_BUFFER_SIZE 1024 // buffer for read URB data (IN)#define URB_READ_BUFFER_SIZE_OLD 64 // used length for revision < 7#define URB_WRITE_BUFFER_SIZE 128 // buffer for write URB (OUT)#define URB_WRITE_BUFFER_SIZE_OLD 64 // used length for hardware < 7#define MAX_CYCLES_TO_WAIT_FOR_RELEASE 100 // max no. of schedules before release#define STARTUP_WAIT_TIME 2 // wait this time at startup to get first messages//****************************************************************************// GLOBALSstatic struct usb_device_id pcan_usb_ids[] ={ { USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID) }, { } // Terminating entry};MODULE_DEVICE_TABLE (usb, pcan_usb_ids);#ifdef LINUX_26static struct usb_class_driver pcan_class ={ .name = "usb/pcan%d", .fops = &pcan_fops, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, #endif .minor_base = PCAN_USB_MINOR_BASE,};#endif//****************************************************************************// LOCALSstatic u16 usb_devices = 0; // the number of accepted usb_devices//****************************************************************************// CODE//****************************************************************************// get cyclic data from endpoint 2#ifdef LINUX_26static void pcan_usb_write_notify(struct urb *purb, struct pt_regs *pregs);#elsestatic void pcan_usb_write_notify(purb_t purb);#endifstatic int pcan_usb_write(struct pcandev *dev){ int err = 0; USB_PORT *u = &dev->port.usb; int nDataLength; int m_nCurrentLength; DPRINTK(KERN_DEBUG "%s: pcan_usb_write()\n", DEVICE_NAME); // don't do anything with non-existent hardware if (!dev->ucPhysicallyInstalled) return -ENODEV; // improvement to control 128 bytes buffers m_nCurrentLength = URB_WRITE_BUFFER_SIZE_OLD; err = pcan_hw_EncodeMessage(dev, u->pucWriteBuffer, &m_nCurrentLength); if (err || (u->ucRevision < 7)) nDataLength = m_nCurrentLength; else { m_nCurrentLength = URB_WRITE_BUFFER_SIZE_OLD; err = pcan_hw_EncodeMessage(dev, u->pucWriteBuffer + 64, &m_nCurrentLength); nDataLength = URB_WRITE_BUFFER_SIZE_OLD + m_nCurrentLength; } // fill the URB and submit if (nDataLength) { FILL_BULK_URB(u->write_data, u->usb_dev, usb_sndbulkpipe(u->usb_dev, u->Endpoint[3].ucNumber), u->pucWriteBuffer, nDataLength, pcan_usb_write_notify, dev); // start next urb if ((err = __usb_submit_urb(u->write_data))) { dev->nLastError = err; dev->dwErrorCounter++; DPRINTK(KERN_ERR "%s: pcan_usb_write() can't submit! (%d)",DEVICE_NAME, err); } else atomic_inc(&dev->port.usb.active_urbs); } // it's no error if I can't get more data but still a packet was sent if ((err == -ENODATA) && (nDataLength)) err = 0; return err;}#ifdef NETDEV_SUPPORTstatic int pcan_usb_write_frame(struct pcandev *dev, struct can_frame *cf){ int err = 0; USB_PORT *u = &dev->port.usb; int nDataLength; int m_nCurrentLength; DPRINTK(KERN_DEBUG "%s: %s\n", DEVICE_NAME, __FUNCTION__); // don't do anything with non-existent hardware if (!dev->ucPhysicallyInstalled) return -ENODEV; // improvement to control 128 bytes buffers m_nCurrentLength = URB_WRITE_BUFFER_SIZE_OLD; err = pcan_hw_EncodeMessage_frame(dev, cf, u->pucWriteBuffer, &m_nCurrentLength); if (err || (u->ucRevision < 7)) nDataLength = m_nCurrentLength; else { m_nCurrentLength = URB_WRITE_BUFFER_SIZE_OLD; err = pcan_hw_EncodeMessage_frame(dev, cf, u->pucWriteBuffer + 64, &m_nCurrentLength); nDataLength = URB_WRITE_BUFFER_SIZE_OLD + m_nCurrentLength; } // fill the URB and submit if (nDataLength) { FILL_BULK_URB(u->write_data, u->usb_dev, usb_sndbulkpipe(u->usb_dev, u->Endpoint[3].ucNumber), u->pucWriteBuffer, nDataLength, pcan_usb_write_notify, dev); // start next urb if ((err = __usb_submit_urb(u->write_data))) { dev->nLastError = err; dev->dwErrorCounter++; DPRINTK(KERN_ERR "%s: pcan_usb_write_frame() can't submit! (%d)",DEVICE_NAME, err); } else atomic_inc(&dev->port.usb.active_urbs); } // it's no error if I can't get more data but still a packet was sent if ((err == -ENODATA) && (nDataLength)) err = 0; return err;}#endif//****************************************************************************// notify functions for read and write data#ifdef LINUX_26static void pcan_usb_write_notify(struct urb *purb, struct pt_regs *pregs)#elsestatic void pcan_usb_write_notify(purb_t purb)#endif{ int err = 0; u16 wwakeup = 0; struct pcandev *dev = purb->context; // DPRINTK(KERN_DEBUG "%s: pcan_usb_write_notify() (%d)\n", DEVICE_NAME, purb->status); // un-register outstanding urb atomic_dec(&dev->port.usb.active_urbs); // don't count interrupts - count packets dev->dwInterruptCounter++; // do write if (!purb->status) // stop with first error err = pcan_usb_write(dev); if (err) { if (err == -ENODATA) wwakeup++; else { DPRINTK(KERN_DEBUG "%s: unexpected error %d from pcan_usb_write()\n", DEVICE_NAME, err); dev->nLastError = err; dev->dwErrorCounter++; dev->wCANStatus |= CAN_ERR_QXMTFULL; // fatal error! } } if (wwakeup) { atomic_set(&dev->DataSendReady, 1); // signal to write I'm ready wake_up_interruptible(&dev->write_queue); #ifdef NETDEV_SUPPORT netif_wake_queue(dev->netdev); #endif }}#ifdef LINUX_26static void pcan_usb_read_notify(struct urb *purb, struct pt_regs *pregs)#elsestatic void pcan_usb_read_notify(purb_t purb)#endif{ int err = 0; struct pcandev *dev = purb->context; USB_PORT *u = &dev->port.usb; // DPRINTK(KERN_DEBUG "%s: pcan_usb_read_notify() (%d)\n", DEVICE_NAME, purb->status); // un-register outstanding urb atomic_dec(&dev->port.usb.active_urbs); // don't count interrupts - count packets dev->dwInterruptCounter++; // do interleaving read if (!purb->status && dev->ucPhysicallyInstalled) // stop with first error { u8 *m_pucTransferBuffer = purb->transfer_buffer; int m_nCurrentLength = purb->actual_length; // buffer interleave to increase speed if (m_pucTransferBuffer == u->pucReadBuffer[0]) { FILL_BULK_URB(purb, u->usb_dev, usb_rcvbulkpipe(u->usb_dev, u->Endpoint[2].ucNumber), u->pucReadBuffer[1], u->wReadBufferLength, pcan_usb_read_notify, dev); } else { FILL_BULK_URB(purb, u->usb_dev, usb_rcvbulkpipe(u->usb_dev, u->Endpoint[2].ucNumber), u->pucReadBuffer[0], u->wReadBufferLength, pcan_usb_read_notify, dev); } // start next urb if ((err = __usb_submit_urb(purb))) { dev->nLastError = err; dev->dwErrorCounter++; printk(KERN_ERR "%s: pcan_usb_read_notify() can't submit! (%d)",DEVICE_NAME, err); } else atomic_inc(&dev->port.usb.active_urbs); do { err = pcan_hw_DecodeMessage(dev, m_pucTransferBuffer, m_nCurrentLength); if (err < 0) { dev->nLastError = err; dev->wCANStatus |= CAN_ERR_QOVERRUN; dev->dwErrorCounter++; DPRINTK(KERN_DEBUG "%s: error %d from pcan_hw_DecodeMessage()\n", DEVICE_NAME, err); } m_pucTransferBuffer += 64; m_nCurrentLength -= 64; } while (m_nCurrentLength > 0); } else { if (purb->status != -ENOENT) { printk(KERN_ERR "%s: read data stream torn off caused by ", DEVICE_NAME); if (!dev->ucPhysicallyInstalled) printk("device plug out!\n"); else printk("err %d!\n", purb->status); } }}//****************************************************************************// usb resource allocation //static int pcan_usb_allocate_resources(struct pcandev *dev){ int err = 0; USB_PORT *u = &dev->port.usb; DPRINTK(KERN_DEBUG "%s: pcan_usb_allocate_resources()\n", DEVICE_NAME); // allocate PCAN_USB_TIME data for comparison // TODO: integrate PCAN_USB_TIME in USB_PORT if (!u->pUSBtime) { if (!(u->pUSBtime = kmalloc(sizeof(PCAN_USB_TIME), GFP_ATOMIC))) { err = -ENOMEM; goto fail; } } dev->wInitStep = 4; // make param URB u->param_urb = __usb_alloc_urb(0); if (!u->param_urb) err = -ENOMEM; dev->wInitStep = 5; // allocate write buffer if (u->ucRevision < 7) u->pucWriteBuffer = kmalloc(URB_WRITE_BUFFER_SIZE_OLD, GFP_ATOMIC); else u->pucWriteBuffer = kmalloc(URB_WRITE_BUFFER_SIZE, GFP_ATOMIC); if (!u->pucWriteBuffer) { err = -ENOMEM; goto fail; } dev->wInitStep = 6; // make write urb u->write_data = __usb_alloc_urb(0); if (!u->write_data) { err = -ENOMEM; goto fail; } dev->wInitStep = 7; // reset telegram count u->dwTelegramCount = 0; // allocate both read buffers for URB u->pucReadBuffer[0] = kmalloc(URB_READ_BUFFER_SIZE * 2, GFP_ATOMIC); if (!u->pucReadBuffer[0]) { err = -ENOMEM; goto fail; } u->pucReadBuffer[1] = u->pucReadBuffer[0] + URB_READ_BUFFER_SIZE; dev->wInitStep = 8; // make read urb u->read_data = __usb_alloc_urb(0); if (!u->read_data) { err = -ENOMEM; goto fail; } dev->wInitStep = 9; // different revisions use different buffer sizes if (u->ucRevision < 7) u->wReadBufferLength = URB_READ_BUFFER_SIZE_OLD; else u->wReadBufferLength = URB_READ_BUFFER_SIZE; fail: return err;}//****************************************************************************// usb resource de-allocation //static int pcan_usb_free_resources(struct pcandev *dev){ int err = 0; USB_PORT *u = &dev->port.usb; DPRINTK(KERN_DEBUG "%s: pcan_usb_free_resources()\n", DEVICE_NAME); // at this point no URB must be pending // this is forced at pcan_usb_stop switch (dev->wInitStep) { case 9: // free read URB usb_free_urb(u->read_data); case 8: kfree(u->pucReadBuffer[0]); case 7: // free write urb usb_free_urb(u->write_data); case 6: kfree(u->pucWriteBuffer); case 5: // free param urb usb_free_urb(u->param_urb); case 4: // remove PCAN_USB_TIME information if (u->pUSBtime) { kfree(u->pUSBtime); u->pUSBtime = NULL; } dev->wInitStep = 3; } return err;}//****************************************************************************// start and stop functions for CAN data IN and OUTstatic int pcan_usb_start(struct pcandev *dev){ int err = 0; USB_PORT *u = &dev->port.usb; DPRINTK(KERN_DEBUG "%s: pcan_usb_start(), minor = %d.\n", DEVICE_NAME, dev->nMinor); FILL_BULK_URB(u->read_data, u->usb_dev, usb_rcvbulkpipe(u->usb_dev, u->Endpoint[2].ucNumber), u->pucReadBuffer[0], u->wReadBufferLength, pcan_usb_read_notify, dev); // submit urb if ((err = __usb_submit_urb(u->read_data))) printk(KERN_ERR "%s: pcan_usb_start() can't submit! (%d)\n",DEVICE_NAME, err); else atomic_inc(&dev->port.usb.active_urbs); return err;}static int pcan_kill_sync_urb(struct urb *urb){ int err = 0; if (urb->status == -EINPROGRESS) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) usb_kill_urb(urb); #else err = usb_unlink_urb(urb); #endif DPRINTK(KERN_DEBUG "%s: pcan_kill_sync_urb done ...\n", DEVICE_NAME); } return err;}static int pcan_usb_stop(struct pcandev *dev){ int err = 0; USB_PORT *u = &dev->port.usb; int i = MAX_CYCLES_TO_WAIT_FOR_RELEASE; DPRINTK(KERN_DEBUG "%s: pcan_usb_stop(), minor = %d.\n", DEVICE_NAME, dev->nMinor); err = pcan_hw_SetCANOff(dev); // wait until all has settled mdelay(5); // unlink URBs pcan_kill_sync_urb(u->read_data); pcan_kill_sync_urb(u->write_data); pcan_kill_sync_urb(u->param_urb); // wait until all urbs returned to sender // (I hope) this should be no problem because all urb's are unlinked while ((atomic_read(&u->active_urbs) > 0) && (i--)) schedule(); if (i <= 0) { DPRINTK(KERN_ERR "%s: have still active URBs: %d!\n", DEVICE_NAME, atomic_read(&u->active_urbs)); } return err;}//****************************************************************************// remove device resources //static int pcan_usb_cleanup(struct pcandev *dev){ DPRINTK(KERN_DEBUG "%s: pcan_usb_cleanup()\n", DEVICE_NAME); if (dev) { pcan_usb_free_resources(dev); switch(dev->wInitStep) { case 4: #ifdef NETDEV_SUPPORT pcan_netdev_unregister(dev); #endif case 3: usb_devices--; pcan_drv.wDeviceCount--; case 2: list_del(&dev->list); case 1: case 0: pcan_delete_filter_chain(dev->filter); dev->filter = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -