📄 pcan_fops.c
字号:
//****************************************************************************// Copyright (C) 2001,2002,2003 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)//****************************************************************************//****************************************************************************//// pcan_fops.c - all file operation functions, exports only struct fops//// $Log: pcan_fops.c,v $// Revision 1.30 2003/03/02 10:58:07 klaus// merged USB thread into main path//// Revision 1.29 2003/03/02 10:58:07 klaus// merged USB thread into main path//// Revision 1.28.2.27 2003/02/25 20:25:02 klaus// correction for core dump in 'receivetest -t=pci'//// Revision 1.28.2.26 2003/02/23 19:33:06 klaus// tried to fix "not possible to stop" bug//// Revision 1.28.2.25 2003/02/21 23:26:55 klaus// obfuscation for pcan_usb_kernel.c improved//// Revision 1.28.2.24 2003/02/16 16:36:16 klaus// pcan_usb_kernel.c returned to main modules//// Revision 1.28.2.23 2003/02/08 17:32:43 klaus// modified to use pcan_usb_kernel as proprietary module//// Revision 1.28.2.22 2003/02/05 23:12:19 klaus// adapted to RedHat 7.2//// Revision 1.28.2.21 2003/01/29 20:34:20 klaus// release_20030129_a and release_20030129_u released//// Revision 1.28.2.20 2003/01/29 20:34:20 klaus// release_20030129_a and release_20030129_u released//// Revision 1.28.2.19 2003/01/28 23:28:26 klaus// reorderd pcan_usb.c and pcan_usb_kernel.c, tidied up////****************************************************************************//****************************************************************************// INCLUDES#include <src/pcan_common.h>// must always be the 1st include#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h> // DPRINTK()#include <linux/slab.h> // kmalloc()#include <linux/fs.h> // everything...#include <linux/errno.h> // error codes#include <linux/types.h> // size_t#include <linux/proc_fs.h> // proc #include <linux/fcntl.h> // O_ACCMODE#include <linux/pci.h> // all about pci#include <linux/capability.h> // all about restrictions#include <asm/system.h> // cli(), *_flags#include <asm/uaccess.h> // copy_...#include <linux/delay.h> // mdelay()#include <linux/poll.h> // poll() and select()#include <pcan.h>#include <src/pcan_main.h>#include <src/pcan_pci.h>#include <src/pcan_isa.h>#include <src/pcan_dongle.h>#include <src/pcan_sja1000.h>#include <src/pcan_fifo.h>#include <src/pcan_fops.h>#include <src/pcan_parse.h>#include <src/pcan_usb.h>//****************************************************************************// DEFINESMODULE_AUTHOR("klaus.hitschler@gmx.de");MODULE_DESCRIPTION("Driver for PEAK-Systems CAN interfaces.");#ifdef USB_SUPPORTMODULE_SUPPORTED_DEVICE("PCAN-ISA, PCAN-PC/104, PCAN-Dongle, PCAN-PCI, PCAN-USB");#elseMODULE_SUPPORTED_DEVICE("PCAN-ISA, PCAN-PC/104, PCAN-Dongle, PCAN-PCI");#endif#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,17)#ifdef USB_SUPPORTMODULE_LICENSE("Proprietary");#elseMODULE_LICENSE("GPL");#endif#endifMODULE_PARM(type, "0-8s");MODULE_PARM_DESC(type, "The type of PCAN interface (isa, sp, epp)");MODULE_PARM(io, "0-8h");MODULE_PARM_DESC(io, "The io-port address for either PCAN-ISA, PC/104 or Dongle");MODULE_PARM(irq, "0-8b");MODULE_PARM_DESC(irq, "The interrupt number for either PCAN-ISA, PC/104 or Dongle");// wait this time in msec at max after releasing the device - give fifo a chance to flush#define MAX_WAIT_UNTIL_CLOSE 1000 //****************************************************************************// GLOBALSEXPORT_NO_SYMBOLS;//****************************************************************************// LOCALS//****************************************************************************// CODE //----------------------------------------------------------------------------// wait until write fifo is empty, max time in msecstatic void wait_until_fifo_empty(struct pcandev *dev, u32 mTime){ u32 dwStart = get_mtime(); while (!atomic_read(&dev->DataSendReady) && ((get_mtime() - dwStart) < mTime)) schedule(); // force it atomic_set(&dev->DataSendReady, 1);} //----------------------------------------------------------------------------// is called when the path is openedstatic int pcan_open(struct inode *inode, struct file *filep){ struct pcandev *dev = (struct pcandev *)NULL; struct fileobj *fobj = (struct fileobj *)NULL; struct list_head *ptr; int err = 0; int minor = MINOR(inode->i_rdev); DPRINTK(KERN_DEBUG "%s: pcan_open(), minor = %d.\n", DEVICE_NAME, minor); // no devices in list err = -ENODEV; if (list_empty(&pcan_drv.devices)) { DPRINTK(KERN_DEBUG "%s: no devices to select from!\n", DEVICE_NAME); goto out; } // loop trough my devices for (ptr = pcan_drv.devices.next; ptr != &pcan_drv.devices; ptr = ptr->next) { dev = (struct pcandev *)ptr; if (dev->nMinor == minor) break; } // didn't find my minor err = -ENODEV; if (dev->nMinor != minor) { DPRINTK(KERN_DEBUG "%s: didn't find my minor!\n", DEVICE_NAME); goto out; } // create file object err = -ENOMEM; fobj = kmalloc(sizeof(struct fileobj), GFP_KERNEL); if (!fobj) { DPRINTK(KERN_DEBUG "%s: can't allocate kernel memory!\n", DEVICE_NAME); goto out; } // fill file object and init read and write method buffers fobj->dev = dev; if (filep->f_mode & FMODE_READ) { fobj->nReadRest = 0; fobj->nTotalReadCount = 0; fobj->pcReadPointer = fobj->pcReadBuffer; } if (filep->f_mode & FMODE_WRITE) { fobj->nWriteCount = 0; fobj->pcWritePointer = fobj->pcWriteBuffer; } filep->private_data = (void *)fobj; // only the first open to this device makes a default init on this device if (!dev->nOpenPaths) { // empty all FIFOs err = pcan_fifo_reset(&dev->writeFifo); if (err) goto out; err = pcan_fifo_reset(&dev->readFifo); if (err) goto out; // open the interface special parts err = dev->open(dev); if (err) { DPRINTK(KERN_DEBUG "%s: can't open interface special parts!\n", DEVICE_NAME); goto out; } // special handling: probe here only for dongle devices, connect after init is possible if ((dev->wType == HW_DONGLE_SJA) || (dev->wType == HW_DONGLE_SJA_EPP)) { err = sja1000_probe(dev); // no usb here, generic sja1000 call for dongle if (err) { printk(KERN_ERR "%s: %s-dongle device minor %d not found (io=0x%04x,irq=%d)\n", DEVICE_NAME, dev->type, dev->nMinor, dev->port.dng.dwPort, dev->port.dng.wIrq); dev->release(dev); goto out; } } // install irq err = dev->req_irq(dev); if (err) { DPRINTK(KERN_DEBUG "%s: can't request irq from device!\n", DEVICE_NAME); goto out; } // open the device itself err = dev->device_open(dev, dev->wBTR0BTR1, dev->ucCANMsgType, dev->ucListenOnly); if (err) { DPRINTK(KERN_DEBUG "%s: can't open device hardware itself!\n", DEVICE_NAME); goto out; } } dev->nOpenPaths++; MOD_INC_USE_COUNT; DPRINTK(KERN_DEBUG "%s: pcan_open() is OK\n", DEVICE_NAME); return 0; out: if (fobj) kfree(fobj); return err;}//----------------------------------------------------------------------------// is called when the path is closedstatic int pcan_release(struct inode *inode, struct file *filep){ struct fileobj *fobj = (struct fileobj *)filep->private_data; struct pcandev *dev; DPRINTK(KERN_DEBUG "%s: pcan_release()\n", DEVICE_NAME); // free the associated irq and allocated memory if (fobj && fobj->dev) { dev = fobj->dev; // if it's the last release: init the chip for non-intrusive operation if (dev->nOpenPaths > 1) dev->nOpenPaths--; else { // wait until fifo is empty or MAX_WAIT_UNTIL_CLOSE time is elapsed wait_until_fifo_empty(dev, MAX_WAIT_UNTIL_CLOSE); // release the device itself dev->device_release(dev); dev->release(dev); dev->nOpenPaths = 0; // release the interface depended irq, after this 'dev' is not valid dev->free_irq(fobj->dev); } kfree(fobj); } MOD_DEC_USE_COUNT; return 0;}//----------------------------------------------------------------------------// is called at user ioctl() with cmd = PCAN_READ_MSG static int pcan_ioctl_read(struct file *filep, struct pcandev *dev, TPCANRdMsg *usr){ int err = 0; TPCANRdMsg *m; DPRINTK(KERN_DEBUG "%s: pcan_ioctl(PCAN_READ_MSG)\n", DEVICE_NAME); // support nonblocking read if requested if ((filep->f_flags & O_NONBLOCK) && (!pcan_fifo_empty(&dev->readFifo))) return -EAGAIN; // sleep until data are available err = wait_event_interruptible(dev->read_queue, (pcan_fifo_empty(&dev->readFifo))); if (err) goto fail; // if the device is plugged out if (!dev->ucPhysicallyInstalled) return -ENODEV; // get new data out of fifo err = pcan_fifo_claim_for_get(&dev->readFifo, (void *)&m); if (err) goto fail; // signal a pending status if (dev->wCANStatus) m->Msg.MSGTYPE |= MSGTYPE_STATUS; if (copy_to_user(usr, m, sizeof(*m))) { err = pcan_fifo_get_reject(&dev->readFifo); err = -EFAULT; goto fail; } // free fifo err = pcan_fifo_get(&dev->readFifo); fail: return err;}//----------------------------------------------------------------------------// is called at user ioctl() with cmd = PCAN_WRITE_MSG static int pcan_ioctl_write(struct file *filep, struct pcandev *dev, TPCANMsg *usr){ int err = 0; TPCANMsg *m; DPRINTK(KERN_DEBUG "%s: pcan_ioctl(PCAN_WRITE_MSG)\n", DEVICE_NAME); // support nonblocking write if requested if ((filep->f_flags & O_NONBLOCK) && (!pcan_fifo_near_full(&dev->writeFifo)) && (!atomic_read(&dev->DataSendReady))) return -EAGAIN; // sleep until space is available err = wait_event_interruptible(dev->write_queue, (pcan_fifo_near_full(&dev->writeFifo) || atomic_read(&dev->DataSendReady))); if (err) goto fail; // if the device is plugged out if (!dev->ucPhysicallyInstalled) return -ENODEV; // put message into write FIFO err = pcan_fifo_claim_for_put(&dev->writeFifo, (void *)&m); if (err) goto fail; if (copy_from_user(m, usr, sizeof(*m))) { err = -EFAULT; pcan_fifo_put_reject(&dev->writeFifo); goto fail; } // filter extended data if initialized to standard only if (!(dev->bExtended) && ((m->MSGTYPE & MSGTYPE_EXTENDED) || (m->ID > 2047))) { err = -EINVAL; pcan_fifo_put_reject(&dev->writeFifo); goto fail; } // free fifo err = pcan_fifo_put(&dev->writeFifo); if (err) goto fail; // pull new transmission if (atomic_read(&dev->DataSendReady)) { atomic_set(&dev->DataSendReady, 0); if ((err = dev->device_write(dev))) { atomic_set(&dev->DataSendReady, 1); goto fail; } } else { // DPRINTK(KERN_DEBUG "%s: pushed %d item into Fifo\n", DEVICE_NAME, dev->writeFifo.nStored); } fail: return err;}//----------------------------------------------------------------------------// is called at user ioctl() with cmd = PCAN_GET_STATUS static int pcan_ioctl_status(struct pcandev *dev, TPSTATUS *status){ int err = 0; TPSTATUS local; DPRINTK(KERN_DEBUG "%s: pcan_ioctl(PCAN_GET_STATUS)\n", DEVICE_NAME); local.wErrorFlag = dev->wCANStatus; // get infos for friends of polling operation if (!pcan_fifo_empty(&dev->readFifo)) local.wErrorFlag |= CAN_ERR_QRCVEMPTY; if (!pcan_fifo_near_full(&dev->writeFifo)) local.wErrorFlag |= CAN_ERR_QXMTFULL; local.nLastError = dev->nLastError; if (copy_to_user(status, &local, sizeof(local))) { err = -EFAULT; goto fail; } dev->wCANStatus = 0; dev->nLastError = 0; fail: return err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -