X-Git-Url: https://rtime.felk.cvut.cz/gitweb/can-usb1.git/blobdiff_plain/5b247e07d0ab3a1613515bc99b6dd1cb859de7fb..47dc030b5528dc04740fcce09e101d0b6e10c0a3:/ulan/embedded/app/usbcan/ul_usb1.c diff --git a/ulan/embedded/app/usbcan/ul_usb1.c b/ulan/embedded/app/usbcan/ul_usb1.c new file mode 100644 index 0000000..b777b14 --- /dev/null +++ b/ulan/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); +// }