X-Git-Url: http://developer.intra2net.com/git/?p=libftdi;a=blobdiff_plain;f=src%2Fftdi.c;h=4b53ac310828cc4dc6e4ba4ebfe0171f4e029247;hp=8399030cb0c8ac6f0b2e1f690d687a472933e235;hb=4c9e381219d807ff74556eb35cae1c90986242bc;hpb=ad397a4bb9a5c258c8655e30f6856c0103d49a9e diff --git a/src/ftdi.c b/src/ftdi.c index 8399030..4b53ac3 100644 --- a/src/ftdi.c +++ b/src/ftdi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "ftdi.h" @@ -158,6 +159,7 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli ftdi_error_return(-2, "usb_find_devices() failed"); curdev = devlist; + *curdev = NULL; for (bus = usb_busses; bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if (dev->descriptor.idVendor == vendor @@ -186,13 +188,77 @@ int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devli */ void ftdi_list_free(struct ftdi_device_list **devlist) { - struct ftdi_device_list **curdev; - for (; *devlist == NULL; devlist = curdev) { - curdev = &(*devlist)->next; - free(*devlist); + struct ftdi_device_list *curdev, *next; + + for (curdev = *devlist; curdev != NULL;) { + next = curdev->next; + free(curdev); + curdev = next; + } + + *devlist = NULL; +} + +/** + Return device ID strings from the usb device. + + The parameters manufacturer, description and serial may be NULL + or pointer to buffers to store the fetched strings. + + \note Use this function only in combination with ftdi_usb_find_all() + as it closes the internal "usb_dev" after use. + + \param ftdi pointer to ftdi_context + \param dev libusb usb_dev to use + \param manufacturer Store manufacturer string here if not NULL + \param mnf_len Buffer size of manufacturer string + \param description Store product description string here if not NULL + \param desc_len Buffer size of product description string + \param serial Store serial string here if not NULL + \param serial_len Buffer size of serial string + + \retval 0: all fine + \retval -1: wrong arguments + \retval -4: unable to open device + \retval -7: get product manufacturer failed + \retval -8: get product description failed + \retval -9: get serial number failed + \retval -10: unable to close device +*/ +int ftdi_usb_get_strings(struct ftdi_context * ftdi, struct usb_device * dev, + char * manufacturer, int mnf_len, char * description, int desc_len, char * serial, int serial_len) +{ + if ((ftdi==NULL) || (dev==NULL)) + return -1; + + if (!(ftdi->usb_dev = usb_open(dev))) + ftdi_error_return(-4, usb_strerror()); + + if (manufacturer != NULL) { + if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iManufacturer, manufacturer, mnf_len) <= 0) { + usb_close (ftdi->usb_dev); + ftdi_error_return(-7, usb_strerror()); + } + } + + if (description != NULL) { + if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iProduct, description, desc_len) <= 0) { + usb_close (ftdi->usb_dev); + ftdi_error_return(-8, usb_strerror()); + } + } + + if (serial != NULL) { + if (usb_get_string_simple(ftdi->usb_dev, dev->descriptor.iSerialNumber, serial, serial_len) <= 0) { + usb_close (ftdi->usb_dev); + ftdi_error_return(-9, usb_strerror()); + } } - devlist = NULL; + if (usb_close (ftdi->usb_dev) != 0) + ftdi_error_return(-10, usb_strerror()); + + return 0; } /** @@ -250,7 +316,8 @@ int ftdi_usb_open_dev(struct ftdi_context *ftdi, struct usb_device *dev) ftdi->type = TYPE_2232C; if (!ftdi->index) ftdi->index = INTERFACE_A; - } + } else if (dev->descriptor.bcdDevice == 0x600) + ftdi->type = TYPE_R; ftdi_error_return(0, "all fine"); } @@ -637,6 +704,122 @@ int ftdi_write_data(struct ftdi_context *ftdi, unsigned char *buf, int size) return total_written; } +/* these structs are stolen from libusb linux implementation + they are needed to directly access usbfs and offer async + writing */ + +struct usb_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +struct usb_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + int number_of_packets; + int error_count; + unsigned int signr; /* signal to be sent on error, -1 if none should be sent */ + void *usercontext; + struct usb_iso_packet_desc iso_frame_desc[0]; +}; + +/* this is strongly dependent on libusb using the same struct layout. If libusb + changes in some later version this may break horribly (this is for libusb 0.1.12) */ +struct usb_dev_handle { + int fd; + // some other stuff coming here we don't need +}; + +// some defines for direct usb access, taken from libusb +#define MAX_READ_WRITE (16 * 1024) +#define IOCTL_USB_SUBMITURB _IOR('U', 10, struct usb_urb) +#define USB_URB_TYPE_BULK 3 + +/** + Stupid libusb does not offer async writes nor does it allow + access to its fd - so we need some hacks here. +*/ +static int usb_bulk_write_async(usb_dev_handle *dev, int ep, char *bytes, int size) +{ + struct usb_urb urb; + int bytesdone = 0, requested; + struct usb_urb *context; + int ret, waiting; + + do { + fd_set writefds; + + requested = size - bytesdone; + if (requested > MAX_READ_WRITE) + requested = MAX_READ_WRITE; + + urb.type = USB_URB_TYPE_BULK; + urb.endpoint = ep; + urb.flags = 0; + urb.buffer = bytes + bytesdone; + urb.buffer_length = requested; + urb.signr = 0; + urb.actual_length = 0; + urb.number_of_packets = 0; /* don't do isochronous yet */ + urb.usercontext = (void*)1000; /* use something else than libusb... */ + + ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb); + if (ret < 0) + return ret; /* the caller can read errno to get more info */ + + bytesdone += requested; + } while (bytesdone < size); + return bytesdone; +} + +/** + Writes data in chunks (see ftdi_write_data_set_chunksize()) to the chip. + Does not wait for completion of the transfer nor does it make sure that + the transfer was successful. + + This function could be extended to use signals and callbacks to inform the + caller of completion or error - but this is not done yet, volunteers welcome. + + Works around libusb and directly accesses functions only available on Linux. + + \param ftdi pointer to ftdi_context + \param buf Buffer with the data + \param size Size of the buffer + + \retval <0: error code from usb_bulk_write() + \retval >0: number of bytes written +*/ +int ftdi_write_data_async(struct ftdi_context *ftdi, unsigned char *buf, int size) +{ + int ret; + int offset = 0; + int total_written = 0; + + while (offset < size) { + int write_size = ftdi->writebuffer_chunksize; + + if (offset+write_size > size) + write_size = size-offset; + + ret = usb_bulk_write_async(ftdi->usb_dev, ftdi->in_ep, buf+offset, write_size); + if (ret < 0) + ftdi_error_return(ret, "usb bulk write async failed"); + + total_written += ret; + offset += write_size; + } + + return total_written; +} + + /** Configure write buffer chunk size. Default is 4096. @@ -676,6 +859,7 @@ int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunk \param size Size of the buffer \retval <0: error code from usb_bulk_read() + \retval 0: no data was available \retval >0: number of bytes read \remark This function is not useful in bitbang mode. @@ -1161,6 +1345,53 @@ int ftdi_read_eeprom(struct ftdi_context *ftdi, unsigned char *eeprom) return 0; } +/* + ftdi_read_chipid_shift does the bitshift operation needed for the FTDIChip-ID + Function is only used internally + \internal +*/ +static unsigned char ftdi_read_chipid_shift(unsigned char value) +{ + return ((value & 1) << 1) | + ((value & 2) << 5) | + ((value & 4) >> 2) | + ((value & 8) << 4) | + ((value & 16) >> 1) | + ((value & 32) >> 1) | + ((value & 64) >> 4) | + ((value & 128) >> 2); +} + +/** + Read the FTDIChip-ID from R-type devices + + \param ftdi pointer to ftdi_context + \param chipid Pointer to store FTDIChip-ID + + \retval 0: all fine + \retval -1: read failed +*/ +int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid) +{ + unsigned int a = 0, b = 0; + + if (usb_control_msg(ftdi->usb_dev, 0xC0, 0x90, 0, 0x43, (char *)&a, 2, ftdi->usb_read_timeout) == 2) + { + a = a << 8 | a >> 8; + if (usb_control_msg(ftdi->usb_dev, 0xC0, 0x90, 0, 0x44, (char *)&b, 2, ftdi->usb_read_timeout) == 2) + { + b = b << 8 | b >> 8; + a = (a << 16) | b; + a = ftdi_read_chipid_shift(a) | ftdi_read_chipid_shift(a>>8)<<8 + | ftdi_read_chipid_shift(a>>16)<<16 | ftdi_read_chipid_shift(a>>24)<<24; + *chipid = a ^ 0xa5f0f7d1; + return 0; + } + } + + ftdi_error_return(-1, "read of FTDIChip-ID failed"); +} + /** Write eeprom