From e0ce8a7265fec155f97438c80dedd7f01ff0055f Mon Sep 17 00:00:00 2001 From: Sebastian Haas Date: Thu, 14 May 2009 20:46:12 -0700 Subject: Staging: add cpc-usb driver to the staging tree This is a CPC CAN USB driver. Just some comments: cpcusb.h and cpc-usb_drv.c: Essential driver source code sja2m16c_2.c: Helper for converting bitrate timings cpc.h: Structures and definition needed to communicate with the device From: Sebastian Haas Signed-off-by: Greg Kroah-Hartman --- drivers/staging/cpc-usb/cpc-usb_drv.c | 1232 +++++++++++++++++++++++++++++++++ drivers/staging/cpc-usb/cpc.h | 440 ++++++++++++ drivers/staging/cpc-usb/cpc_int.h | 85 +++ drivers/staging/cpc-usb/cpcusb.h | 86 +++ drivers/staging/cpc-usb/sja2m16c.h | 41 ++ drivers/staging/cpc-usb/sja2m16c_2.c | 452 ++++++++++++ 6 files changed, 2336 insertions(+) create mode 100644 drivers/staging/cpc-usb/cpc-usb_drv.c create mode 100644 drivers/staging/cpc-usb/cpc.h create mode 100644 drivers/staging/cpc-usb/cpc_int.h create mode 100644 drivers/staging/cpc-usb/cpcusb.h create mode 100644 drivers/staging/cpc-usb/sja2m16c.h create mode 100644 drivers/staging/cpc-usb/sja2m16c_2.c (limited to 'drivers/staging') diff --git a/drivers/staging/cpc-usb/cpc-usb_drv.c b/drivers/staging/cpc-usb/cpc-usb_drv.c new file mode 100644 index 00000000000..a08c6655b86 --- /dev/null +++ b/drivers/staging/cpc-usb/cpc-usb_drv.c @@ -0,0 +1,1232 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* usb_kill_urb has been introduced in kernel version 2.6.8 (RC2) */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)) +#define usb_kill_urb usb_unlink_urb +#endif + +#ifdef CONFIG_PROC_FS +# include +#endif + +#include "cpc.h" + +#include "cpc_int.h" +#include "cpcusb.h" + +#include "sja2m16c.h" + +/* Version Information */ +#define DRIVER_AUTHOR "Sebastian Haas " +#define DRIVER_DESC "CPC-USB Driver for Linux Kernel 2.6" +#define DRIVER_VERSION CPC_DRIVER_VERSION " (CDKL v" CDKL_VERSION ")" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +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 + +#ifndef CONFIG_PROC_FS +#error "PROCFS needed" +#endif + +#define CPC_USB_PROC_DIR CPC_PROC_DIR "cpc-usb" + +static struct proc_dir_entry *procDir = NULL; +static struct proc_dir_entry *procEntry = NULL; + +/* Module parameters */ +static int debug = 0; +module_param(debug, int, S_IRUGO); + +/* table of devices that work with this driver */ +static struct usb_device_id cpcusb_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, cpcusb_table); + +/* use to prevent kernel panic if driver is unloaded + * while a programm has still open the device + */ +DECLARE_WAIT_QUEUE_HEAD(rmmodWq); +atomic_t useCount; + +static CPC_USB_T *CPCUSB_Table[CPC_USB_CARD_CNT] = { 0 }; +static unsigned int CPCUsbCnt = 0; + +/* prevent races between open() and disconnect() */ +static DECLARE_MUTEX(disconnect_sem); + +/* local function prototypes */ +static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count, + loff_t * ppos); +static ssize_t cpcusb_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos); +static unsigned int cpcusb_poll(struct file *file, poll_table * wait); +static int cpcusb_open(struct inode *inode, struct file *file); +static int cpcusb_release(struct inode *inode, struct file *file); + +static int cpcusb_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void cpcusb_disconnect(struct usb_interface *interface); + +static void cpcusb_read_bulk_callback(struct urb *urb +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif +); +static void cpcusb_write_bulk_callback(struct urb *urb +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif +); +static void cpcusb_read_interrupt_callback(struct urb *urb +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif +); + +static int cpcusb_setup_intrep(CPC_USB_T * card); + +static struct file_operations cpcusb_fops = { + /* + * The owner field is part of the module-locking + * mechanism. The idea is that the kernel knows + * which module to increment the use-counter of + * BEFORE it calls the device's open() function. + * This also means that the kernel can decrement + * the use-counter again before calling release() + * or should the open() function fail. + */ + .owner = THIS_MODULE, + + .read = cpcusb_read, + .write = cpcusb_write, + .poll = cpcusb_poll, + .open = cpcusb_open, + .release = cpcusb_release, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver cpcusb_class = { + .name = "usb/cpc_usb%d", + .fops = &cpcusb_fops, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)) + .mode = + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | + S_IWOTH, +#endif + .minor_base = CPC_USB_BASE_MNR, +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver cpcusb_driver = { +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,14)) + .owner = THIS_MODULE, +#endif + .name = "cpc-usb", + .probe = cpcusb_probe, + .disconnect = cpcusb_disconnect, + .id_table = cpcusb_table, +}; + +static int cpcusb_create_info_output(char *buf) +{ + int i = 0, j; + + for (j = 0; j < CPC_USB_CARD_CNT; j++) { + if (CPCUSB_Table[j]) { + CPC_USB_T *card = CPCUSB_Table[j]; + CPC_CHAN_T *chan = card->chan; + + /* MINOR CHANNELNO BUSNO SLOTNO */ + i += sprintf(&buf[i], "%d %s\n", chan->minor, + card->serialNumber); + } + } + + return i; +} + +static int cpcusb_proc_read_info(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpcusb_create_info_output(page); + + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +/* + * Remove CPC-USB and cleanup + */ +static inline void cpcusb_delete(CPC_USB_T * card) +{ + if (card) { + if (card->chan) { + if (card->chan->buf) + vfree(card->chan->buf); + + if (card->chan->CPCWait_q) + kfree(card->chan->CPCWait_q); + + kfree(card->chan); + } + + CPCUSB_Table[card->idx] = NULL; + kfree(card); + } +} + +/* + * setup the interrupt IN endpoint of a specific CPC-USB device + */ +static int cpcusb_setup_intrep(CPC_USB_T * card) +{ + int retval = 0; + struct usb_endpoint_descriptor *ep; + + ep = &card->interface->altsetting[0].endpoint[card->num_intr_in].desc; + + card->intr_in_buffer[0] = 0; + card->free_slots = 15; /* initial size */ + + /* setup the urb */ + usb_fill_int_urb(card->intr_in_urb, card->udev, + usb_rcvintpipe(card->udev, card->num_intr_in), + card->intr_in_buffer, + sizeof(card->intr_in_buffer), + cpcusb_read_interrupt_callback, + card, + ep->bInterval); + + card->intr_in_urb->status = 0; /* needed! */ + + /* submit the urb */ + retval = usb_submit_urb(card->intr_in_urb, GFP_KERNEL); + + if (retval) { + err("%s - failed submitting intr urb, error %d", __FUNCTION__, retval); + } + + return retval; +} + +static int cpcusb_open(struct inode *inode, struct file *file) +{ + CPC_USB_T *card = NULL; + struct usb_interface *interface; + int subminor; + int j, retval = 0; + + subminor = iminor(inode); + + /* prevent disconnects */ + down(&disconnect_sem); + + interface = usb_find_interface(&cpcusb_driver, subminor); + if (!interface) { + err("%s - error, can't find device for minor %d", + __FUNCTION__, subminor); + retval = CPC_ERR_NO_INTERFACE_PRESENT; + goto exit_no_device; + } + + card = usb_get_intfdata(interface); + if (!card) { + retval = CPC_ERR_NO_INTERFACE_PRESENT; + goto exit_no_device; + } + + /* lock this device */ + down(&card->sem); + + /* increment our usage count for the driver */ + if (card->open) { + dbg("device already opened"); + retval = CPC_ERR_CHANNEL_ALREADY_OPEN; + goto exit_on_error; + } + + /* save our object in the file's private structure */ + file->private_data = card; + for (j = 0; j < CPC_USB_URB_CNT; j++) { + usb_fill_bulk_urb(card->urbs[j].urb, card->udev, + usb_rcvbulkpipe(card->udev, card->num_bulk_in), + card->urbs[j].buffer, card->urbs[j].size, + cpcusb_read_bulk_callback, card); + + retval = usb_submit_urb(card->urbs[j].urb, GFP_KERNEL); + + if (retval) { + err("%s - failed submitting read urb, error %d", + __FUNCTION__, retval); + retval = CPC_ERR_TRANSMISSION_FAILED; + goto exit_on_error; + } + } + + info("%s - %d URB's submitted", __FUNCTION__, j); + + ResetBuffer(card->chan); + + cpcusb_setup_intrep(card); + card->open = 1; + + atomic_inc(&useCount); + +exit_on_error: + /* unlock this device */ + up(&card->sem); + +exit_no_device: + up(&disconnect_sem); + + return retval; +} + +static unsigned int cpcusb_poll(struct file *file, poll_table * wait) +{ + CPC_USB_T *card = (CPC_USB_T *) file->private_data; + unsigned int retval = 0; + + if (!card) { + err("%s - device object lost", __FUNCTION__); + return -EIO; + } + + poll_wait(file, card->chan->CPCWait_q, wait); + + if (IsBufferNotEmpty(card->chan) || !(card->present)) + retval |= (POLLIN | POLLRDNORM); + + if (card->free_slots) + retval |= (POLLOUT | POLLWRNORM); + + return retval; +} + +static int cpcusb_release(struct inode *inode, struct file *file) +{ + CPC_USB_T *card = (CPC_USB_T *) file->private_data; + int j, retval = 0; + + if (card == NULL) { + dbg("%s - object is NULL", __FUNCTION__); + return CPC_ERR_NO_INTERFACE_PRESENT; + } + + /* lock our device */ + down(&card->sem); + + if (!card->open) { + dbg("%s - device not opened", __FUNCTION__); + retval = CPC_ERR_NO_INTERFACE_PRESENT; + goto exit_not_opened; + } + + /* if device wasn't unplugged kill all urbs */ + if (card->present) { + /* kill read urbs */ + for (j = 0; j < CPC_USB_URB_CNT; j++) { + usb_kill_urb(card->urbs[j].urb); + } + + /* kill irq urb */ + usb_kill_urb(card->intr_in_urb); + + /* kill write urbs */ + for (j = 0; j < CPC_USB_URB_CNT; j++) { + if (atomic_read(&card->wrUrbs[j].busy)) { + usb_kill_urb(card->wrUrbs[j].urb); + wait_for_completion(&card->wrUrbs[j].finished); + } + } + } + + atomic_dec(&useCount); + + /* last process detached */ + if (atomic_read(&useCount) == 0) { + wake_up(&rmmodWq); + } + + if (!card->present && card->open) { + /* the device was unplugged before the file was released */ + up(&card->sem); + cpcusb_delete(card); + return 0; + } + + card->open = 0; + +exit_not_opened: + up(&card->sem); + + return 0; +} + +static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count, + loff_t * ppos) +{ + CPC_USB_T *card = (CPC_USB_T *) file->private_data; + CPC_CHAN_T *chan; + int retval = 0; + + if (count < sizeof(CPC_MSG_T)) + return CPC_ERR_UNKNOWN; + + /* check if can read from the given address */ + if (!access_ok(VERIFY_WRITE, buffer, count)) + return CPC_ERR_UNKNOWN; + + /* lock this object */ + down(&card->sem); + + /* verify that the device wasn't unplugged */ + if (!card->present) { + up(&card->sem); + return CPC_ERR_NO_INTERFACE_PRESENT; + } + + if (IsBufferEmpty(card->chan)) { + retval = 0; + } else { + chan = card->chan; + +#if 0 + /* convert LPC2119 params back to SJA1000 params */ + if (card->deviceRevision >= 0x0200 + && chan->buf[chan->oidx].type == CPC_MSG_T_CAN_PRMS) { + LPC2119_TO_SJA1000_Params(&chan->buf[chan->oidx]); + } +#endif + + if (copy_to_user(buffer, &chan->buf[chan->oidx], count) != 0) { + retval = CPC_ERR_IO_TRANSFER; + } else { + chan->oidx = (chan->oidx + 1) % CPC_MSG_BUF_CNT; + chan->WnR = 1; + retval = sizeof(CPC_MSG_T); + } + } +// spin_unlock_irqrestore(&card->slock, flags); + + /* unlock the device */ + up(&card->sem); + + return retval; +} + +#define SHIFT 1 +static void inline cpcusb_align_buffer_alignment(unsigned char *buf) +{ + // CPC-USB uploads packed bytes. + CPC_MSG_T *cpc = (CPC_MSG_T *) buf; + unsigned int i; + + for (i = 0; i < cpc->length + (2 * sizeof(unsigned long)); i++) { + ((unsigned char *) &cpc->msgid)[1 + i] = + ((unsigned char *) &cpc->msgid)[1 + SHIFT + i]; + } +} + +static int cpc_get_buffer_count(CPC_CHAN_T * chan) +{ + // check the buffer parameters + if (chan->iidx == chan->oidx) + return !chan->WnR ? CPC_MSG_BUF_CNT : 0; + else if (chan->iidx >= chan->oidx) + return (chan->iidx - chan->oidx) % CPC_MSG_BUF_CNT; + + return (chan->iidx + CPC_MSG_BUF_CNT - chan->oidx) % CPC_MSG_BUF_CNT; +} + +static ssize_t cpcusb_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + CPC_USB_T *card = (CPC_USB_T *) file->private_data; + CPC_USB_WRITE_URB_T *wrUrb = NULL; + + ssize_t bytes_written = 0; + int retval = 0; + int j; + + unsigned char *obuf = NULL; + unsigned char type = 0; + CPC_MSG_T *info = NULL; + + dbg("%s - entered minor %d, count = %d, present = %d", + __FUNCTION__, card->minor, count, card->present); + + if (count > sizeof(CPC_MSG_T)) + return CPC_ERR_UNKNOWN; + + /* check if can read from the given address */ + if (!access_ok(VERIFY_READ, buffer, count)) + return CPC_ERR_UNKNOWN; + + /* lock this object */ + down(&card->sem); + + /* verify that the device wasn't unplugged */ + if (!card->present) { + retval = CPC_ERR_NO_INTERFACE_PRESENT; + goto exit; + } + + /* verify that we actually have some data to write */ + if (count == 0) { + dbg("%s - write request of 0 bytes", __FUNCTION__); + goto exit; + } + + if (card->free_slots <= 5) { + info = (CPC_MSG_T *) buffer; + + if (info->type != CPC_CMD_T_CLEAR_CMD_QUEUE + || card->free_slots <= 0) { + dbg("%s - send buffer full please try again %d", + __FUNCTION__, card->free_slots); + retval = CPC_ERR_CAN_NO_TRANSMIT_BUF; + goto exit; + } + } + + /* Find a free write urb */ + for (j = 0; j < CPC_USB_URB_CNT; j++) { + if (!atomic_read(&card->wrUrbs[j].busy)) { + wrUrb = &card->wrUrbs[j]; /* remember found URB */ + atomic_set(&wrUrb->busy, 1); /* lock this URB */ + init_completion(&wrUrb->finished); /* init completion */ + dbg("WR URB no. %d started", j); + break; + } + } + + /* don't found write urb say error */ + if (!wrUrb) { + dbg("%s - no free send urb available", __FUNCTION__); + retval = CPC_ERR_CAN_NO_TRANSMIT_BUF; + goto exit; + } + dbg("URB write req"); + + obuf = (unsigned char *) wrUrb->urb->transfer_buffer; + + /* copy the data from userspace into our transfer buffer; + * this is the only copy required. + */ + if (copy_from_user(&obuf[4], buffer, count) != 0) { + atomic_set(&wrUrb->busy, 0); /* release urb */ + retval = CPC_ERR_IO_TRANSFER; + goto exit; + } + + /* check if it is a DRIVER information message, so we can + * response to that message and not the USB + */ + info = (CPC_MSG_T *) & obuf[4]; + + bytes_written = 11 + info->length; + if (bytes_written >= wrUrb->size) { + retval = CPC_ERR_IO_TRANSFER; + goto exit; + } + + switch (info->type) { + case CPC_CMD_T_CLEAR_MSG_QUEUE: + ResetBuffer(card->chan); + break; + + case CPC_CMD_T_INQ_MSG_QUEUE_CNT: + retval = cpc_get_buffer_count(card->chan); + atomic_set(&wrUrb->busy, 0); + + goto exit; + + case CPC_CMD_T_INQ_INFO: + if (info->msg.info.source == CPC_INFOMSG_T_DRIVER) { + /* release urb cause we'll use it for driver + * information + */ + atomic_set(&wrUrb->busy, 0); + if (IsBufferFull(card->chan)) { + retval = CPC_ERR_IO_TRANSFER; + goto exit; + } + + /* it is a driver information request message and we have + * free rx slots to store the response + */ + type = info->msg.info.type; + info = &card->chan->buf[card->chan->iidx]; + + info->type = CPC_MSG_T_INFO; + info->msg.info.source = CPC_INFOMSG_T_DRIVER; + info->msg.info.type = type; + + switch (type) { + case CPC_INFOMSG_T_VERSION: + info->length = strlen(CPC_DRIVER_VERSION) + 2; + sprintf(info->msg.info.msg, "%s\n", + CPC_DRIVER_VERSION); + break; + + case CPC_INFOMSG_T_SERIAL: + info->length = strlen(CPC_DRIVER_SERIAL) + 2; + sprintf(info->msg.info.msg, "%s\n", + CPC_DRIVER_SERIAL); + break; + + default: + info->length = 2; + info->msg.info.type = + CPC_INFOMSG_T_UNKNOWN_TYPE; + } + + card->chan->WnR = 0; + card->chan->iidx = + (card->chan->iidx + 1) % CPC_MSG_BUF_CNT; + + retval = info->length; + goto exit; + } + break; + case CPC_CMD_T_CAN_PRMS: + /* Check the controller type. If it's the new CPC-USB, make sure if these are SJA1000 params */ + if (info->msg.canparams.cc_type != SJA1000 + && info->msg.canparams.cc_type != M16C_BASIC + && (card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID + && info->msg.canparams.cc_type != SJA1000)) { + /* don't forget to release the urb */ + atomic_set(&wrUrb->busy, 0); + retval = CPC_ERR_WRONG_CONTROLLER_TYPE; + goto exit; + } + break; + } + + /* just convert the params if it is an old CPC-USB with M16C controller */ + if (card->productId == USB_CPCUSB_M16C_PRODUCT_ID) { + /* if it is a parameter message convert it from SJA1000 controller + * settings to M16C Basic controller settings + */ + SJA1000_TO_M16C_BASIC_Params((CPC_MSG_T *) & obuf[4]); + } + + /* don't forget the byte alignment */ + cpcusb_align_buffer_alignment(&obuf[4]); + + /* setup a the 4 byte header */ + obuf[0] = obuf[1] = obuf[2] = obuf[3] = 0; + + /* this urb was already set up, except for this write size */ + wrUrb->urb->transfer_buffer_length = bytes_written + 4; + + /* send the data out the bulk port */ + /* a character device write uses GFP_KERNEL, + unless a spinlock is held */ + retval = usb_submit_urb(wrUrb->urb, GFP_KERNEL); + if (retval) { + atomic_set(&wrUrb->busy, 0); /* release urb */ + err("%s - failed submitting write urb, error %d", + __FUNCTION__, retval); + } else { + retval = bytes_written; + } + +exit: + /* unlock the device */ + up(&card->sem); + + dbg("%s - leaved", __FUNCTION__); + + return retval; +} + +/* + * callback for interrupt IN urb + */ +static void cpcusb_read_interrupt_callback(struct urb *urb +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif +) +{ + CPC_USB_T *card = (CPC_USB_T *) urb->context; + int retval; + unsigned long flags; + + spin_lock_irqsave(&card->slock, flags); + + if (!card->present) { + spin_unlock_irqrestore(&card->slock, flags); + info("%s - no such device", __FUNCTION__); + return; + } + + switch (urb->status) { + case 0: /* success */ + card->free_slots = card->intr_in_buffer[1]; + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* urb was killed */ + spin_unlock_irqrestore(&card->slock, flags); + dbg("%s - intr urb killed", __FUNCTION__); + return; + default: + info("%s - nonzero urb status %d", __FUNCTION__, urb->status); + break; + } + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) { + err("%s - failed resubmitting intr urb, error %d", + __FUNCTION__, retval); + } + + spin_unlock_irqrestore(&card->slock, flags); + wake_up_interruptible(card->chan->CPCWait_q); + + return; +} + +#define UN_SHIFT 1 +#define CPCMSG_HEADER_LEN_FIRMWARE 11 +static int inline cpcusb_unalign_and_copy_buffy(unsigned char *out, + unsigned char *in) +{ + unsigned int i, j; + + for (i = 0; i < 3; i++) { + out[i] = in[i]; + } + + for (j = 0; j < (in[1] + (CPCMSG_HEADER_LEN_FIRMWARE - 3)); j++) { + out[j + i + UN_SHIFT] = in[j + i]; + } + + return i + j; +} + +/* + * callback for bulk IN urb + */ +static void cpcusb_read_bulk_callback(struct urb *urb +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif +) +{ + CPC_USB_T *card = (CPC_USB_T *) urb->context; + CPC_CHAN_T *chan; + unsigned char *ibuf = urb->transfer_buffer; + int retval, msgCnt, start, again = 0; + unsigned long flags; + + if (!card) { + err("%s - device object lost", __FUNCTION__); + return; + } + + spin_lock_irqsave(&card->slock, flags); + + if (!card->present) { + spin_unlock_irqrestore(&card->slock, flags); + info("%s - no such device", __FUNCTION__); + return; + } + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* urb was killed */ + spin_unlock_irqrestore(&card->slock, flags); + dbg("%s - read urb killed", __FUNCTION__); + return; + default: + info("%s - nonzero urb status %d", __FUNCTION__, urb->status); + break; + } + + if (urb->actual_length) { + msgCnt = ibuf[0] & ~0x80; + again = ibuf[0] & 0x80; + + /* we have a 4 byte header */ + start = 4; + chan = card->chan; + while (msgCnt) { + if (!(IsBufferFull(card->chan))) { + start += + cpcusb_unalign_and_copy_buffy((unsigned char *) + &chan->buf[chan->iidx], &ibuf[start]); + + if (start > urb->transfer_buffer_length) { + err("%d > %d", start, urb->transfer_buffer_length); + break; + } + + chan->WnR = 0; + chan->iidx = (chan->iidx + 1) % CPC_MSG_BUF_CNT; + msgCnt--; + } else { + break; + } + } + } + + usb_fill_bulk_urb(urb, card->udev, + usb_rcvbulkpipe(card->udev, card->num_bulk_in), + urb->transfer_buffer, + urb->transfer_buffer_length, + cpcusb_read_bulk_callback, card); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval) { + err("%s - failed resubmitting read urb, error %d", __FUNCTION__, retval); + } + + spin_unlock_irqrestore(&card->slock, flags); + + wake_up_interruptible(card->chan->CPCWait_q); +} + +/* + * callback for bulk IN urb + */ +static void cpcusb_write_bulk_callback(struct urb *urb +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) + , struct pt_regs *regs +#endif +) +{ + CPC_USB_T *card = (CPC_USB_T *) urb->context; + unsigned long flags; + int j; + + spin_lock_irqsave(&card->slock, flags); + + /* find this urb */ + for (j = 0; j < CPC_USB_URB_CNT; j++) { + if (card->wrUrbs[j].urb == urb) { + dbg("URB found no. %d", j); + /* notify anyone waiting that the write has finished */ + complete(&card->wrUrbs[j].finished); + atomic_set(&card->wrUrbs[j].busy, 0); + break; + } + } + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* urb was killed */ + spin_unlock_irqrestore(&card->slock, flags); + dbg("%s - write urb no. %d killed", __FUNCTION__, j); + return; + default: + info("%s - nonzero urb status %d", __FUNCTION__, urb->status); + break; + } + + spin_unlock_irqrestore(&card->slock, flags); + + wake_up_interruptible(card->chan->CPCWait_q); +} + +static inline int cpcusb_get_free_slot(void) +{ + int i; + + for (i = 0; i < CPC_USB_CARD_CNT; i++) { + if (!CPCUSB_Table[i]) + return i; + } + + return -1; +} + +/* + * probe function for new CPC-USB devices + */ +static int cpcusb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + CPC_USB_T *card = NULL; + CPC_CHAN_T *chan = NULL; + + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + + int i, j, retval = -ENOMEM, slot; + + if ((slot = cpcusb_get_free_slot()) < 0) { + info("No more devices supported"); + return -ENOMEM; + } + + /* allocate memory for our device state and initialize it */ + card = kzalloc(sizeof(CPC_USB_T), GFP_KERNEL); + if (!card) { + err("Out of memory"); + return -ENOMEM; + } + CPCUSB_Table[slot] = card; + + /* allocate and initialize the channel struct */ + card->chan = kmalloc(sizeof(CPC_CHAN_T), GFP_KERNEL); + if (!card) { + kfree(card); + err("Out of memory"); + return -ENOMEM; + } + + chan = card->chan; + memset(chan, 0, sizeof(CPC_CHAN_T)); + ResetBuffer(chan); + + init_MUTEX(&card->sem); + spin_lock_init(&card->slock); + + card->udev = udev; + card->interface = interface; + if (udev->descriptor.iSerialNumber) { + usb_string(udev, udev->descriptor.iSerialNumber, card->serialNumber, + 128); + info("Serial %s", card->serialNumber); + } + + card->productId = udev->descriptor.idProduct; + info("Product %s", + card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID ? + "CPC-USB/ARM7" : "CPC-USB/M16C"); + + /* set up the endpoint information */ + /* check out the endpoints */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = &interface->altsetting[0]; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!card->num_intr_in && + (endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT)) { + card->intr_in_urb = usb_alloc_urb(0, GFP_KERNEL); + card->num_intr_in = 1; + + if (!card->intr_in_urb) { + err("No free urbs available"); + goto error; + } + + dbg("intr_in urb %d", card->num_intr_in); + } + + if (!card->num_bulk_in && + (endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + card->num_bulk_in = 2; + for (j = 0; j < CPC_USB_URB_CNT; j++) { + card->urbs[j].size = endpoint->wMaxPacketSize; + card->urbs[j].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!card->urbs[j].urb) { + err("No free urbs available"); + goto error; + } + card->urbs[j].buffer = + usb_buffer_alloc(udev, + card->urbs[j].size, + GFP_KERNEL, + &card->urbs[j].urb->transfer_dma); + if (!card->urbs[j].buffer) { + err("Couldn't allocate bulk_in_buffer"); + goto error; + } + } + info("%s - %d reading URB's allocated", + __FUNCTION__, CPC_USB_URB_CNT); + } + + if (!card->num_bulk_out && + !(endpoint->bEndpointAddress & USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + + card->num_bulk_out = 2; + + for (j = 0; j < CPC_USB_URB_CNT; j++) { + card->wrUrbs[j].size = + endpoint->wMaxPacketSize; + card->wrUrbs[j].urb = + usb_alloc_urb(0, GFP_KERNEL); + if (!card->wrUrbs[j].urb) { + err("No free urbs available"); + goto error; + } + card->wrUrbs[j].buffer = usb_buffer_alloc(udev, + card->wrUrbs[j].size, GFP_KERNEL, + &card->wrUrbs[j].urb->transfer_dma); + + if (!card->wrUrbs[j].buffer) { + err("Couldn't allocate bulk_out_buffer"); + goto error; + } + + usb_fill_bulk_urb(card->wrUrbs[j].urb, udev, + usb_sndbulkpipe(udev, endpoint->bEndpointAddress), + card->wrUrbs[j].buffer, + card->wrUrbs[j].size, + cpcusb_write_bulk_callback, + card); + } + + info("%s - %d writing URB's allocated", __FUNCTION__, CPC_USB_URB_CNT); + } + } + + if (!(card->num_bulk_in && card->num_bulk_out)) { + err("Couldn't find both bulk-in and bulk-out endpoints"); + goto error; + } + + /* allow device read, write and ioctl */ + card->present = 1; + + /* we can register the device now, as it is ready */ + usb_set_intfdata(interface, card); + retval = usb_register_dev(interface, &cpcusb_class); + + if (retval) { + /* something prevented us from registering this driver */ + err("Not able to get a minor for this device."); + usb_set_intfdata(interface, NULL); + goto error; + } + + card->chan->minor = card->minor = interface->minor; + + chan->buf = vmalloc(sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT); + if (chan->buf == NULL) { + err("Out of memory"); + retval = -ENOMEM; + goto error; + } + info("Allocated memory for %d messages (%d kbytes)", + CPC_MSG_BUF_CNT, (sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT) / 1000); + memset(chan->buf, 0, sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT); + + ResetBuffer(chan); + + card->chan->CPCWait_q = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); + if (!card->chan->CPCWait_q) { + err("Out of memory"); + retval = -ENOMEM; + goto error; + } + init_waitqueue_head(card->chan->CPCWait_q); + + CPCUSB_Table[slot] = card; + card->idx = slot; + CPCUsbCnt++; + + /* let the user know what node this device is now attached to */ + info("Device now attached to USB-%d", card->minor); + return 0; + +error: + for (j = 0; j < CPC_USB_URB_CNT; j++) { + if (card->urbs[j].buffer) { + usb_buffer_free(card->udev, card->urbs[j].size, + card->urbs[j].buffer, + card->urbs[j].urb->transfer_dma); + card->urbs[j].buffer = NULL; + } + if (card->urbs[j].urb) { + usb_free_urb(card->urbs[j].urb); + card->urbs[j].urb = NULL; + } + } + + cpcusb_delete(card); + return retval; +} + +/* + * called by the usb core when the device is removed from the system + */ +static void cpcusb_disconnect(struct usb_interface *interface) +{ + CPC_USB_T *card = NULL; + int minor, j; + + /* prevent races with open() */ + down(&disconnect_sem); + + card = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + down(&card->sem); + + /* prevent device read, write and ioctl */ + card->present = 0; + + minor = card->minor; + + /* free all urbs and their buffers */ + for (j = 0; j < CPC_USB_URB_CNT; j++) { + /* terminate an ongoing write */ + if (atomic_read(&card->wrUrbs[j].busy)) { + usb_kill_urb(card->wrUrbs[j].urb); + wait_for_completion(&card->wrUrbs[j].finished); + } + usb_buffer_free(card->udev, card->wrUrbs[j].size, + card->wrUrbs[j].buffer, + card->wrUrbs[j].urb->transfer_dma); + usb_free_urb(card->wrUrbs[j].urb); + } + info("%d write URBs freed", CPC_USB_URB_CNT); + + /* free all urbs and their buffers */ + for (j = 0; j < CPC_USB_URB_CNT; j++) { + usb_buffer_free(card->udev, card->urbs[j].size, + card->urbs[j].buffer, + card->urbs[j].urb->transfer_dma); + usb_free_urb(card->urbs[j].urb); + } + info("%d read URBs freed", CPC_USB_URB_CNT); + usb_free_urb(card->intr_in_urb); + + /* give back our minor */ + usb_deregister_dev(interface, &cpcusb_class); + + up(&card->sem); + + /* if the device is opened, cpcusb_release will clean this up */ + if (!card->open) + cpcusb_delete(card); + else + wake_up_interruptible(card->chan->CPCWait_q); + + up(&disconnect_sem); + + CPCUsbCnt--; + info("USB-%d now disconnected", minor); +} + +static int __init CPCUsb_Init(void) +{ + int result, i; + + info(DRIVER_DESC " v" DRIVER_VERSION); + info("Build on " __DATE__ " at " __TIME__); + + for (i = 0; i < CPC_USB_CARD_CNT; i++) + CPCUSB_Table[i] = 0; + + /* register this driver with the USB subsystem */ + result = usb_register(&cpcusb_driver); + if (result) { + err("usb_register failed. Error number %d", result); + return result; + } + + procDir = proc_mkdir(CPC_USB_PROC_DIR, NULL); + if (!procDir) { + err("Could not create proc entry"); + } else { + procDir->owner = THIS_MODULE; + procEntry = create_proc_read_entry("info", 0444, procDir, + cpcusb_proc_read_info, + NULL); + if (!procEntry) { + err("Could not create proc entry %s", CPC_USB_PROC_DIR "/info"); + remove_proc_entry(CPC_USB_PROC_DIR, NULL); + procDir = NULL; + } else { + procEntry->owner = THIS_MODULE; + } + } + + return 0; +} + +static void __exit CPCUsb_Exit(void) +{ + wait_event(rmmodWq, !atomic_read(&useCount)); + + /* deregister this driver with the USB subsystem */ + usb_deregister(&cpcusb_driver); + + if (procDir) { + if (procEntry) + remove_proc_entry("info", procDir); + remove_proc_entry(CPC_USB_PROC_DIR, NULL); + } +} + +module_init(CPCUsb_Init); +module_exit(CPCUsb_Exit); diff --git a/drivers/staging/cpc-usb/cpc.h b/drivers/staging/cpc-usb/cpc.h new file mode 100644 index 00000000000..ed8cb34d476 --- /dev/null +++ b/drivers/staging/cpc-usb/cpc.h @@ -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 diff --git a/drivers/staging/cpc-usb/cpc_int.h b/drivers/staging/cpc-usb/cpc_int.h new file mode 100644 index 00000000000..dfdbed1c1f9 --- /dev/null +++ b/drivers/staging/cpc-usb/cpc_int.h @@ -0,0 +1,85 @@ +/* + * CPCLIB + * + * 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_INT_H +#define CPC_INT_H + +#include + +#define CPC_MSG_BUF_CNT 1500 + +#ifdef CONFIG_PROC_FS +# define CPC_PROC_DIR "driver/" +#endif + +#undef dbg +#undef err +#undef info + +/* Use our own dbg macro */ +#define dbg(format, arg...) do { if (debug) printk( KERN_INFO format "\n" , ## arg); } while (0) +#define err(format, arg...) do { printk( KERN_INFO "ERROR " format "\n" , ## arg); } while (0) +#define info(format, arg...) do { printk( KERN_INFO format "\n" , ## arg); } while (0) + +/* Macros help using of our buffers */ +#define IsBufferFull(x) (!(x)->WnR) && ((x)->iidx == (x)->oidx) +#define IsBufferEmpty(x) ((x)->WnR) && ((x)->iidx == (x)->oidx) +#define IsBufferNotEmpty(x) (!(x)->WnR) || ((x)->iidx != (x)->oidx) +#define ResetBuffer(x) do { (x)->oidx = (x)->iidx=0; (x)->WnR = 1; } while(0); + +#define CPC_BufWriteAllowed ((chan->oidx != chan->iidx) || chan->WnR) + +typedef void (*chan_write_byte_t) (void *chan, unsigned int reg, + unsigned char val); +typedef unsigned char (*chan_read_byte_t) (void *chan, unsigned int reg); + +typedef struct CPC_CHAN { + void __iomem * canBase; // base address of SJA1000 + chan_read_byte_t read_byte; // CAN controller read access routine + chan_write_byte_t write_byte; // CAN controller write access routine + CPC_MSG_T *buf; // buffer for CPC msg + unsigned int iidx; + unsigned int oidx; + unsigned int WnR; + unsigned int minor; + unsigned int locked; + unsigned int irqDisabled; + + unsigned char cpcCtrlCANMessage; + unsigned char cpcCtrlCANState; + unsigned char cpcCtrlBUSState; + + unsigned char controllerType; + + unsigned long ovrTimeSec; + unsigned long ovrTimeNSec; + unsigned long ovrLockedBuffer; + CPC_OVERRUN_T ovr; + + /* for debugging only */ + unsigned int handledIrqs; + unsigned int lostMessages; + + unsigned int sentStdCan; + unsigned int sentExtCan; + unsigned int sentStdRtr; + unsigned int sentExtRtr; + + unsigned int recvStdCan; + unsigned int recvExtCan; + unsigned int recvStdRtr; + unsigned int recvExtRtr; + + wait_queue_head_t *CPCWait_q; + + void *private; +} CPC_CHAN_T; + +#endif diff --git a/drivers/staging/cpc-usb/cpcusb.h b/drivers/staging/cpc-usb/cpcusb.h new file mode 100644 index 00000000000..e5273ddd9e0 --- /dev/null +++ b/drivers/staging/cpc-usb/cpcusb.h @@ -0,0 +1,86 @@ +/* Header for CPC-USB Driver ******************** + * Copyright 1999, 2000, 2001 + * + * Company: EMS Dr. Thomas Wuensche + * Sonnenhang 3 + * 85304 Ilmmuenster + * Phone: +49-8441-490260 + * Fax: +49-8441-81860 + * email: support@ems-wuensche.com + * WWW: www.ems-wuensche.com + */ + +#ifndef CPCUSB_H +#define CPCUSB_H + +#undef err +#undef dbg +#undef info + +/* Use our own dbg macro */ +#define dbg(format, arg...) do { if (debug) printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0) +#define info(format, arg...) do { printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0) +#define err(format, arg...) do { printk(KERN_INFO "CPC-USB(ERROR): " format "\n" , ## arg); } while (0) + +#define CPC_USB_CARD_CNT 4 + +typedef struct CPC_USB_READ_URB { + unsigned char *buffer; /* the buffer to send data */ + size_t size; /* the size of the send buffer */ + struct urb *urb; /* the urb used to send data */ +} CPC_USB_READ_URB_T; + +typedef struct CPC_USB_WRITE_URB { + unsigned char *buffer; /* the buffer to send data */ + size_t size; /* the size of the send buffer */ + struct urb *urb; /* the urb used to send data */ + atomic_t busy; /* true if write urb is busy */ + struct completion finished; /* wait for the write to finish */ +} CPC_USB_WRITE_URB_T; + +#define CPC_USB_URB_CNT 10 + +typedef struct CPC_USB { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface; /* the interface for this device */ + unsigned char minor; /* the starting minor number for this device */ + unsigned char num_ports; /* the number of ports this device has */ + int num_intr_in; /* number of interrupt in endpoints we have */ + int num_bulk_in; /* number of bulk in endpoints we have */ + int num_bulk_out; /* number of bulk out endpoints we have */ + + CPC_USB_READ_URB_T urbs[CPC_USB_URB_CNT]; + + unsigned char intr_in_buffer[4]; /* interrupt transfer buffer */ + struct urb *intr_in_urb; /* interrupt transfer urb */ + + CPC_USB_WRITE_URB_T wrUrbs[CPC_USB_URB_CNT]; + + int open; /* if the port is open or not */ + int present; /* if the device is not disconnected */ + struct semaphore sem; /* locks this structure */ + + int free_slots; /* free send slots of CPC-USB */ + int idx; + + spinlock_t slock; + + char serialNumber[128]; /* serial number */ + int productId; /* product id to differ between M16C and LPC2119 */ + CPC_CHAN_T *chan; +} CPC_USB_T; + +#define CPCTable CPCUSB_Table + +#define CPC_DRIVER_VERSION "0.724" +#define CPC_DRIVER_SERIAL "not applicable" + +#define OBUF_SIZE 255 // 4096 + +/* read timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */ +#define RD_NAK_TIMEOUT (10*HZ) /* Default number of X seconds to wait */ +#define RD_EXPIRE 12 /* Number of attempts to wait X seconds */ + +#define CPC_USB_BASE_MNR 0 /* CPC-USB start at minor 0 */ + +#endif diff --git a/drivers/staging/cpc-usb/sja2m16c.h b/drivers/staging/cpc-usb/sja2m16c.h new file mode 100644 index 00000000000..bd453e27055 --- /dev/null +++ b/drivers/staging/cpc-usb/sja2m16c.h @@ -0,0 +1,41 @@ +#ifndef SJA2M16C_H +#define SJA2M16C_H + +#include "cpc.h" + +#define BAUDRATE_TOLERANCE_PERCENT 1 +#define SAMPLEPOINT_TOLERANCE_PERCENT 5 +#define SAMPLEPOINT_UPPER_LIMIT 88 + +// M16C parameters + typedef struct FIELD_C0CONR { + unsigned int brp:4; + unsigned int sam:1; + unsigned int pr:3; + unsigned int dummy:8; +} FIELD_C0CONR_T; +typedef struct FIELD_C1CONR { + unsigned int ph1:3; + unsigned int ph2:3; + unsigned int sjw:2; + unsigned int dummy:8; +} FIELD_C1CONR_T; +typedef union C0CONR { + unsigned char c0con; + FIELD_C0CONR_T bc0con; +} C0CONR_T; +typedef union C1CONR { + unsigned char c1con; + FIELD_C1CONR_T bc1con; +} C1CONR_T; + +#define SJA_TSEG1 ((pParams->btr1 & 0x0f)+1) +#define SJA_TSEG2 (((pParams->btr1 & 0x70)>>4)+1) +#define SJA_BRP ((pParams->btr0 & 0x3f)+1) +#define SJA_SJW ((pParams->btr0 & 0xc0)>>6) +#define SJA_SAM ((pParams->btr1 & 0x80)>>7) +int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2); +int samplepoint_m16c(int brp, int pr, int ph1, int ph2); +int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T * pMsg); + +#endif /* */ diff --git a/drivers/staging/cpc-usb/sja2m16c_2.c b/drivers/staging/cpc-usb/sja2m16c_2.c new file mode 100644 index 00000000000..bf0230fb778 --- /dev/null +++ b/drivers/staging/cpc-usb/sja2m16c_2.c @@ -0,0 +1,452 @@ +/**************************************************************************** +* +* Copyright (c) 2003,2004 by EMS Dr. Thomas Wuensche +* +* - All rights reserved - +* +* This code is provided "as is" without warranty of any kind, either +* expressed or implied, including but not limited to the liability +* concerning the freedom from material defects, the fitness for parti- +* cular purposes or the freedom of proprietary rights of third parties. +* +***************************************************************************** +* Module name.: cpcusb +***************************************************************************** +* Include file: cpc.h +***************************************************************************** +* Project.....: Windows Driver Development Kit +* Filename....: sja2m16c.cpp +* Authors.....: (GU) Gerhard Uttenthaler +* (CS) Christian Schoett +***************************************************************************** +* Short descr.: converts baudrate between SJA1000 and M16C +***************************************************************************** +* Description.: handles the baudrate conversion from SJA1000 parameters to +* M16C parameters +***************************************************************************** +* Address : EMS Dr. Thomas Wuensche +* Sonnenhang 3 +* D-85304 Ilmmuenster +* Tel. : +49-8441-490260 +* Fax. : +49-8441-81860 +* email: support@ems-wuensche.com +***************************************************************************** +* History +***************************************************************************** +* Version Date Auth Remark +* +* 01.00 ?? GU - initial release +* 01.10 ?????????? CS - adapted to fit into the USB Windows driver +* 02.00 18.08.2004 GU - improved the baudrate calculating algorithm +* - implemented acceptance filtering +* 02.10 10.09.2004 CS - adapted to fit into the USB Windows driver +***************************************************************************** +* ToDo's +***************************************************************************** +*/ + +/****************************************************************************/ +/* I N C L U D E S +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpc.h" +#include "cpc_int.h" +#include "cpcusb.h" + +#include "sja2m16c.h" + +/*********************************************************************/ +int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2) +{ + return (16000000 / (1 << clk)) / 2 / (brp + 1) / (1 + pr + 1 + + ph1 + 1 + ph2 + + 1); +} + + +/*********************************************************************/ +int samplepoint_m16c(int brp, int pr, int ph1, int ph2) +{ + return (100 * (1 + pr + 1 + ph1 + 1)) / (1 + pr + 1 + ph1 + 1 + + ph2 + 1); +} + + +/**************************************************************************** +* Function.....: SJA1000_TO_M16C_BASIC_Params +* +* Task.........: This routine converts SJA1000 CAN btr parameters into M16C +* parameters based on the sample point and the error. In +* addition it converts the acceptance filter parameters to +* suit the M16C parameters +* +* Parameters...: None +* +* Return values: None +* +* Comments.....: +***************************************************************************** +* History +***************************************************************************** +* 19.01.2005 CS - modifed the conversion of SJA1000 filter params into +* M16C params. Due to compatibility reasons with the +* older 82C200 CAN controller the SJA1000 +****************************************************************************/ +int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T * in) +{ + int sjaBaudrate; + int sjaSamplepoint; + int *baudrate_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7] + int *samplepoint_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7] + int baudrate_error_merk; + int clk, brp, pr, ph1, ph2; + int clk_merk, brp_merk, pr_merk, ph1_merk, ph2_merk; + int index; + unsigned char acc_code0, acc_code1, acc_code2, acc_code3; + unsigned char acc_mask0, acc_mask1, acc_mask2, acc_mask3; + CPC_MSG_T * out; + C0CONR_T c0con; + C1CONR_T c1con; + int tmpAccCode; + int tmpAccMask; + + // we have to convert the parameters into M16C parameters + CPC_SJA1000_PARAMS_T * pParams; + + // check if the type is CAN parameters and if we have to convert the given params + if (in->type != CPC_CMD_T_CAN_PRMS + || in->msg.canparams.cc_type != SJA1000) + return 0; + pParams = + (CPC_SJA1000_PARAMS_T *) & in->msg.canparams.cc_params.sja1000; + acc_code0 = pParams->acc_code0; + acc_code1 = pParams->acc_code1; + acc_code2 = pParams->acc_code2; + acc_code3 = pParams->acc_code3; + acc_mask0 = pParams->acc_mask0; + acc_mask1 = pParams->acc_mask1; + acc_mask2 = pParams->acc_mask2; + acc_mask3 = pParams->acc_mask3; + +#ifdef _DEBUG_OUTPUT_CAN_PARAMS + info("acc_code0: %2.2Xh\n", acc_code0); + info("acc_code1: %2.2Xh\n", acc_code1); + info("acc_code2: %2.2Xh\n", acc_code2); + info("acc_code3: %2.2Xh\n", acc_code3); + info("acc_mask0: %2.2Xh\n", acc_mask0); + info("acc_mask1: %2.2Xh\n", acc_mask1); + info("acc_mask2: %2.2Xh\n", acc_mask2); + info("acc_mask3: %2.2Xh\n", acc_mask3); + +#endif /* */ + if (! + (baudrate_error = + (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) { + err("Could not allocate memory\n"); + return -3; + } + if (! + (samplepoint_error = + (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) { + err("Could not allocate memory\n"); + vfree(baudrate_error); + return -3; + } + memset(baudrate_error, 0xff, sizeof(baudrate_error)); + memset(samplepoint_error, 0xff, sizeof(baudrate_error)); + sjaBaudrate = + 16000000 / 2 / SJA_BRP / (1 + SJA_TSEG1 + SJA_TSEG2); + sjaSamplepoint = + 100 * (1 + SJA_TSEG1) / (1 + SJA_TSEG1 + SJA_TSEG2); + if (sjaBaudrate == 0) { + vfree(baudrate_error); + vfree(samplepoint_error); + return -2; + } + +#ifdef _DEBUG_OUTPUT_CAN_PARAMS + info("\nStarting SJA CAN params\n"); + info("-------------------------\n"); + info("TS1 : %2.2Xh TS2 : %2.2Xh\n", SJA_TSEG1, SJA_TSEG2); + info("BTR0 : %2.2Xh BTR1: %2.2Xh\n", pParams->btr0, + pParams->btr1); + info("Baudrate: %d.%dkBaud\n", sjaBaudrate / 1000, + sjaBaudrate % 1000); + info("Sample P: 0.%d\n", sjaSamplepoint); + info("\n"); + +#endif /* */ + c0con.bc0con.sam = SJA_SAM; + c1con.bc1con.sjw = SJA_SJW; + + // calculate errors for all baudrates + index = 0; + for (clk = 0; clk < 5; clk++) { + for (brp = 0; brp < 16; brp++) { + for (pr = 0; pr < 8; pr++) { + for (ph1 = 0; ph1 < 8; ph1++) { + for (ph2 = 0; ph2 < 8; ph2++) { + baudrate_error[index] = + 100 * + abs(baudrate_m16c + (clk, brp, pr, ph1, + ph2) - + sjaBaudrate) / + sjaBaudrate; + samplepoint_error[index] = + abs(samplepoint_m16c + (brp, pr, ph1, + ph2) - + sjaSamplepoint); + +#if 0 + info + ("Baudrate : %d kBaud\n", + baudrate_m16c(clk, + brp, pr, + ph1, + ph2)); + info + ("Baudrate Error: %d\n", + baudrate_error + [index]); + info + ("Sample P Error: %d\n", + samplepoint_error + [index]); + info + ("clk : %d\n", + clk); + +#endif /* */ + index++; + } + } + } + } + } + + // mark all baudrate_error entries which are outer limits + index = 0; + for (clk = 0; clk < 5; clk++) { + for (brp = 0; brp < 16; brp++) { + for (pr = 0; pr < 8; pr++) { + for (ph1 = 0; ph1 < 8; ph1++) { + for (ph2 = 0; ph2 < 8; ph2++) { + if ((baudrate_error[index] + > + BAUDRATE_TOLERANCE_PERCENT) + || + (samplepoint_error + [index] > + SAMPLEPOINT_TOLERANCE_PERCENT) + || + (samplepoint_m16c + (brp, pr, ph1, + ph2) > + SAMPLEPOINT_UPPER_LIMIT)) + { + baudrate_error + [index] = -1; + } else + if (((1 + pr + 1 + + ph1 + 1 + ph2 + + 1) < 8) + || + ((1 + pr + 1 + + ph1 + 1 + ph2 + + 1) > 25)) { + baudrate_error + [index] = -1; + } + +#if 0 + else { + info + ("Baudrate : %d kBaud\n", + baudrate_m16c + (clk, brp, pr, + ph1, ph2)); + info + ("Baudrate Error: %d\n", + baudrate_error + [index]); + info + ("Sample P Error: %d\n", + samplepoint_error + [index]); + } + +#endif /* */ + index++; + } + } + } + } + } + + // find list of minimum of baudrate_error within unmarked entries + clk_merk = brp_merk = pr_merk = ph1_merk = ph2_merk = 0; + baudrate_error_merk = 100; + index = 0; + for (clk = 0; clk < 5; clk++) { + for (brp = 0; brp < 16; brp++) { + for (pr = 0; pr < 8; pr++) { + for (ph1 = 0; ph1 < 8; ph1++) { + for (ph2 = 0; ph2 < 8; ph2++) { + if (baudrate_error[index] + != -1) { + if (baudrate_error + [index] < + baudrate_error_merk) + { + baudrate_error_merk + = + baudrate_error + [index]; + brp_merk = + brp; + pr_merk = + pr; + ph1_merk = + ph1; + ph2_merk = + ph2; + clk_merk = + clk; + +#if 0 + info + ("brp: %2.2Xh pr: %2.2Xh ph1: %2.2Xh ph2: %2.2Xh\n", + brp, + pr, + ph1, + ph2); + info + ("Baudrate : %d kBaud\n", + baudrate_m16c + (clk, + brp, + pr, + ph1, + ph2)); + info + ("Baudrate Error: %d\n", + baudrate_error + [index]); + info + ("Sample P Error: %d\n", + samplepoint_error + [index]); + +#endif /* */ + } + } + index++; + } + } + } + } + } + if (baudrate_error_merk == 100) { + info("ERROR: Could not convert CAN init parameter\n"); + vfree(baudrate_error); + vfree(samplepoint_error); + return -1; + } + + // setting m16c CAN parameter + c0con.bc0con.brp = brp_merk; + c0con.bc0con.pr = pr_merk; + c1con.bc1con.ph1 = ph1_merk; + c1con.bc1con.ph2 = ph2_merk; + +#ifdef _DEBUG_OUTPUT_CAN_PARAMS + info("\nResulting M16C CAN params\n"); + info("-------------------------\n"); + info("clk : %2.2Xh\n", clk_merk); + info("ph1 : %2.2Xh ph2: %2.2Xh\n", c1con.bc1con.ph1 + 1, + c1con.bc1con.ph2 + 1); + info("pr : %2.2Xh brp: %2.2Xh\n", c0con.bc0con.pr + 1, + c0con.bc0con.brp + 1); + info("sjw : %2.2Xh sam: %2.2Xh\n", c1con.bc1con.sjw, + c0con.bc0con.sam); + info("co1 : %2.2Xh co0: %2.2Xh\n", c1con.c1con, c0con.c0con); + info("Baudrate: %d.%dBaud\n", + baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr, + c1con.bc1con.ph1, c1con.bc1con.ph2) / 1000, + baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr, + c1con.bc1con.ph1, c1con.bc1con.ph2) % 1000); + info("Sample P: 0.%d\n", + samplepoint_m16c(c0con.bc0con.brp, c0con.bc0con.pr, + c1con.bc1con.ph1, c1con.bc1con.ph2)); + info("\n"); + +#endif /* */ + out = in; + out->type = 6; + out->length = sizeof(CPC_M16C_BASIC_PARAMS_T) + 1; + out->msg.canparams.cc_type = M16C_BASIC; + out->msg.canparams.cc_params.m16c_basic.con0 = c0con.c0con; + out->msg.canparams.cc_params.m16c_basic.con1 = c1con.c1con; + out->msg.canparams.cc_params.m16c_basic.ctlr0 = 0x4C; + out->msg.canparams.cc_params.m16c_basic.ctlr1 = 0x00; + out->msg.canparams.cc_params.m16c_basic.clk = clk_merk; + out->msg.canparams.cc_params.m16c_basic.acc_std_code0 = + acc_code0; + out->msg.canparams.cc_params.m16c_basic.acc_std_code1 = acc_code1; + +// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1); + tmpAccCode = (acc_code1 >> 5) + (acc_code0 << 3); + out->msg.canparams.cc_params.m16c_basic.acc_std_code0 = + (unsigned char) tmpAccCode; + out->msg.canparams.cc_params.m16c_basic.acc_std_code1 = + (unsigned char) (tmpAccCode >> 8); + +// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1); + out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 = + ~acc_mask0; + out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 = + ~acc_mask1; + +// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1); + tmpAccMask = ((acc_mask1) >> 5) + ((acc_mask0) << 3); + +// info("tmpAccMask: 0x%4.4X\n", tmpAccMask); + out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 = + (unsigned char) ~tmpAccMask; + out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 = + (unsigned char) ~(tmpAccMask >> 8); + +// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1); + out->msg.canparams.cc_params.m16c_basic.acc_ext_code0 = + (unsigned char) tmpAccCode; + out->msg.canparams.cc_params.m16c_basic.acc_ext_code1 = + (unsigned char) (tmpAccCode >> 8); + out->msg.canparams.cc_params.m16c_basic.acc_ext_code2 = acc_code2; + out->msg.canparams.cc_params.m16c_basic.acc_ext_code3 = acc_code3; + out->msg.canparams.cc_params.m16c_basic.acc_ext_mask0 = + (unsigned char) ~tmpAccMask; + out->msg.canparams.cc_params.m16c_basic.acc_ext_mask1 = + (unsigned char) ~(tmpAccMask >> 8); + out->msg.canparams.cc_params.m16c_basic.acc_ext_mask2 = + ~acc_mask2; + out->msg.canparams.cc_params.m16c_basic.acc_ext_mask3 = + ~acc_mask3; + vfree(baudrate_error); + vfree(samplepoint_error); + return 0; +} + + -- cgit v1.2.3