From 8bb61362e6640738a252b1d7cdf325617fd80926 Mon Sep 17 00:00:00 2001 From: Jan Kriz Date: Mon, 7 Jul 2008 23:11:32 +0200 Subject: [PATCH] Adding device side of can queue --- embedded/app/usbcan/can/i82527.h | 177 ++++++++ embedded/app/usbcan/can/modparms.h | 10 + embedded/app/usbcan/can/setup.h | 16 + embedded/app/usbcan/can/ul_usb1.h | 21 + embedded/app/usbcan/setup.c | 395 ++++++++++++++++++ embedded/app/usbcan/ul_usb1.c | 637 +++++++++++++++++++++++++++++ 6 files changed, 1256 insertions(+) create mode 100644 embedded/app/usbcan/can/i82527.h create mode 100644 embedded/app/usbcan/can/modparms.h create mode 100644 embedded/app/usbcan/can/setup.h create mode 100644 embedded/app/usbcan/can/ul_usb1.h create mode 100644 embedded/app/usbcan/setup.c create mode 100644 embedded/app/usbcan/ul_usb1.c diff --git a/embedded/app/usbcan/can/i82527.h b/embedded/app/usbcan/can/i82527.h new file mode 100644 index 0000000..1747396 --- /dev/null +++ b/embedded/app/usbcan/can/i82527.h @@ -0,0 +1,177 @@ +/* i82527.h + * Header file for the Linux CAN-bus driver. + * Written by Arnaud Westenberg email:arnaud@wanadoo.nl + * Rewritten for new CAN queues by Pavel Pisa - OCERA team member + * email:pisa@cmp.felk.cvut.cz + * This software is released under the GPL-License. + * Version lincan-0.3 17 Jun 2004 + */ + +int i82527_enable_configuration(struct canchip_t *chip); +int i82527_disable_configuration(struct canchip_t *chip); +int i82527_chip_config(struct canchip_t *chip); +int i82527_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw, + int sampl_pt, int flags); +int i82527_standard_mask(struct canchip_t *chip, unsigned short code, + unsigned short mask); +int i82527_extended_mask(struct canchip_t *chip, unsigned long code, + unsigned long mask); +int i82527_message15_mask(struct canchip_t *chip, unsigned long code, + unsigned long mask); +int i82527_clear_objects(struct canchip_t *chip); +int i82527_config_irqs(struct canchip_t *chip, short irqs); +int i82527_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj); +int i82527_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, + struct canmsg_t *msg); +int i82527_send_msg(struct canchip_t *chip, struct msgobj_t *obj, + struct canmsg_t *msg); +int i82527_remote_request(struct canchip_t *chip, struct msgobj_t *obj); +int i82527_set_btregs(struct canchip_t *chip, unsigned short btr0, + unsigned short btr1); +int i82527_start_chip(struct canchip_t *chip); +int i82527_stop_chip(struct canchip_t *chip); +int i82527_check_tx_stat(struct canchip_t *chip); +int i82527_irq_handler(int irq, struct canchip_t *chip); +int i82527_fill_chipspecops(struct canchip_t *chip); + + +#define MSG_OFFSET(object) ((object)*0x10) + +#define iCTL 0x00 // Control Register +#define iSTAT 0x01 // Status Register +#define iCPU 0x02 // CPU Interface Register +#define iHSR 0x04 // High Speed Read +#define iSGM0 0x06 // Standard Global Mask byte 0 +#define iSGM1 0x07 +#define iEGM0 0x08 // Extended Global Mask byte 0 +#define iEGM1 0x09 +#define iEGM2 0x0a +#define iEGM3 0x0b +#define i15M0 0x0c // Message 15 Mask byte 0 +#define i15M1 0x0d +#define i15M2 0x0e +#define i15M3 0x0f +#define iCLK 0x1f // Clock Out Register +#define iBUS 0x2f // Bus Configuration Register +#define iBT0 0x3f // Bit Timing Register byte 0 +#define iBT1 0x4f +#define iIRQ 0x5f // Interrupt Register +#define iP1C 0x9f // Port 1 Register +#define iP2C 0xaf // Port 2 Register +#define iP1I 0xbf // Port 1 Data In Register +#define iP2I 0xcf // Port 2 Data In Register +#define iP1O 0xdf // Port 1 Data Out Register +#define iP2O 0xef // Port 2 Data Out Register +#define iSRA 0xff // Serial Reset Address + +#define iMSGCTL0 0x00 /* First Control register */ +#define iMSGCTL1 0x01 /* Second Control register */ +#define iMSGID0 0x02 /* First Byte of Message ID */ +#define iMSGID1 0x03 +#define iMSGID2 0x04 +#define iMSGID3 0x05 +#define iMSGCFG 0x06 /* Message Configuration */ +#define iMSGDAT0 0x07 /* First Data Byte */ +#define iMSGDAT1 0x08 +#define iMSGDAT2 0x09 +#define iMSGDAT3 0x0a +#define iMSGDAT4 0x0b +#define iMSGDAT5 0x0c +#define iMSGDAT6 0x0d +#define iMSGDAT7 0x0e + +/* Control Register (0x00) */ +enum i82527_iCTL { + iCTL_INI = 1, // Initialization + iCTL_IE = 1<<1, // Interrupt Enable + iCTL_SIE = 1<<2, // Status Interrupt Enable + iCTL_EIE = 1<<3, // Error Interrupt Enable + iCTL_CCE = 1<<6 // Change Configuration Enable +}; + +/* Status Register (0x01) */ +enum i82527_iSTAT { + iSTAT_TXOK = 1<<3, // Transmit Message Successfully + iSTAT_RXOK = 1<<4, // Receive Message Successfully + iSTAT_WAKE = 1<<5, // Wake Up Status + iSTAT_WARN = 1<<6, // Warning Status + iSTAT_BOFF = 1<<7 // Bus Off Status +}; + +/* CPU Interface Register (0x02) */ +enum i82527_iCPU { + iCPU_CEN = 1, // Clock Out Enable + iCPU_MUX = 1<<2, // Multiplex + iCPU_SLP = 1<<3, // Sleep + iCPU_PWD = 1<<4, // Power Down Mode + iCPU_DMC = 1<<5, // Divide Memory Clock + iCPU_DSC = 1<<6, // Divide System Clock + iCPU_RST = 1<<7 // Hardware Reset Status +}; + +/* Clock Out Register (0x1f) */ +enum i82527_iCLK { + iCLK_CD0 = 1, // Clock Divider bit 0 + iCLK_CD1 = 1<<1, + iCLK_CD2 = 1<<2, + iCLK_CD3 = 1<<3, + iCLK_SL0 = 1<<4, // Slew Rate bit 0 + iCLK_SL1 = 1<<5 +}; + +/* Bus Configuration Register (0x2f) */ +enum i82527_iBUS { + iBUS_DR0 = 1, // Disconnect RX0 Input + iBUS_DR1 = 1<<1, // Disconnect RX1 Input + iBUS_DT1 = 1<<3, // Disconnect TX1 Output + iBUS_POL = 1<<5, // Polarity + iBUS_CBY = 1<<6 // Comparator Bypass +}; + +#define RESET 1 // Bit Pair Reset Status +#define SET 2 // Bit Pair Set Status +#define UNCHANGED 3 // Bit Pair Unchanged + +/* Message Control Register 0 (Base Address + 0x0) */ +enum i82527_iMSGCTL0 { + INTPD_SET = SET, // Interrupt pending + INTPD_RES = RESET, // No Interrupt pending + INTPD_UNC = UNCHANGED, + RXIE_SET = SET<<2, // Receive Interrupt Enable + RXIE_RES = RESET<<2, // Receive Interrupt Disable + RXIE_UNC = UNCHANGED<<2, + TXIE_SET = SET<<4, // Transmit Interrupt Enable + TXIE_RES = RESET<<4, // Transmit Interrupt Disable + TXIE_UNC = UNCHANGED<<4, + MVAL_SET = SET<<6, // Message Valid + MVAL_RES = RESET<<6, // Message Invalid + MVAL_UNC = UNCHANGED<<6 +}; + +/* Message Control Register 1 (Base Address + 0x01) */ +enum i82527_iMSGCTL1 { + NEWD_SET = SET, // New Data + NEWD_RES = RESET, // No New Data + NEWD_UNC = UNCHANGED, + MLST_SET = SET<<2, // Message Lost + MLST_RES = RESET<<2, // No Message Lost + MLST_UNC = UNCHANGED<<2, + CPUU_SET = SET<<2, // CPU Updating + CPUU_RES = RESET<<2, // No CPU Updating + CPUU_UNC = UNCHANGED<<2, + TXRQ_SET = SET<<4, // Transmission Request + TXRQ_RES = RESET<<4, // No Transmission Request + TXRQ_UNC = UNCHANGED<<4, + RMPD_SET = SET<<6, // Remote Request Pending + RMPD_RES = RESET<<6, // No Remote Request Pending + RMPD_UNC = UNCHANGED<<6 +}; + +/* Message Configuration Register (Base Address + 0x06) */ +enum i82527_iMSGCFG { + MCFG_XTD = 1<<2, // Extended Identifier + MCFG_DIR = 1<<3 // Direction is Transmit +}; + +void i82527_seg_write_reg(const struct canchip_t *chip, unsigned char data, unsigned address); +unsigned i82527_seg_read_reg(const struct canchip_t *chip, unsigned address); diff --git a/embedded/app/usbcan/can/modparms.h b/embedded/app/usbcan/can/modparms.h new file mode 100644 index 0000000..6691931 --- /dev/null +++ b/embedded/app/usbcan/can/modparms.h @@ -0,0 +1,10 @@ +/* mod_parms.h + * Header file for the Linux CAN-bus driver. + * Written by Arnaud Westenberg email:arnaud@wanadoo.nl + * Rewritten for new CAN queues by Pavel Pisa - OCERA team member + * email:pisa@cmp.felk.cvut.cz + * This software is released under the GPL-License. + * Version lincan-0.3 17 Jun 2004 + */ + +int parse_mod_parms(void); diff --git a/embedded/app/usbcan/can/setup.h b/embedded/app/usbcan/can/setup.h new file mode 100644 index 0000000..d35a8b1 --- /dev/null +++ b/embedded/app/usbcan/can/setup.h @@ -0,0 +1,16 @@ +/* setup.h + * Header file for the Linux CAN-bus driver. + * Written by Arnaud Westenberg email:arnaud@wanadoo.nl + * Rewritten for new CAN queues by Pavel Pisa - OCERA team member + * email:pisa@cmp.felk.cvut.cz + * This software is released under the GPL-License. + * Version lincan-0.3 17 Jun 2004 + */ + +int init_hw_struct(void); +int list_hw(void); +void *can_checked_malloc(size_t size); +int can_checked_free(void *address_p); +int can_del_mem_list(void); +int can_chip_setup_irq(struct canchip_t *chip); +void can_chip_free_irq(struct canchip_t *chip); diff --git a/embedded/app/usbcan/can/ul_usb1.h b/embedded/app/usbcan/can/ul_usb1.h new file mode 100644 index 0000000..818bb24 --- /dev/null +++ b/embedded/app/usbcan/can/ul_usb1.h @@ -0,0 +1,21 @@ +/* ul_usb1.h + * Header file for the Linux CAN-bus driver. + * Written by Arnaud Westenberg email:arnaud@wanadoo.nl + * Rewritten for new CAN queues by Pavel Pisa - OCERA team member + * email:pisa@cmp.felk.cvut.cz + * This software is released under the GPL-License. + * Version lincan-0.3 17 Jun 2004 + */ + +// int ul_usb1_request_io(struct candevice_t *candev); +// int ul_usb1_release_io(struct candevice_t *candev); +// int ul_usb1_reset(struct candevice_t *candev); +// int ul_usb1_init_hw_data(struct candevice_t *candev); +// int ul_usb1_init_chip_data(struct candevice_t *candev, int chipnr); +// int ul_usb1_init_obj_data(struct canchip_t *chip, int objnr); +// void ul_usb1_write_register(struct candevice_t *candev,unsigned data, unsigned long address); +// unsigned ul_usb1_read_register(struct candevice_t *candev,unsigned long address); +// int ul_usb1_program_irq(struct candevice_t *candev); + +int ul_usb1_init(void); +void ul_usb1_exit(void); diff --git a/embedded/app/usbcan/setup.c b/embedded/app/usbcan/setup.c new file mode 100644 index 0000000..59ab8b5 --- /dev/null +++ b/embedded/app/usbcan/setup.c @@ -0,0 +1,395 @@ +/* setup.c + * Linux CAN-bus device driver. + * Written by Arnaud Westenberg email:arnaud@wanadoo.nl + * Rewritten for new CAN queues by Pavel Pisa - OCERA team member + * email:pisa@cmp.felk.cvut.cz + * This software is released under the GPL-License. + * Version lincan-0.3 17 Jun 2004 + */ + +#include "./can/can.h" +#include "./can/can_sysless.h" +#include "./can/main.h" +#include "./can/devcommon.h" +#include "./can/setup.h" +//#include "./can/finish.h" + +int init_hwspecops(struct candevice_t *candev, int *irqnum_p); +int init_device_struct(int card, int *chan_param_idx_p, int *irq_param_idx_p); +int init_chip_struct(struct candevice_t *candev, int chipnr, int irq, long baudrate); +int init_obj_struct(struct candevice_t *candev, struct canchip_t *hostchip, int objnr); + +/** + * can_base_addr_fixup - relocates board physical memory addresses to the CPU accessible ones + * @candev: pointer to the previously filled device/board, chips and message objects structures + * @new_base: @candev new base address + * + * This function adapts base addresses of all structures of one board + * to the new board base address. + * It is required for translation between physical and virtual address mappings. + * This function is prepared to simplify board specific xxx_request_io() function + * for memory mapped devices. + */ +int can_base_addr_fixup(struct candevice_t *candev, unsigned long new_base) +{ + unsigned long offs; + int i, j; + + offs=new_base-candev->dev_base_addr; + candev->dev_base_addr=new_base; + for(i=0;inr_all_chips;i++){ + candev->chip[i]->chip_base_addr += offs; + for(j=0;jchip[i]->max_objects;j++) + candev->chip[i]->msgobj[j]->obj_base_addr += offs; + } + return 0; +} + +/** + * can_check_dev_taken - checks if bus device description is already taken by driver + * @anydev: pointer to bus specific Linux device description + * + * Returns: Returns 1 if device is already used by LinCAN driver, 0 otherwise. + */ +int can_check_dev_taken(void *anydev) +{ + int board_nr; + struct candevice_t *candev; + void *boarddev; + + for (board_nr=hardware_p->nr_boards; board_nr--; ) { + if((candev=hardware_p->candevice[board_nr])==NULL) + continue; + boarddev=candev->sysdevptr.anydev; + if(boarddev == anydev) + return 1; + } + + return 0; +} + + +/** + * register_obj_struct - registers message object into global array + * @obj: the initialized message object being registered + * @minorbase: wanted minor number, if (-1) automatically selected + * + * Return Value: returns negative number in the case of fail + */ +int register_obj_struct(struct msgobj_t *obj, int minorbase) +{ + static int next_minor=0; + int i; + + if(minorbase>=0) + next_minor=minorbase; + if(next_minor>=MAX_TOT_MSGOBJS) + next_minor=0; + i=next_minor; + do{ + if(objects_p[i]==NULL){ + objects_p[i]=obj; + obj->minor=i; + next_minor=i+1; + return 0; + } + if(++i >= MAX_TOT_MSGOBJS) i=0; + }while(i!=next_minor); + obj->minor=-1; + return -1; +} + + +/** + * register_chip_struct - registers chip into global array + * @chip: the initialized chip structure being registered + * @minorbase: wanted minor number base, if (-1) automatically selected + * + * Return Value: returns negative number in the case of fail + */ +int register_chip_struct(struct canchip_t *chip, int minorbase) +{ + static int next_chip_slot=0; + int i; + + if(next_chip_slot>=MAX_TOT_CHIPS) + next_chip_slot=0; + i=next_chip_slot; + do{ + if(chips_p[i]==NULL){ + chips_p[i]=chip; + + next_chip_slot=i+1; + return 0; + } + if(++i >= MAX_TOT_CHIPS) i=0; + }while(i!=next_chip_slot); + return -1; +} + + + +/** + * init_hw_struct - initializes driver hardware description structures + * + * The function init_hw_struct() is used to initialize the hardware structure. + * + * Return Value: returns negative number in the case of fail + */ +int init_hw_struct(void) +{ + int i=0; + int irq_param_idx=0; + int chan_param_idx=0; + + hardware_p->nr_boards=0; + while ( (hw[i] != NULL) & (i < MAX_HW_CARDS) ) { + hardware_p->nr_boards++; + + if (init_device_struct(i, &chan_param_idx, &irq_param_idx)) { + CANMSG("Error initializing candevice_t structures.\n"); + return -ENODEV; + } + i++; + } + + return 0; +} + +/** + * init_device_struct - initializes single CAN device/board + * @card: index into @hardware_p HW description + * @chan_param_idx_p: pointer to the index into arrays of the CAN channel parameters + * @irq_param_idx_p: pointer to the index into arrays of the per CAN channel IRQ parameters + * + * The function builds representation of the one board from parameters provided + * in the module parameters arrays: + * @hw[card] .. hardware type, + * @io[card] .. base IO address, + * @baudrate[chan_param_idx] .. per channel baudrate, + * @minor[chan_param_idx] .. optional specification of requested channel minor base, + * @irq[irq_param_idx] .. one or more board/chips IRQ parameters. + * The indexes are advanced after consumed parameters if the registration is successful. + * + * The hardware specific operations of the device/board are initialized by call to + * init_hwspecops() function. Then board data are initialized by board specific + * init_hw_data() function. Then chips and objects representation is build by + * init_chip_struct() function. If all above steps are successful, chips and + * message objects are registered into global arrays. + * + * Return Value: returns negative number in the case of fail + */ +int init_device_struct(int card, int *chan_param_idx_p, int *irq_param_idx_p) +{ + struct candevice_t *candev; + int ret; + int irqnum; + int chipnr; + long bd; + int irqsig=-1; + + candev=(struct candevice_t *)can_checked_malloc(sizeof(struct candevice_t)); + if (candev==NULL) + return -ENOMEM; + + memset(candev, 0, sizeof(struct candevice_t)); + + hardware_p->candevice[card]=candev; + candev->candev_idx=card; + + candev=candev; + + candev->hwname=hw[card]; + candev->io_addr=io[card]; + candev->dev_base_addr=io[card]; + + candev->hwspecops=(struct hwspecops_t *)can_checked_malloc(sizeof(struct hwspecops_t)); + if (candev->hwspecops==NULL) + goto error_nomem; + + memset(candev->hwspecops, 0, sizeof(struct hwspecops_t)); + + if (init_hwspecops(candev, &irqnum)) + goto error_nodev; + + if (candev->hwspecops->init_hw_data(candev)) + goto error_nodev; + + /* Alocate and initialize the chip structures */ + for (chipnr=0; chipnr < candev->nr_all_chips; chipnr++) { + + if(chipnrnr_all_chips; chipnr++) { + int m=minor[*chan_param_idx_p+chipnr]; + struct canchip_t *chip=candev->chip[chipnr]; + int objnr; + + register_chip_struct(chip, m); + + for (objnr=0; objnrmax_objects; objnr++) { + register_obj_struct(chip->msgobj[objnr], m); + if(m>=0) m++; + } + } + + *irq_param_idx_p += irqnum; + *chan_param_idx_p += candev->nr_all_chips; + + return 0; + + error_nodev: + ret=-ENODEV; + error_chip: + candevice_done(candev); + goto error_both; + + error_nomem: + ret=-ENOMEM; + + error_both: + hardware_p->candevice[card]=NULL; + can_checked_free(candev); + return ret; + +} + +/** + * init_chip_struct - initializes one CAN chip structure + * @candev: pointer to the corresponding CAN device/board + * @chipnr: index of the chip in the corresponding device/board structure + * @irq: chip IRQ number or (-1) if not appropriate + * @baudrate: baudrate in the units of 1Bd + * + * Chip structure is allocated and chip specific operations are filled by + * call to board specific init_chip_data() which calls chip specific + * fill_chipspecops(). The message objects are generated by + * calls to init_obj_struct() function. + * + * Return Value: returns negative number in the case of fail + */ +int init_chip_struct(struct candevice_t *candev, int chipnr, int irq, long baudrate) +{ + struct canchip_t *chip; + int objnr; + int ret; + + candev->chip[chipnr]=(struct canchip_t *)can_checked_malloc(sizeof(struct canchip_t)); + if ((chip=candev->chip[chipnr])==NULL) + return -ENOMEM; + + memset(chip, 0, sizeof(struct canchip_t)); + + chip->write_register=candev->hwspecops->write_register; + chip->read_register=candev->hwspecops->read_register; + + chip->chipspecops=can_checked_malloc(sizeof(struct chipspecops_t)); + if (chip->chipspecops==NULL) + return -ENOMEM; + memset(chip->chipspecops,0,sizeof(struct chipspecops_t)); + + chip->chip_idx=chipnr; + chip->hostdevice=candev; + chip->chip_irq=irq; + chip->baudrate=baudrate; + chip->flags=0x0; + + if(candev->hwspecops->init_chip_data(candev,chipnr)<0) + return -ENODEV; + + for (objnr=0; objnrmax_objects; objnr++) { + ret=init_obj_struct(candev, chip, objnr); + if(ret<0) return ret; + } + + return 0; +} + + +/** + * init_obj_struct - initializes one CAN message object structure + * @candev: pointer to the corresponding CAN device/board + * @hostchip: pointer to the chip containing this object + * @objnr: index of the builded object in the chip structure + * + * The function initializes message object structure and allocates and initializes + * CAN queue chip ends structure. + * + * Return Value: returns negative number in the case of fail + */ +int init_obj_struct(struct candevice_t *candev, struct canchip_t *hostchip, int objnr) +{ + struct canque_ends_t *qends; + struct msgobj_t *obj; + int ret; + + obj=(struct msgobj_t *)can_checked_malloc(sizeof(struct msgobj_t)); + hostchip->msgobj[objnr]=obj; + if (obj == NULL) + return -ENOMEM; + + memset(obj, 0, sizeof(struct msgobj_t)); + obj->minor=-1; + + atomic_set(&obj->obj_used,0); + INIT_LIST_HEAD(&obj->obj_users); + init_timer(&obj->tx_timeout); + + qends = (struct canque_ends_t *)can_checked_malloc(sizeof(struct canque_ends_t)); + if(qends == NULL) return -ENOMEM; + memset(qends, 0, sizeof(struct canque_ends_t)); + obj->hostchip=hostchip; + obj->object=objnr+1; + obj->qends=qends; + obj->tx_qedge=NULL; + obj->tx_slot=NULL; + obj->obj_flags = 0x0; + + ret=canqueue_ends_init_chip(qends, hostchip, obj); + if(ret<0) return ret; + + ret=candev->hwspecops->init_obj_data(hostchip,objnr); + if(ret<0) return ret; + + return 0; +} + + +/** + * init_hwspecops - finds and initializes board/device specific operations + * @candev: pointer to the corresponding CAN device/board + * @irqnum_p: optional pointer to the number of interrupts required by board + * + * The function searches board @hwname in the list of supported boards types. + * The board type specific board_register() function is used to initialize + * @hwspecops operations. + * + * Return Value: returns negative number in the case of fail + */ +int init_hwspecops(struct candevice_t *candev, int *irqnum_p) +{ + const struct boardtype_t *brp; + + brp = boardtype_find(candev->hwname); + + if(!brp) { + CANMSG("Sorry, hardware \"%s\" is currently not supported.\n",candev->hwname); + return -EINVAL; + } + + if(irqnum_p) + *irqnum_p=brp->irqnum; + brp->board_register(candev->hwspecops); + + return 0; +} diff --git a/embedded/app/usbcan/ul_usb1.c b/embedded/app/usbcan/ul_usb1.c new file mode 100644 index 0000000..b777b14 --- /dev/null +++ b/embedded/app/usbcan/ul_usb1.c @@ -0,0 +1,637 @@ +/* ul_usb1.c + * Linux CAN-bus device driver. + * Written by Jan Kriz email:johen@post.cz + * This software is released under the GPL-License. + * Version lincan-0.3 17 Jun 2004 + * + * Based on + * USB Skeleton driver - 2.2 + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.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, version 2. + * + * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c + * but has been rewritten to be easier to read and use. + * + */ + +#include "./can/ul_usb1.h" +#include "./can/can.h" +#include "./can/can_sysdep.h" +#include "./can/main.h" +#include "./can/devcommon.h" +#include "./can/setup.h" +#include "./can/finish.h" +#include "./can/i82527.h" +//#include "../include/sja1000.h" +#include "./can/sja1000p.h" + +#include "./can/errno.h" + +/* Get a minor range for your devices from the usb maintainer */ +#define USB_SKEL_MINOR_BASE 192 + +#define CAN_OP_MASK 0x80 +#define CAN_OP_READ 0x80 +#define CAN_OP_WRITE 0x00 + + +/* our private defines. if this grows any larger, use your own .h file */ +#define MAX_TRANSFER (PAGE_SIZE - 512) +/* MAX_TRANSFER is chosen so that the VM is not stressed by + allocations > PAGE_SIZE and the number of packets in a page + is an integer 512 is the largest possible packet on EHCI */ +#define WRITES_IN_FLIGHT 8 +/* arbitrarily chosen */ + +/* Define these values to match your devices */ +#define USB_SKEL_VENDOR_ID 0xDEAD +#define USB_SKEL_PRODUCT_ID 0x1001 + +/* table of devices that work with this driver */ +// static struct usb_device_id ul_usb1_table [] = { +// { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, +// { } /* Terminating entry */ +// }; +// MODULE_DEVICE_TABLE(usb, ul_usb1_table); + +extern struct file_operations can_fops; + +struct ul_usb1_combo { + struct usb_ul_usb1 * dev; + struct urb * urb; +}; + +/* + * IO_RANGE is the io-memory range that gets reserved, please adjust according + * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or + * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode. + */ +#define IO_RANGE 0x100 + +/* Structure to hold all of our device specific stuff */ +struct usb_ul_usb1 { + struct usb_device *udev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ + struct semaphore limit_sem; /* limiting the number of writes in progress */ + struct usb_anchor submitted; /* in case we need to retract our submissions */ + unsigned char *bulk_in_buffer; /* the buffer to receive data */ + size_t bulk_in_size; /* the size of the receive buffer */ + unsigned char *int_in_buffer; /* the buffer to receive data */ + size_t int_in_size; /* the size of the receive buffer */ + __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + __u8 int_in_endpointAddr; /* the address of the interrupt in endpoint */ + int int_in_interval; + int errors; /* the last request tanked */ + int open_count; /* count the number of openers */ + spinlock_t err_lock; /* lock for errors */ + struct mutex io_mutex; /* synchronize I/O with disconnect */ + struct urb *irq; + struct candevice_t *candev; +}; + +static struct usb_driver ul_usb1_driver; + +/** ul_usb1_request_io + * ul_usb1_request_io: - reserve io or memory range for can board + * @candev: pointer to candevice/board which asks for io. Field @io_addr + * of @candev is used in most cases to define start of the range + * + * The function ul_usb1_request_io() is used to reserve the io-memory. If your + * hardware uses a dedicated memory range as hardware control registers you + * will have to add the code to reserve this memory as well. + * %IO_RANGE is the io-memory range that gets reserved, please adjust according + * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or + * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode. + * Return Value: The function returns zero on success or %-ENODEV on failure + * File: src/ul_usb1.c + */ +int ul_usb1_request_io(struct candevice_t *candev) +{ + ((struct usb_ul_usb1*)candev->sysdevptr.anydev)->candev=candev; + return 0; +} + +/** ul_usb1_release_io + * ul_usb1_release_io - free reserved io memory range + * @candev: pointer to candevice/board which releases io + * + * The function ul_usb1_release_io() is used to free reserved io-memory. + * In case you have reserved more io memory, don't forget to free it here. + * IO_RANGE is the io-memory range that gets released, please adjust according + * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or + * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode. + * Return Value: The function always returns zero + * File: src/ul_usb1.c + */ +int ul_usb1_release_io(struct candevice_t *candev) +{ + struct usb_ul_usb1 *dev; + if (candev->sysdevptr.anydev){ + dev=(struct usb_ul_usb1*) candev->sysdevptr.anydev; + usb_put_dev(dev->udev); + usb_kill_urb(dev->irq); + usb_free_urb(dev->irq); + kfree(dev->bulk_in_buffer); + kfree(dev->int_in_buffer); + if (dev->candev){ + dev->candev->sysdevptr.anydev=NULL; + //cleanup_usbdev(dev->candev); + } + kfree(dev); + } + return 0; +} + +/** ul_usb1_reset + * ul_usb1_reset - hardware reset routine + * @candev: Pointer to candevice/board structure + * + * The function ul_usb1_reset() is used to give a hardware reset. This is + * rather hardware specific so I haven't included example code. Don't forget to + * check the reset status of the chip before returning. + * Return Value: The function returns zero on success or %-ENODEV on failure + * File: src/ul_usb1.c + */ +int ul_usb1_reset(struct candevice_t *candev) +{ + return 0; +} + +#define RESET_ADDR 0x0 +#define NR_82527 0 +#define NR_SJA1000 1 + +/** ul_usb1_init_hw_data + * ul_usb1_init_hw_data - Initialize hardware cards + * @candev: Pointer to candevice/board structure + * + * The function ul_usb1_init_hw_data() is used to initialize the hardware + * structure containing information about the installed CAN-board. + * %RESET_ADDR represents the io-address of the hardware reset register. + * %NR_82527 represents the number of Intel 82527 chips on the board. + * %NR_SJA1000 represents the number of Philips sja1000 chips on the board. + * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that + * the hardware uses programmable interrupts. + * Return Value: The function always returns zero + * File: src/ul_usb1.c + */ +int ul_usb1_init_hw_data(struct candevice_t *candev) +{ + candev->res_addr=RESET_ADDR; + candev->nr_82527_chips=NR_82527; + candev->nr_sja1000_chips=NR_SJA1000; + candev->nr_all_chips=NR_82527+NR_SJA1000; + //candev->flags |= CANDEV_PROGRAMMABLE_IRQ; + + return 0; +} + +/** ul_usb1_init_chip_data + * ul_usb1_init_chip_data - Initialize chips + * @candev: Pointer to candevice/board structure + * @chipnr: Number of the CAN chip on the hardware card + * + * The function ul_usb1_init_chip_data() is used to initialize the hardware + * structure containing information about the CAN chips. + * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or + * "sja1000". + * The @chip_base_addr entry represents the start of the 'official' memory map + * of the installed chip. It's likely that this is the same as the @io_addr + * argument supplied at module loading time. + * The @clock entry holds the chip clock value in Hz. + * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider + * register. Options defined in the %sja1000.h file: + * %sjaCDR_CLKOUT_MASK, %sjaCDR_CLK_OFF, %sjaCDR_RXINPEN, %sjaCDR_CBP, %sjaCDR_PELICAN + * The entry @sja_ocr_reg holds hardware specific options for the Output Control + * register. Options defined in the %sja1000.h file: + * %sjaOCR_MODE_BIPHASE, %sjaOCR_MODE_TEST, %sjaOCR_MODE_NORMAL, %sjaOCR_MODE_CLOCK, + * %sjaOCR_TX0_LH, %sjaOCR_TX1_ZZ. + * The entry @int_clk_reg holds hardware specific options for the Clock Out + * register. Options defined in the %i82527.h file: + * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1. + * The entry @int_bus_reg holds hardware specific options for the Bus + * Configuration register. Options defined in the %i82527.h file: + * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY. + * The entry @int_cpu_reg holds hardware specific options for the cpu interface + * register. Options defined in the %i82527.h file: + * %iCPU_CEN, %iCPU_MUX, %iCPU_SLP, %iCPU_PWD, %iCPU_DMC, %iCPU_DSC, %iCPU_RST. + * Return Value: The function always returns zero + * File: src/ul_usb1.c + */ +int ul_usb1_init_chip_data(struct candevice_t *candev, int chipnr) +{ + /*i82527_fill_chipspecops(candev->chip[chipnr]);*/ + /*sja1000_fill_chipspecops(candev->chip[chipnr]);*/ + sja1000p_fill_chipspecops(candev->chip[chipnr]); + + candev->chip[chipnr]->flags|= CHIP_IRQ_CUSTOM; + + candev->chip[chipnr]->chip_base_addr=0; + candev->chip[chipnr]->clock = 24000000; + candev->chip[chipnr]->int_cpu_reg = iCPU_DSC; + candev->chip[chipnr]->int_clk_reg = iCLK_SL1; + candev->chip[chipnr]->int_bus_reg = iBUS_CBY; + candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF; + candev->chip[chipnr]->sja_ocr_reg = sjaOCR_MODE_NORMAL | + sjaOCR_TX0_LH; + + return 0; +} + +/** ul_usb1_init_obj_data + * ul_usb1_init_obj_data - Initialize message buffers + * @chip: Pointer to chip specific structure + * @objnr: Number of the message buffer + * + * The function ul_usb1_init_obj_data() is used to initialize the hardware + * structure containing information about the different message objects on the + * CAN chip. In case of the sja1000 there's only one message object but on the + * i82527 chip there are 15. + * The code below is for a i82527 chip and initializes the object base addresses + * The entry @obj_base_addr represents the first memory address of the message + * object. In case of the sja1000 @obj_base_addr is taken the same as the chips + * base address. + * Unless the hardware uses a segmented memory map, flags can be set zero. + * Return Value: The function always returns zero + * File: src/ul_usb1.c + */ +int ul_usb1_init_obj_data(struct canchip_t *chip, int objnr) +{ + chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr+(objnr+1)*0x10; + + return 0; +} + +/** ul_usb1_program_irq + * ul_usb1_program_irq - program interrupts + * @candev: Pointer to candevice/board structure + * + * The function ul_usb1_program_irq() is used for hardware that uses + * programmable interrupts. If your hardware doesn't use programmable interrupts + * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and + * leave this function unedited. Again this function is hardware specific so + * there's no example code. + * Return value: The function returns zero on success or %-ENODEV on failure + * File: src/ul_usb1.c + */ +int ul_usb1_program_irq(struct candevice_t *candev) +{ + return 0; +} + +/** ul_usb1_write_register + * ul_usb1_write_register - Low level write register routine + * @data: data to be written + * @address: memory address to write to + * + * The function ul_usb1_write_register() is used to write to hardware registers + * on the CAN chip. You should only have to edit this function if your hardware + * uses some specific write process. + * Return Value: The function does not return a value + * File: src/ul_usb1.c + */ +void ul_usb1_write_register(struct candevice_t *candev,unsigned data, unsigned long address) +{ + struct usb_ul_usb1 *dev; + int retval; + int bytes_transferred; + unsigned char buffer[2]; + buffer[0]=((unsigned char)address & ~CAN_OP_MASK)+CAN_OP_WRITE; + buffer[1]=(unsigned char)data; + + dev = (struct usb_ul_usb1 *)candev->sysdevptr.anydev; + + mutex_lock(&dev->io_mutex); + if (!dev) { /* disconnect() was called */ + CANMSG("Sending %lu:%X : ERR No device\n",address,(uint8_t)data); + retval = -ENODEV; + goto exit; + } + if (!dev->interface) { /* disconnect() was called */ + CANMSG("Sending %lu:%X : ERR No interface\n",address,(uint8_t)data); + retval = -ENODEV; + goto exit; + } + + /* do a blocking bulk write to send data to the device */ + retval = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + buffer, + 2, + &bytes_transferred, 10000); + CANMSG("Sending %lu:%X : retval %d, transferred %d bytes\n",address,(uint8_t)data,retval,bytes_transferred); + +exit: + mutex_unlock(&dev->io_mutex); +} + +/** ul_usb1_read_register + * ul_usb1_read_register - Low level read register routine + * @address: memory address to read from + * + * The function ul_usb1_read_register() is used to read from hardware registers + * on the CAN chip. You should only have to edit this function if your hardware + * uses some specific read process. + * Return Value: The function returns the value stored in @address + * File: src/ul_usb1.c + */ +unsigned ul_usb1_read_register(struct candevice_t *candev,unsigned long address) +{ + struct usb_ul_usb1 *dev; + int retval; + int bytes_transferred; + unsigned char buffer[2]; + buffer[0]=((unsigned char)address & ~CAN_OP_MASK)+CAN_OP_READ; + buffer[1]=0x00; + + dev = (struct usb_ul_usb1 *)candev->sysdevptr.anydev; + + mutex_lock(&dev->io_mutex); + if (!dev) { /* disconnect() was called */ + retval = -ENODEV; + goto exit; + } + if (!dev->interface) { /* disconnect() was called */ + retval = -ENODEV; + goto exit; + } + + /* do a blocking bulk write to send data to the device */ + retval = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + buffer, + 2, + &bytes_transferred, 10000); + + CANMSG("Requested: %ld : retval %d, transferred %d bytes\n",address,retval,bytes_transferred); + if ((retval)||(bytes_transferred!=2)){ + retval = -EFAULT; + goto exit; + } + + /* do a blocking bulk read to get data from the device */ + retval = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + dev->bulk_in_buffer, + dev->bulk_in_size, + &bytes_transferred, 10000); + + /* if the read was successful, copy the data to userspace */ + CANMSG("Received %d bytes : %u:%X\n",bytes_transferred,(dev->bulk_in_buffer[0] & 0x7F),dev->bulk_in_buffer[1]); + if (!retval) { + if (bytes_transferred!=2) + retval = -EFAULT; + else + retval = dev->bulk_in_buffer[1]; + } + +exit: + mutex_unlock(&dev->io_mutex); + return retval; +} + +/* !!! Don't change this function !!! */ +int ul_usb1_register(struct hwspecops_t *hwspecops) +{ + hwspecops->request_io = ul_usb1_request_io; + hwspecops->release_io = ul_usb1_release_io; + hwspecops->reset = ul_usb1_reset; + hwspecops->init_hw_data = ul_usb1_init_hw_data; + hwspecops->init_chip_data = ul_usb1_init_chip_data; + hwspecops->init_obj_data = ul_usb1_init_obj_data; + hwspecops->write_register = ul_usb1_write_register; + hwspecops->read_register = ul_usb1_read_register; + hwspecops->program_irq = ul_usb1_program_irq; + return 0; +} + + + + +/* --------------------------------------------------------------------------------------------------- */ + + +static void ul_usb1_irq(struct urb *urb) +{ + struct usb_ul_usb1 *dev = urb->context; + struct ul_usb1_combo devc; + int retval; + + CANMSG("Interrupt poll\n"); + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + CANMSG("%s - urb shutting down with status: %d\n", __FUNCTION__, urb->status); + return; + default: + CANMSG("%s - nonzero urb status received: %d\n", __FUNCTION__, urb->status); + goto exit; + } + + devc.dev = dev; + devc.urb = urb; + + dev->candev->chip[0]->chipspecops->irq_handler(0,dev->candev->chip[0]); + CANMSG("Interrupt caught\n"); + + exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + CANMSG("%s - usb_submit_urb failed with result %d\n", + __FUNCTION__, retval); +} + +static void ul_usb1_delete(struct usb_ul_usb1 *dev) +{ + usb_put_dev(dev->udev); + usb_kill_urb(dev->irq); + usb_free_urb(dev->irq); + kfree(dev->bulk_in_buffer); + kfree(dev->int_in_buffer); + if (dev->candev){ + dev->candev->sysdevptr.anydev=NULL; + cleanup_usbdev(dev->candev); + } + kfree(dev); +} + +static int ul_usb1_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_ul_usb1 *dev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct candevice_t *candev; + size_t buffer_size; + int i; + int retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + err("Out of memory"); + goto error; + } + sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); + mutex_init(&dev->io_mutex); + spin_lock_init(&dev->err_lock); + init_usb_anchor(&dev->submitted); + +// dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->udev = interface_to_usbdev(interface); + dev->interface = interface; + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpointAddr && + usb_endpoint_is_bulk_in(endpoint)) { + /* we found a bulk in endpoint */ + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!dev->bulk_in_buffer) { + err("Could not allocate bulk_in_buffer"); + goto error; + } + } + + if (!dev->bulk_out_endpointAddr && + usb_endpoint_is_bulk_out(endpoint)) { + /* we found a bulk out endpoint */ + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; + } + + if (!dev->int_in_endpointAddr && + usb_endpoint_is_int_in(endpoint)) { + /* we found an interrupt in endpoint */ + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->int_in_size = buffer_size; + dev->int_in_endpointAddr = endpoint->bEndpointAddress; + dev->int_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + dev->int_in_interval = endpoint->bInterval; + if (!dev->int_in_buffer) { + err("Could not allocate int_in_buffer"); + goto error; + } + } + } + if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr && dev->int_in_endpointAddr)) { + err("Could not find all bulk-in, bulk-out and interrupt endpoints"); + goto error; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + if (main_init_done==1) + register_usbdev("ul_usb1",(void *) dev); + else { + mutex_lock(&usbdev_reg_mutex); + if (main_init_done==1) + register_usbdev("ul_usb1",(void *) dev); + else { + for (i=0;ihwname,"ul_usb1"); + usbregq[i]->anydev=(void *) dev; + break; + } + } + if (i==MAX_HW_CARDS){ + CANMSG("No free space to register new card"); + mutex_unlock(&usbdev_reg_mutex); + goto error; + } + } + mutex_unlock(&usbdev_reg_mutex); + } + + dev->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->irq){ + CANMSG("Error allocating usb urb\n"); + goto error; + } + dev->irq->dev = dev->udev; + usb_fill_int_urb(dev->irq, dev->udev, + usb_rcvintpipe(dev->udev, dev->int_in_endpointAddr), + dev->int_in_buffer, dev->int_in_size, + ul_usb1_irq, dev, dev->int_in_interval); +/* usb_fill_bulk_urb(dev->irq, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + dev->int_in_buffer, dev->int_in_size, + ul_usb1_irq, dev);*/ + +/* dev->irq->transfer_dma = wacom->data_dma; + dev->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;*/ + retval=usb_submit_urb(dev->irq, GFP_KERNEL); + if (retval){ + CANMSG("INT URB %d\n",retval); + return -EIO; + }else + CANMSG("INT URB SUCCCESS\n"); + + /* let the user know what node this device is now attached to */ + info("USB Skeleton device now attached"); + return 0; + +error: + ul_usb1_delete(dev); + return retval; +} + +static void ul_usb1_disconnect(struct usb_interface *interface) +{ + struct usb_ul_usb1 *dev; + int minor = interface->minor; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* prevent more I/O from starting */ + mutex_lock(&dev->io_mutex); + dev->interface = NULL; + mutex_unlock(&dev->io_mutex); + + //usb_kill_anchored_urbs(&dev->submitted); + + ul_usb1_delete(dev); + + info("USB Skeleton now disconnected"); +} + +// static struct usb_driver ul_usb1_driver = { +// .name = "ul_usb1-can", +// .id_table = ul_usb1_table, +// .probe = ul_usb1_probe, +// .disconnect = ul_usb1_disconnect, +// .id_table = ul_usb1_table, +// }; + +// int ul_usb1_init(void){ +// return usb_register(&ul_usb1_driver); +// } +// +// void ul_usb1_exit(void){ +// usb_deregister(&ul_usb1_driver); +// } -- 2.39.2