/* Turbo BDM Light ColdFire Copyright (C) 2006 Daniel Malik Changed to support the BDM project. Chris Johns (cjohns@user.sourgeforge.net) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "log.h" #include "tblcf_usb.h" #include "tblcf_hwdesc.h" #include "usb.h" /* * Manage the USB devices we have connected. */ typedef struct tblcf_usb_dev_s { struct usb_bus *bus; struct usb_device *device; usb_dev_handle *handle; char *name; } tblcf_usb_dev; /* * The lits of devices. */ static tblcf_usb_dev *usb_devs; static int usb_dev_count; /* provides low level USB functions which talk to the hardware */ /* initialisation */ void tblcf_usb_init(void) { usb_init(); /* init LIBUSB */ usb_set_debug(0); /* set debug level to minimum */ } /* find all TBLCF devices attached to the computer */ void tblcf_usb_find_devices(unsigned short int product_id) { struct usb_bus *libusb_bus; struct usb_device *libusb_dev; if (usb_devs) return; usb_dev_count = 0; usb_find_busses(); /* enumerate all busses */ usb_find_devices(); /* enumerate all devices */ /* scan through all busses then devices counting the number found */ for (libusb_bus = usb_get_busses(); libusb_bus; libusb_bus = libusb_bus->next) { /* scan through all devices */ for (libusb_dev = libusb_bus->devices; libusb_dev; libusb_dev = libusb_dev->next) { if ((libusb_dev->descriptor.idVendor==TBLCF_VID) && (libusb_dev->descriptor.idProduct==product_id)) { /* found a device */ usb_dev_count++; } } } usb_devs = calloc (usb_dev_count, sizeof (tblcf_usb_dev)); if (!usb_devs) return; usb_dev_count = 0; /* scan through all busses and devices adding each one */ for (libusb_bus = usb_get_busses(); libusb_bus; libusb_bus = libusb_bus->next) { /* scan through all devices */ for (libusb_dev = libusb_bus->devices; libusb_dev; libusb_dev = libusb_dev->next) { if ((libusb_dev->descriptor.idVendor==TBLCF_VID) && (libusb_dev->descriptor.idProduct==product_id)) { /* found a device */ usb_devs[usb_dev_count].bus = libusb_bus; usb_devs[usb_dev_count].device = libusb_dev; usb_devs[usb_dev_count].name = malloc (strlen (libusb_bus->dirname) + strlen (libusb_dev->filename) + 2); if (!usb_devs[usb_dev_count].name) usb_devs[usb_dev_count].name = "no memory for name"; else { strcpy (usb_devs[usb_dev_count].name, libusb_bus->dirname); strcat (usb_devs[usb_dev_count].name, "-"); strcat (usb_devs[usb_dev_count].name, libusb_dev->filename); } usb_dev_count++; } } } } /* returns number of attached TBDML devices */ unsigned int tblcf_usb_cnt(void) { return usb_dev_count; } /* returns number of attached TBDML devices */ void tblcf_usb_dev_name(int dev, char *name, int namelen) { if (usb_devs && (dev < tblcf_usb_cnt ())) strncpy (name, usb_devs[dev].name, namelen - 1); else strncpy (name, "invalid device", namelen - 1); name[namelen - 1] = '\0'; } int tblcf_usb_dev_open(int dev) { return (dev < tblcf_usb_cnt ()) && usb_devs[dev].handle; } /* open connection to device enumerated by tblcf_usb_find_devices */ /* returns the device number on success and -1 on error */ int tblcf_usb_open(const char *device) { int dev; for (dev = 0; dev < usb_dev_count; dev++) if (strcmp (device, usb_devs[dev].name) == 0) break; if (dev == usb_dev_count) { tblcf_print("USB Device '%s' not found\n", device); return -1; } if (usb_devs[dev].handle) { tblcf_print("USB Device '%s' alread open\n", device); return -1; } usb_devs[dev].handle = usb_open(usb_devs[dev].device); if (usb_devs[dev].handle==NULL) return -1; tblcf_print("USB Device open\n"); /* TBLCF has only one valid configuration */ if (usb_set_configuration(usb_devs[dev].handle,1)) return -1; tblcf_print("USB Configuration set\n"); /* TBLCF has only 1 interface */ if (usb_claim_interface(usb_devs[dev].handle,0)) return -1; tblcf_print("USB Interface claimed\n"); return dev; } /* closes connection to the currently open device */ void tblcf_usb_close(int dev) { if (tblcf_usb_dev_open(dev)) { /* release the interface */ usb_release_interface(usb_devs[dev].handle,0); tblcf_print("USB Interface released\n"); /* close the device */ usb_close(usb_devs[dev].handle); tblcf_print("USB Device closed\n"); /* indicate that no device is open */ usb_devs[dev].handle=NULL; } } /* Message data format: 1 byte: size of cmd+data 1 byte: cmd (size-1) bytes: data */ /* sends a message to the TBDML device over EP0 */ /* returns 0 on success and 1 on error */ /* since the EP0 transfer is unidirectional in this case, data returned by the * device must be read separately */ unsigned char tblcf_usb_send_ep0(int dev, unsigned char * data) { unsigned char * count = data; /* data count is the first byte of the message */ int i; if (!tblcf_usb_dev_open(dev)) { tblcf_print("USB EP0 send: device not open\n"); return(1); } tblcf_print("USB EP0 send:\n"); tblcf_print_dump(data,(*count)+1); i=usb_control_msg(usb_devs[dev].handle, 0x40, *(data+1), (*(data+2))+256*(*(data+3)), (*(data+4))+256*(*(data+5)), (char*) data+6, ((*count)>5)?((*count)-5):0, TIMEOUT); if (i<0) return(1); else return(0); } /* sends a message to the TBDML device over EP0 which instruct the device to * exec command and return data */ /* returns 0 on success and 1 on error */ /* data count in the message is number of bytes EXPECTED/REQUIRED from the device */ /* the device will get the cmd number and 4 following data bytes */ unsigned char tblcf_usb_recv_ep0(int dev, unsigned char * data) { unsigned char count = *data; /* data count is the first byte of the message */ int i; if (!tblcf_usb_dev_open(dev)) { tblcf_print("USB EP0 receive request: device not open\n"); return(1); } tblcf_print("USB EP0 receive request:\n"); tblcf_print_dump(data,6); i=usb_control_msg(usb_devs[dev].handle, 0xC0, *(data+1), (*(data+2))+256*(*(data+3)), (*(data+4))+256*(*(data+5)), (char*) data, count, TIMEOUT); tblcf_print("USB EP0 receive:\n"); tblcf_print_dump(data,count); if (i<0) return(1); else return(0); } /* sends a message to the TBDML device over EP2 */ /* returns 0 on success and 1 on error */ /* data the device wants to return need to be read out in separate recv transaction */ unsigned char tblcf_usb_send_ep2(int dev, unsigned char *data) { unsigned char * count = data; /* data count is the first byte of the message */ int i; if (!tblcf_usb_dev_open(dev)) { tblcf_print("USB EP2 send: device not open\n"); return(1); } tblcf_print("USB EP2 send:\n"); tblcf_print_dump(data,(*count)+1); i=usb_bulk_write(usb_devs[dev].handle, 0x02, (char*) data, (*count)+1, TIMEOUT); if (i<0) return(1); else return(0); } /* receives data from EP2 */ /* returns 0 on success and 1 on error */ /* data count in the message is number of bytes EXPECTED/REQUIRED from the device */ unsigned char tblcf_usb_recv_ep2(int dev, unsigned char * data) { unsigned char count = *data; /* data count is the first byte of the message */ int i; if (!tblcf_usb_dev_open(dev)) { tblcf_print("USB EP2 receive request: device not open\n"); return(1); } tblcf_print("USB EP2 receive request:\n"); tblcf_print_dump(data,6); i=usb_bulk_read(usb_devs[dev].handle, 0x82, (char*) data, count, TIMEOUT); tblcf_print("USB EP2 receive (%d byte(s)):\n",i); tblcf_print_dump(data,count); if (i<0) return(1); else return(0); } /* routines to interact with the ICP code on JB16 */ /* programs given number of bytes into flash of the device from given address */ /* returns 0 on succes, -1 on USB error and 1 on compare error */ char icp_program(int dev, unsigned char * data, unsigned int address, unsigned int count) { int i; unsigned char result; if (!tblcf_usb_dev_open(dev)) { tblcf_print("ICP program: device not open\n"); return(-1); } while (count>ICP_MAX_PACKET_SIZE) { tblcf_print("ICP program %d byte from address 0x%04X:\n",ICP_MAX_PACKET_SIZE,address); tblcf_print_dump(data,ICP_MAX_PACKET_SIZE); i=usb_control_msg(usb_devs[dev].handle, 0x40, ICP_PROGRAM, address, address+ICP_MAX_PACKET_SIZE-1, (char*) data, ICP_MAX_PACKET_SIZE, TIMEOUT); if (i<0) { tblcf_print("ICP program: request failed\n"); return(-1); } usleep(10 * 1000 * 1000); /* give the part plenty of time to do the programming */ do { i=usb_control_msg(usb_devs[dev].handle, 0xC0, ICP_GET_RESULT, 0, 0, (char*) &result, 1, TIMEOUT); if (i<0) { tblcf_print("ICP program: get result request failed\n"); return(-1); } } while (result==ICP_RESULT_BUSY); /* keep checking the result while busy */ if (result!=ICP_RESULT_OK) { tblcf_print("ICP programming error (%02X)\n",result); return(1); } count-=ICP_MAX_PACKET_SIZE; address+=ICP_MAX_PACKET_SIZE; data+=ICP_MAX_PACKET_SIZE; } if (count) { tblcf_print("ICP program %d byte(s) from address 0x%04X:\n",count,address); tblcf_print_dump(data,count); i=usb_control_msg(usb_devs[dev].handle, 0x40, ICP_PROGRAM, address, address+count-1, (char*) data, count, TIMEOUT); if (i<0) { tblcf_print("ICP program: request failed\n"); return(-1); } usleep(10 * 1000 * 1000); /* give the part plenty of time to do the programming */ do { i=usb_control_msg(usb_devs[dev].handle, 0xC0, ICP_GET_RESULT, 0, 0, (char*) &result, 1, TIMEOUT); if (i<0) { tblcf_print("ICP program: get result request failed\n"); return(-1); } } while (result==ICP_RESULT_BUSY); /* keep checking the result while busy */ if (result!=ICP_RESULT_OK) { tblcf_print("ICP programming error (%02X)\n",result); return(1); } } return(0); } /* performs mass erase of device flash */ /* returns 0 on success , -1 on USB error and 1 on operation error */ char icp_mass_erase(int dev) { int i; unsigned char result; if (!tblcf_usb_dev_open(dev)) { tblcf_print("ICP mass erase: device not open\n"); return(-1); } tblcf_print("ICP mass erase\n"); i=usb_control_msg(usb_devs[dev].handle, 0x40, ICP_MASS_ERASE, 0, 0, NULL, 0, TIMEOUT); if (i<0) { tblcf_print("ICP mass erase request failed\n"); return(-1); } /* wait 250ms (time of mass erase) + lots of extra to make sure the operation * is finished by the time new traffic apears on the USB bus */ usleep(400 * 1000); do { i=usb_control_msg(usb_devs[dev].handle, 0xC0, ICP_GET_RESULT, 0, 0, (char*) &result, 1, TIMEOUT); if (i<0) { tblcf_print("ICP mass erase: get result request failed\n"); return(-1); } } while (result==ICP_RESULT_BUSY); /* keep checking the result while busy */ if (result!=ICP_RESULT_OK) { tblcf_print("ICP mass erase error (0x%02X)\n",result); return(1); } return(0); } /* performs block erase on block containing given address */ /* returns 0 on success, -1 on USB error and 1 on operation error */ char icp_block_erase(int dev, unsigned int address) { int i; unsigned char result; unsigned char blank_test_array[ICP_FLASH_BLOCK_SIZE]; if (!tblcf_usb_dev_open(dev)) { tblcf_print("ICP block erase: device not open\n"); return(-1); } /* make sure the address is only 16 bit and align it to the closest lower * block boundary */ address&=(65536-ICP_FLASH_BLOCK_SIZE); tblcf_print("ICP block erase\n"); for (i=0;iICP_MAX_PACKET_SIZE) { tblcf_print("ICP verify %d bytes from address 0x%04X:\n",ICP_MAX_PACKET_SIZE,address); tblcf_print_dump(data,ICP_MAX_PACKET_SIZE); i=usb_control_msg(usb_devs[dev].handle, 0x40, ICP_VERIFY, address, address+ICP_MAX_PACKET_SIZE-1, (char*) data, ICP_MAX_PACKET_SIZE, TIMEOUT); if (i<0) { tblcf_print("ICP verify request failed\n"); return(-1); } /* wait to make sure the operation is finished by the time new traffic * apears on the USB bus */ usleep(5 * 1000); do { i=usb_control_msg(usb_devs[dev].handle, 0xC0, ICP_GET_RESULT, 0, 0, (char*) &result, 1, TIMEOUT); if (i<0) { tblcf_print("ICP verify: get result request failed\n"); return(-1); } } while (result==ICP_RESULT_BUSY); /* keep checking the result while busy */ if (result!=ICP_RESULT_OK) { tblcf_print("ICP verification error\n"); return(1); } count-=ICP_MAX_PACKET_SIZE; address+=ICP_MAX_PACKET_SIZE; data+=ICP_MAX_PACKET_SIZE; } if (count) { tblcf_print("ICP verify %d byte(s) from address 0x%04X:\n",count,address); tblcf_print_dump(data,count); i=usb_control_msg(usb_devs[dev].handle, 0x40, ICP_VERIFY, address, address+count-1, (char*) data, count, TIMEOUT); if (i<0) { tblcf_print("ICP verify request failed\n"); return(-1); } /* wait to make sure the operation is finished by the time new traffic * apears on the USB bus */ usleep(5 * 1000); do { i=usb_control_msg(usb_devs[dev].handle, 0xC0, ICP_GET_RESULT, 0, 0, (char*) &result, 1, TIMEOUT); if (i<0) { tblcf_print("ICP verify: get result request failed\n"); return(-1); } } while (result==ICP_RESULT_BUSY); /* keep checking the result while busy */ if (result!=ICP_RESULT_OK) { tblcf_print("ICP verification error\n"); return(1); } } return(0); }