├── .gitignore ├── 51-ft60x.rules ├── Makefile ├── README.md ├── ft60x.c ├── ftconfig.c ├── ftd3xx.h └── loopback.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.ko 2 | *.o 3 | *.cmd 4 | *.mod 5 | *.mod.c 6 | *.symvers 7 | *.order 8 | -------------------------------------------------------------------------------- /51-ft60x.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="usbmisc", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="601f", MODE="0666", GROUP="plugdev" 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for kernel module 2 | 3 | KERNEL_VERSION:=$(shell uname -r) 4 | KERNEL_PATH:=/lib/modules/$(KERNEL_VERSION)/build 5 | 6 | obj-m = ft60x.o 7 | sdr-objs = ft60x.o 8 | 9 | all: ft60x.ko 10 | 11 | ft60x.ko: ft60x.c 12 | make -C $(KERNEL_PATH) M=$(PWD) modules 13 | 14 | clean: 15 | make -C $(KERNEL_PATH) M=$(PWD) clean 16 | rm -f *~ 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ft60x_driver 2 | 3 | #### Build 4 | ``` 5 | make 6 | ``` 7 | 8 | #### Load 9 | ``` 10 | sudo insmod ft60x.ko 11 | ``` 12 | 13 | #### Example 14 | With Default FT60x configuration: 15 | * FIFO Mode 245 16 | * 1 Channel 17 | 18 | ``` 19 | $ dmesg 20 | 21 | [ 9462.813651] usb 2-1: new SuperSpeed Gen 1 USB device number 2 using xhci_hcd 22 | [ 9462.831246] usb 2-1: New USB device found, idVendor=0403, idProduct=601f, bcdDevice= 0.00 23 | [ 9462.831254] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 24 | [ 9462.831258] usb 2-1: Product: FTDI SuperSpeed-FIFO Bridge 25 | [ 9462.831262] usb 2-1: Manufacturer: FTDI 26 | [ 9462.831266] usb 2-1: SerialNumber: 000000000001 27 | [ 9462.835717] FT60x i/f 0 now probed: (0403:601F) 28 | [ 9462.835721] ID->bNumEndpoints: 02 29 | [ 9462.835723] ID->bInterfaceClass: FF 30 | [ 9462.835725] ED[0]->bEndpointAddress: 0x01 31 | [ 9462.835727] ED[0]->bmAttributes: 0x02 32 | [ 9462.835729] ED[0]->wMaxPacketSize: 0x0400 (1024) 33 | [ 9462.835732] ED[1]->bEndpointAddress: 0x81 34 | [ 9462.835734] ED[1]->bmAttributes: 0x03 35 | [ 9462.835737] ED[1]->wMaxPacketSize: 0x0040 (64) 36 | [ 9462.836316] FT60x i/f 1 now probed: (0403:601F) 37 | [ 9462.836319] ID->bNumEndpoints: 02 38 | [ 9462.836321] ID->bInterfaceClass: FF 39 | [ 9462.836324] ED[0]->bEndpointAddress: 0x02 40 | [ 9462.836326] ED[0]->bmAttributes: 0x02 41 | [ 9462.836331] ED[0]->wMaxPacketSize: 0x0400 (1024) 42 | [ 9462.836334] ED[1]->bEndpointAddress: 0x82 43 | [ 9462.836336] ED[1]->bmAttributes: 0x02 44 | [ 9462.836338] ED[1]->wMaxPacketSize: 0x0400 (1024) 45 | [ 9462.836458] ft60x 2-1:1.1: USB FT60x device now attached to ft60x-0 46 | ``` 47 | 48 | ``` 49 | $ ls /dev/ft60x0 50 | /dev/ft60x0 51 | ``` 52 | -------------------------------------------------------------------------------- /ft60x.c: -------------------------------------------------------------------------------- 1 | /* 2 | * USB FT60x driver - 2.2 3 | * 4 | * Copyright (C) 2018 ramtin@lambdaconcept.com 5 | * po@lambdaconcept.com 6 | * 7 | * This program is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, version 2. 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | /* Define these values to match your devices */ 26 | #define USB_FT60X_VENDOR_ID 0x0403 27 | #define USB_FT60X_PRODUCT_ID 0x601f 28 | 29 | /* table of devices that work with this driver */ 30 | static const struct usb_device_id ft60x_table[] = { 31 | {USB_DEVICE(USB_FT60X_VENDOR_ID, USB_FT60X_PRODUCT_ID)}, 32 | {} /* Terminating entry */ 33 | }; 34 | 35 | MODULE_DEVICE_TABLE(usb, ft60x_table); 36 | 37 | /* Get a minor */ 38 | #ifdef CONFIG_USB_DYNAMIC_MINORS 39 | #define USB_FT60X_MINOR_BASE 0 40 | #else 41 | #define USB_FT60X_MINOR_BASE 192 42 | #endif 43 | 44 | /* our private defines. if this grows any larger, use your own .h file */ 45 | #define MAX_TRANSFER 2048 46 | 47 | #define WRITES_IN_FLIGHT 8 48 | /* arbitrarily chosen */ 49 | 50 | #define FT60X_MAX_IF 5 51 | 52 | struct ft60x_config { 53 | /* Device Descriptor */ 54 | u16 VendorID; 55 | u16 ProductID; 56 | /* String Descriptors */ 57 | u8 StringDescriptors[128]; 58 | /* Configuration Descriptor */ 59 | u8 Reserved; 60 | u8 PowerAttributes; 61 | u16 PowerConsumption; 62 | /* Data Transfer Configuration */ 63 | u8 Reserved2; 64 | u8 FIFOClock; 65 | u8 FIFOMode; 66 | u8 ChannelConfig; 67 | /* Optional Feature Support */ 68 | u16 OptionalFeatureSupport; 69 | u8 BatteryChargingGPIOConfig; 70 | u8 FlashEEPROMDetection; /* Read-only */ 71 | /* MSIO and GPIO Configuration */ 72 | u32 MSIO_Control; 73 | u32 GPIO_Control; 74 | } __attribute__ ((packed)); 75 | 76 | /* Structure to hold all of our device specific stuff */ 77 | struct usb_ft60x { 78 | struct ft60x_device *ft; 79 | 80 | struct usb_device *udev; /* the usb device for this device */ 81 | struct usb_interface *interface; /* the interface for this device */ 82 | struct semaphore limit_sem; /* limiting the number of writes in progress */ 83 | struct usb_anchor submitted; /* in case we need to retract our submissions */ 84 | 85 | struct urb *int_in_urb; /* the urb to read data with */ 86 | size_t int_in_size; /* the size of the receive buffer */ 87 | signed char *int_in_buffer; /* the buffer to receive int data */ 88 | dma_addr_t int_in_data_dma; /* the dma buffer to receive int data */ 89 | wait_queue_head_t int_in_wait; /* to wait for an ongoing read */ 90 | 91 | bool got_int; /* got interrupted */ 92 | bool busy_write; /* for the writing poll */ 93 | bool done_reading; 94 | 95 | struct urb *bulk_in_urb; /* the urb to read data with */ 96 | size_t bulk_in_size; /* the size of the receive buffer */ 97 | size_t bulk_in_filled; /* number of bytes in the buffer */ 98 | size_t bulk_in_copied; /* already copied to user space */ 99 | unsigned char *bulk_in_buffer; /* the buffer to receive data */ 100 | wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ 101 | wait_queue_head_t bulk_out_wait; /* to wait for an ongoing write */ 102 | bool ongoing_read; /* a read is going on */ 103 | 104 | int len_to_read; 105 | bool sent_cmd_read; 106 | bool waiting_int; 107 | 108 | __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ 109 | __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ 110 | __u8 int_in_endpointAddr; /* the address of the int in endpoint */ 111 | int errors; /* the last request tanked */ 112 | spinlock_t err_lock; /* lock for errors */ 113 | struct kref kref; 114 | struct mutex io_mutex; /* synchronize I/O with disconnect */ 115 | }; 116 | #define to_ft60x_dev(d) container_of(d, struct usb_ft60x, kref) 117 | 118 | struct ft60x_ctrlreq { 119 | u32 idx; 120 | u8 pipe; 121 | u8 cmd; 122 | u8 unk1; 123 | u8 unk2; 124 | u32 len; 125 | u32 unk4; 126 | u32 unk5; 127 | } __attribute__ ((packed)); 128 | 129 | struct ft60x_irqresp { 130 | int idx; 131 | short ep; 132 | short len; 133 | int val2; 134 | } __attribute__ ((packed)); 135 | 136 | struct ft60x_device { 137 | struct usb_device *udev; 138 | struct ft60x_config ft60x_cfg; 139 | struct usb_ft60x *ft60x_interface[FT60X_MAX_IF]; 140 | struct ft60x_ctrlreq ctrlreq; 141 | int respidx; 142 | int n_interface; 143 | struct list_head list; 144 | wait_queue_head_t int_in_wait; /* to wait for an ongoing read */ 145 | }; 146 | 147 | static LIST_HEAD(ft60x_list); 148 | 149 | #define to_ft60x_dev(d) container_of(d, struct usb_ft60x, kref) 150 | 151 | static struct usb_driver ft60x_driver; 152 | static void ft60x_draw_down(struct usb_ft60x *dev); 153 | static int ft60x_set_config(struct ft60x_device *ft, 154 | struct ft60x_config *ft60x_cfg); 155 | 156 | static void ft60x_delete(struct kref *kref) 157 | { 158 | struct usb_ft60x *dev = to_ft60x_dev(kref); 159 | 160 | if (dev->bulk_in_urb) { 161 | usb_free_urb(dev->bulk_in_urb); 162 | dev->bulk_in_urb = NULL; 163 | } 164 | if (dev->int_in_urb) { 165 | usb_free_urb(dev->int_in_urb); 166 | dev->int_in_urb = NULL; 167 | } 168 | usb_put_dev(dev->udev); 169 | if (dev->int_in_buffer) { 170 | usb_free_coherent(dev->udev, 171 | dev->int_in_size, 172 | dev->int_in_buffer, dev->int_in_data_dma); 173 | dev->int_in_buffer = NULL; 174 | } 175 | if (dev->bulk_in_buffer) { 176 | kfree(dev->bulk_in_buffer); 177 | dev->bulk_in_buffer = NULL; 178 | } 179 | dev->ft->n_interface--; 180 | if (dev->ft->n_interface == 0) { 181 | /* last interface, so removing device */ 182 | list_del(&dev->ft->list); 183 | kfree(dev->ft); 184 | } 185 | kfree(dev); 186 | 187 | } 188 | 189 | static int ft60x_ctrl_req(struct ft60x_device *ft) 190 | { 191 | int actual_len = 0; 192 | u8 *b = NULL; 193 | int retval = 0; 194 | 195 | //printk(KERN_INFO "IN_FUNCTION %s\n", __func__); 196 | 197 | if (!ft) { 198 | retval = -EINVAL; 199 | goto exit; 200 | } 201 | 202 | b = kmalloc(sizeof(struct ft60x_ctrlreq), GFP_KERNEL); 203 | if (!b) { 204 | retval = -ENOMEM; 205 | printk(KERN_ERR "NOT ENOUGH MEM\n"); 206 | goto exit; 207 | } 208 | 209 | memcpy(b, &ft->ctrlreq, sizeof(struct ft60x_ctrlreq)); 210 | 211 | retval = usb_bulk_msg(ft->udev, 212 | usb_sndbulkpipe(ft->udev, 1), b, 213 | sizeof(struct ft60x_ctrlreq), &actual_len, 1000); 214 | if (retval) { 215 | printk("%s: command bulk message failed: error %d\n", 216 | __func__, retval); 217 | goto exit; 218 | } 219 | 220 | exit: 221 | if (b) { 222 | kfree(b); 223 | } 224 | 225 | return retval; 226 | } 227 | 228 | static int ft60x_open(struct inode *inode, struct file *file) 229 | { 230 | struct usb_ft60x *dev; 231 | struct usb_interface *interface; 232 | int subminor; 233 | int retval = 0; 234 | 235 | subminor = iminor(inode); 236 | 237 | interface = usb_find_interface(&ft60x_driver, subminor); 238 | if (!interface) { 239 | pr_err("%s - error, can't find device for minor %d\n", 240 | __func__, subminor); 241 | retval = -ENODEV; 242 | goto exit; 243 | } 244 | 245 | dev = usb_get_intfdata(interface); 246 | if (!dev) { 247 | retval = -ENODEV; 248 | goto exit; 249 | } 250 | 251 | retval = usb_autopm_get_interface(interface); 252 | if (retval) 253 | goto exit; 254 | 255 | /* increment our usage count for the device */ 256 | kref_get(&dev->kref); 257 | 258 | /* save our object in the file's private structure */ 259 | file->private_data = dev; 260 | 261 | exit: 262 | return retval; 263 | } 264 | 265 | static int ft60x_release(struct inode *inode, struct file *file) 266 | { 267 | struct usb_ft60x *dev; 268 | struct ft60x_device *ft; 269 | 270 | dev = file->private_data; 271 | if (dev == NULL) 272 | return -ENODEV; 273 | 274 | ft = dev->ft; 275 | ft->ctrlreq.idx++; 276 | ft->ctrlreq.pipe = dev->bulk_in_endpointAddr; 277 | ft->ctrlreq.cmd = 0; 278 | ft->ctrlreq.unk1 = 0; 279 | ft->ctrlreq.unk2 = 0; 280 | ft->ctrlreq.len = 0; 281 | ft->ctrlreq.unk4 = 0; 282 | ft->ctrlreq.unk5 = 0; 283 | ft60x_ctrl_req(ft); 284 | 285 | ft->ctrlreq.idx++; 286 | ft->ctrlreq.pipe = dev->bulk_in_endpointAddr; 287 | ft->ctrlreq.cmd = 3; 288 | ft->ctrlreq.unk1 = 0; 289 | ft->ctrlreq.unk2 = 0; 290 | ft->ctrlreq.len = 0; 291 | ft->ctrlreq.unk4 = 0; 292 | ft->ctrlreq.unk5 = 0; 293 | ft60x_ctrl_req(ft); 294 | 295 | /* allow the device to be autosuspended */ 296 | mutex_lock(&dev->io_mutex); 297 | if (dev->interface) 298 | usb_autopm_put_interface(dev->interface); 299 | mutex_unlock(&dev->io_mutex); 300 | 301 | /* decrement the count on our device */ 302 | kref_put(&dev->kref, ft60x_delete); 303 | return 0; 304 | } 305 | 306 | static unsigned int ft60x_poll(struct file *file, poll_table * wait) 307 | { 308 | struct usb_ft60x *dev; 309 | unsigned int mask = 0; 310 | char notif; 311 | 312 | //printk(KERN_INFO "IN_FUNCTION %s\n", __func__); 313 | 314 | dev = file->private_data; 315 | if (!dev->interface) 316 | return POLLERR | POLLHUP; 317 | 318 | notif = (dev->ft->ft60x_cfg.OptionalFeatureSupport >> dev->bulk_out_endpointAddr) & 1; 319 | 320 | poll_wait(file, &dev->bulk_in_wait, wait); 321 | //poll_wait(file, &dev->bulk_out_wait, wait); 322 | poll_wait(file, &dev->int_in_wait, wait); 323 | 324 | /* if no notif, we always can read/write, better not use poll */ 325 | if (!notif) { 326 | mask |= POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; 327 | return mask; 328 | } 329 | 330 | if (((dev->bulk_in_filled - dev->bulk_in_copied) && dev->done_reading) 331 | || dev->got_int) { 332 | dev->got_int = 0; 333 | mask |= POLLIN | POLLRDNORM; 334 | } 335 | if (!dev->busy_write) { 336 | mask |= POLLOUT | POLLWRNORM; 337 | } 338 | return mask; 339 | } 340 | 341 | static int ft60x_flush(struct file *file, fl_owner_t id) 342 | { 343 | struct usb_ft60x *dev; 344 | int res; 345 | 346 | dev = file->private_data; 347 | if (dev == NULL) 348 | return -ENODEV; 349 | 350 | /* wait for io to stop */ 351 | mutex_lock(&dev->io_mutex); 352 | ft60x_draw_down(dev); 353 | 354 | /* read out errors, leave subsequent opens a clean slate */ 355 | spin_lock_irq(&dev->err_lock); 356 | res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0; 357 | dev->errors = 0; 358 | spin_unlock_irq(&dev->err_lock); 359 | 360 | mutex_unlock(&dev->io_mutex); 361 | 362 | return res; 363 | } 364 | 365 | static void ft60x_int_callback(struct urb *urb) 366 | { 367 | int retval; 368 | struct usb_ft60x *dev = urb->context; 369 | struct usb_ft60x *tmpdev; 370 | struct ft60x_device *ft = dev->ft; 371 | struct ft60x_irqresp *irqresp; 372 | int i; 373 | int ongoing_read; 374 | 375 | switch (urb->status) { 376 | case 0: /* success */ 377 | irqresp = (struct ft60x_irqresp *)dev->int_in_buffer; 378 | for (i = 0; i < FT60X_MAX_IF; i++) { 379 | tmpdev = ft->ft60x_interface[i]; 380 | if (tmpdev) { 381 | if (tmpdev->bulk_in_endpointAddr == irqresp->ep) { 382 | tmpdev->got_int = 1; 383 | tmpdev->len_to_read = irqresp->len; 384 | tmpdev->sent_cmd_read = 0; 385 | 386 | spin_lock(&tmpdev->err_lock); 387 | ongoing_read = tmpdev->ongoing_read; 388 | spin_unlock(&tmpdev->err_lock); 389 | 390 | /* we had a read URB going on, have to kill it */ 391 | if (ongoing_read && !tmpdev->waiting_int) { 392 | usb_unlink_urb(tmpdev->bulk_in_urb); 393 | } 394 | if (tmpdev->waiting_int) { 395 | spin_lock(&tmpdev->err_lock); 396 | tmpdev->ongoing_read = 0; 397 | spin_unlock(&tmpdev->err_lock); 398 | tmpdev->waiting_int = 0; 399 | } 400 | wake_up_interruptible(&tmpdev->int_in_wait); 401 | wake_up_interruptible(&tmpdev->bulk_in_wait); 402 | } 403 | } 404 | } 405 | break; 406 | case -ECONNRESET: 407 | case -ENOENT: 408 | case -ESHUTDOWN: 409 | /* this urb is terminated, clean up */ 410 | printk(KERN_INFO "%s - urb shutting down with status: %d\n", 411 | __func__, urb->status); 412 | return; 413 | default: 414 | printk(KERN_INFO "%s - nonzero urb status received: %d\n", 415 | __func__, urb->status); 416 | goto exit; 417 | } 418 | 419 | exit: 420 | retval = usb_submit_urb(urb, GFP_ATOMIC); 421 | if (retval) { 422 | printk(KERN_ERR "%s - usb_submit_urb failed with result: %d\n", 423 | __func__, retval); 424 | } 425 | } 426 | 427 | static void ft60x_read_bulk_callback(struct urb *urb) 428 | { 429 | struct usb_ft60x *dev; 430 | 431 | //printk(KERN_INFO "IN_FUNCTION %s\n", __func__); 432 | 433 | dev = urb->context; 434 | spin_lock(&dev->err_lock); 435 | /* sync/async unlink faults aren't errors */ 436 | if (urb->status) { 437 | if (!(urb->status == -ENOENT || 438 | urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) 439 | dev_err(&dev->interface->dev, 440 | "%s - nonzero write bulk status received: %d\n", 441 | __func__, urb->status); 442 | 443 | dev->errors = urb->status; 444 | } 445 | dev->bulk_in_filled = urb->actual_length; 446 | dev->len_to_read -= urb->actual_length; 447 | dev->ongoing_read = 0; 448 | spin_unlock(&dev->err_lock); 449 | 450 | wake_up_interruptible(&dev->bulk_in_wait); 451 | } 452 | 453 | static int ft60x_send_cmd_read(struct usb_ft60x *dev, size_t count) 454 | { 455 | int retval = 0; 456 | char notif; 457 | 458 | if (!dev) { 459 | retval = -ENODEV; 460 | goto exit; 461 | } 462 | notif = (dev->ft->ft60x_cfg.OptionalFeatureSupport >> dev->bulk_out_endpointAddr) & 1; 463 | dev->ft->ctrlreq.idx++; 464 | dev->ft->ctrlreq.pipe = dev->bulk_in_endpointAddr; 465 | dev->ft->ctrlreq.cmd = 1; 466 | if (notif) { 467 | dev->ft->ctrlreq.len = umin(dev->bulk_in_size * 128, dev->len_to_read); 468 | } else { 469 | dev->ft->ctrlreq.len = dev->bulk_in_size * 128; 470 | } 471 | retval = ft60x_ctrl_req(dev->ft); 472 | dev->sent_cmd_read = 1; 473 | exit: 474 | return retval; 475 | } 476 | 477 | static int ft60x_do_read_io(struct usb_ft60x *dev, size_t count) 478 | { 479 | int rv = 0; 480 | char notif; 481 | 482 | //printk(KERN_INFO "IN_FUNCTION %s\n", __func__); 483 | 484 | /* If we are in notification mode */ 485 | notif = (dev->ft->ft60x_cfg.OptionalFeatureSupport >> dev->bulk_out_endpointAddr) & 1; 486 | if (notif) { 487 | if ((dev->len_to_read > 0) && !dev->sent_cmd_read) { 488 | ft60x_send_cmd_read(dev, count); 489 | } 490 | } else { 491 | ft60x_send_cmd_read(dev, count); 492 | } 493 | 494 | /* Can't read now ! */ 495 | if (notif && (dev->len_to_read <= 0)) { 496 | 497 | spin_lock_irq(&dev->err_lock); 498 | dev->ongoing_read = 1; 499 | dev->waiting_int = 1; 500 | spin_unlock_irq(&dev->err_lock); 501 | return rv; 502 | } 503 | 504 | if (notif) { 505 | usb_fill_bulk_urb(dev->bulk_in_urb, 506 | dev->udev, 507 | usb_rcvbulkpipe(dev->udev, 508 | dev->bulk_in_endpointAddr), 509 | dev->bulk_in_buffer, 510 | umin(dev->bulk_in_size * 128, 511 | dev->len_to_read), 512 | ft60x_read_bulk_callback, dev); 513 | } else { 514 | usb_fill_bulk_urb(dev->bulk_in_urb, 515 | dev->udev, 516 | usb_rcvbulkpipe(dev->udev, 517 | dev->bulk_in_endpointAddr), 518 | dev->bulk_in_buffer, 519 | dev->bulk_in_size * 128, 520 | ft60x_read_bulk_callback, dev); 521 | } 522 | 523 | /* tell everybody to leave the URB alone */ 524 | spin_lock_irq(&dev->err_lock); 525 | dev->ongoing_read = 1; 526 | spin_unlock_irq(&dev->err_lock); 527 | 528 | /* submit bulk in urb, which means no data to deliver */ 529 | dev->bulk_in_filled = 0; 530 | dev->bulk_in_copied = 0; 531 | 532 | /* do it */ 533 | rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL); 534 | 535 | if (rv < 0) { 536 | dev_err(&dev->interface->dev, 537 | "%s - failed submitting read urb, error %d\n", 538 | __func__, rv); 539 | rv = (rv == -ENOMEM) ? rv : -EIO; 540 | spin_lock_irq(&dev->err_lock); 541 | dev->ongoing_read = 0; 542 | spin_unlock_irq(&dev->err_lock); 543 | } 544 | return rv; 545 | } 546 | 547 | static ssize_t ft60x_read(struct file *file, char *buffer, size_t count, 548 | loff_t * ppos) 549 | { 550 | struct usb_ft60x *dev; 551 | int rv; 552 | bool ongoing_io; 553 | 554 | //printk(KERN_INFO "IN_FUNCTION %s\n", __func__); 555 | 556 | dev = file->private_data; 557 | 558 | /* if we cannot read at all, return EOF */ 559 | if (!dev->bulk_in_urb || !count) 560 | return 0; 561 | 562 | /* no concurrent readers */ 563 | rv = mutex_lock_interruptible(&dev->io_mutex); 564 | if (rv < 0) 565 | return rv; 566 | 567 | if (!dev->interface) { /* disconnect() was called */ 568 | rv = -ENODEV; 569 | goto exit; 570 | } 571 | 572 | dev->done_reading = 0; 573 | /* if IO is under way, we must not touch things */ 574 | retry: 575 | spin_lock_irq(&dev->err_lock); 576 | ongoing_io = dev->ongoing_read; 577 | spin_unlock_irq(&dev->err_lock); 578 | 579 | if (ongoing_io) { 580 | /* nonblocking IO shall not wait */ 581 | if (file->f_flags & O_NONBLOCK) { 582 | rv = -EAGAIN; 583 | goto exit; 584 | } 585 | /* 586 | * IO may take forever 587 | * hence wait in an interruptible state 588 | */ 589 | rv = wait_event_interruptible(dev->bulk_in_wait, 590 | (!dev->ongoing_read)); 591 | if (rv < 0) 592 | goto exit; 593 | } 594 | 595 | /* errors must be reported */ 596 | rv = dev->errors; 597 | if (rv == -ECONNRESET) { 598 | dev->errors = 0; 599 | if (!dev->bulk_in_filled) { 600 | rv = ft60x_send_cmd_read(dev, count); 601 | if (rv < 0) 602 | goto exit; 603 | printk(KERN_INFO "FROM here 1 %d \n", 604 | dev->bulk_in_urb->actual_length); 605 | rv = ft60x_do_read_io(dev, count); 606 | if (rv < 0) { 607 | goto exit; 608 | } else 609 | goto retry; 610 | } 611 | } 612 | 613 | if ((rv < 0) && (rv != -ECONNRESET)) { 614 | /* any error is reported once */ 615 | dev->errors = 0; 616 | /* to preserve notifications about reset */ 617 | rv = (rv == -EPIPE) ? rv : -EIO; 618 | /* report it */ 619 | goto exit; 620 | } 621 | 622 | /* 623 | * if the buffer is filled we may satisfy the read 624 | * else we need to start IO 625 | */ 626 | if (dev->bulk_in_filled) { 627 | /* we had read data */ 628 | size_t available = dev->bulk_in_filled - dev->bulk_in_copied; 629 | size_t chunk = min(available, count); 630 | 631 | if (!available) { 632 | /* 633 | * all data has been used 634 | * actual IO needs to be done 635 | */ 636 | rv = ft60x_do_read_io(dev, count); 637 | if (rv < 0) 638 | goto exit; 639 | else 640 | goto retry; 641 | } 642 | 643 | /* 644 | * data is available 645 | * chunk tells us how much shall be copied 646 | */ 647 | if (copy_to_user(buffer, 648 | dev->bulk_in_buffer + dev->bulk_in_copied, 649 | chunk)) 650 | rv = -EFAULT; 651 | else { 652 | rv = chunk; 653 | } 654 | dev->bulk_in_copied += chunk; 655 | 656 | /* 657 | * if we are asked for more than we have, 658 | * we start IO but don't wait 659 | */ 660 | if (available < count) { 661 | ft60x_do_read_io(dev, count - chunk); 662 | } 663 | } else { 664 | /* no data in the buffer */ 665 | rv = ft60x_do_read_io(dev, count); 666 | 667 | if (rv < 0) 668 | goto exit; 669 | else 670 | goto retry; 671 | } 672 | exit: 673 | dev->done_reading = 1; 674 | mutex_unlock(&dev->io_mutex); 675 | return rv; 676 | } 677 | 678 | static void ft60x_write_bulk_callback(struct urb *urb) 679 | { 680 | struct usb_ft60x *dev; 681 | 682 | dev = urb->context; 683 | 684 | /* sync/async unlink faults aren't errors */ 685 | if (urb->status) { 686 | if (!(urb->status == -ENOENT || 687 | urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) 688 | dev_err(&dev->interface->dev, 689 | "%s - nonzero write bulk status received: %d\n", 690 | __func__, urb->status); 691 | 692 | spin_lock(&dev->err_lock); 693 | dev->busy_write = 0; 694 | dev->errors = urb->status; 695 | spin_unlock(&dev->err_lock); 696 | wake_up_interruptible(&dev->bulk_out_wait); 697 | } 698 | 699 | /* free up our allocated buffer */ 700 | usb_free_coherent(urb->dev, urb->transfer_buffer_length, 701 | urb->transfer_buffer, urb->transfer_dma); 702 | up(&dev->limit_sem); 703 | } 704 | 705 | static ssize_t ft60x_write(struct file *file, const char *user_buffer, 706 | size_t count, loff_t * ppos) 707 | { 708 | struct usb_ft60x *dev; 709 | int retval = 0; 710 | struct urb *urb = NULL; 711 | char *buf = NULL; 712 | size_t writesize = min(count, (size_t) MAX_TRANSFER); 713 | 714 | dev = file->private_data; 715 | 716 | /* verify that we actually have some data to write */ 717 | if (count == 0) 718 | goto exit; 719 | 720 | /* 721 | * limit the number of URBs in flight to stop a user from using up all 722 | * RAM 723 | */ 724 | if (!(file->f_flags & O_NONBLOCK)) { 725 | if (down_interruptible(&dev->limit_sem)) { 726 | retval = -ERESTARTSYS; 727 | goto exit; 728 | } 729 | } else { 730 | if (down_trylock(&dev->limit_sem)) { 731 | retval = -EAGAIN; 732 | goto exit; 733 | } 734 | } 735 | 736 | spin_lock_irq(&dev->err_lock); 737 | retval = dev->errors; 738 | if (retval < 0) { 739 | /* any error is reported once */ 740 | dev->errors = 0; 741 | /* to preserve notifications about reset */ 742 | retval = (retval == -EPIPE) ? retval : -EIO; 743 | } 744 | spin_unlock_irq(&dev->err_lock); 745 | if (retval < 0) 746 | goto error; 747 | 748 | /* create a urb, and a buffer for it, and copy the data to the urb */ 749 | urb = usb_alloc_urb(0, GFP_KERNEL); 750 | if (!urb) { 751 | retval = -ENOMEM; 752 | goto error; 753 | } 754 | 755 | buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL, 756 | &urb->transfer_dma); 757 | if (!buf) { 758 | retval = -ENOMEM; 759 | goto error; 760 | } 761 | 762 | if (copy_from_user(buf, user_buffer, writesize)) { 763 | retval = -EFAULT; 764 | goto error; 765 | } 766 | 767 | /* this lock makes sure we don't submit URBs to gone devices */ 768 | mutex_lock(&dev->io_mutex); 769 | if (!dev->interface) { /* disconnect() was called */ 770 | mutex_unlock(&dev->io_mutex); 771 | retval = -ENODEV; 772 | goto error; 773 | } 774 | 775 | /* initialize the urb properly */ 776 | usb_fill_bulk_urb(urb, dev->udev, 777 | usb_sndbulkpipe(dev->udev, 778 | dev->bulk_out_endpointAddr), buf, 779 | writesize, ft60x_write_bulk_callback, dev); 780 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 781 | usb_anchor_urb(urb, &dev->submitted); 782 | 783 | /* send the data out the bulk port */ 784 | retval = usb_submit_urb(urb, GFP_KERNEL); 785 | mutex_unlock(&dev->io_mutex); 786 | if (retval) { 787 | dev_err(&dev->interface->dev, 788 | "%s - failed submitting write urb, error %d\n", 789 | __func__, retval); 790 | goto error_unanchor; 791 | } 792 | spin_lock_irq(&dev->err_lock); 793 | dev->busy_write = 1; 794 | spin_unlock_irq(&dev->err_lock); 795 | 796 | /* 797 | * release our reference to this urb, the USB core will eventually free 798 | * it entirely 799 | */ 800 | usb_free_urb(urb); 801 | 802 | return writesize; 803 | 804 | error_unanchor: 805 | usb_unanchor_urb(urb); 806 | error: 807 | if (urb) { 808 | usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma); 809 | usb_free_urb(urb); 810 | } 811 | up(&dev->limit_sem); 812 | 813 | exit: 814 | return retval; 815 | } 816 | 817 | static long ft60x_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 818 | { 819 | long retval = 0; 820 | struct usb_ft60x *dev; 821 | struct ft60x_config cfg; 822 | 823 | dev = file->private_data; 824 | 825 | if (!dev) { 826 | retval = -ENODEV; 827 | goto exit; 828 | } 829 | switch (cmd) { 830 | case 0: 831 | if (copy_to_user((unsigned char *)arg, 832 | (unsigned char *)&dev->ft->ft60x_cfg, 833 | sizeof(struct ft60x_config))) 834 | retval = -EFAULT; 835 | break; 836 | case 1: 837 | if (copy_from_user 838 | (&cfg, (unsigned char *)arg, sizeof(struct ft60x_config))) { 839 | retval = -EFAULT; 840 | goto exit; 841 | } 842 | ft60x_set_config(dev->ft, &cfg); 843 | break; 844 | default: 845 | printk(KERN_ERR "Unknown IOCTL %d\n", cmd); 846 | } 847 | 848 | exit: 849 | return retval; 850 | } 851 | 852 | static const struct file_operations ft60x_fops = { 853 | .owner = THIS_MODULE, 854 | .read = ft60x_read, 855 | .write = ft60x_write, 856 | .open = ft60x_open, 857 | .release = ft60x_release, 858 | .flush = ft60x_flush, 859 | .poll = ft60x_poll, 860 | .unlocked_ioctl = ft60x_ioctl, 861 | .llseek = noop_llseek, 862 | }; 863 | 864 | /* 865 | * usb class driver info in order to get a minor number from the usb core, 866 | * and to have the device registered with the driver core 867 | */ 868 | static struct usb_class_driver ft60x_class = { 869 | .name = "ft60x%d", 870 | .fops = &ft60x_fops, 871 | .minor_base = USB_FT60X_MINOR_BASE, 872 | }; 873 | 874 | static int ft60x_set_config(struct ft60x_device *ft, 875 | struct ft60x_config *ft60x_cfg) 876 | { 877 | struct ft60x_config *cfg = NULL; 878 | int ret; 879 | 880 | if (!ft || !ft60x_cfg) { 881 | ret = -EINVAL; 882 | goto exit; 883 | } 884 | 885 | /* Make sure we are not sending the same thing because it'd disconnect us */ 886 | if (!memcmp(ft60x_cfg, &ft->ft60x_cfg, sizeof(struct ft60x_config))) { 887 | return 0; 888 | } 889 | 890 | /* Changing configuration */ 891 | cfg = kmalloc(sizeof(struct ft60x_config), GFP_NOIO); 892 | if (!cfg) { 893 | ret = -ENOMEM; 894 | goto exit; 895 | } 896 | 897 | memcpy(cfg, ft60x_cfg, sizeof(struct ft60x_config)); 898 | 899 | ret = usb_control_msg(ft->udev, usb_sndctrlpipe(ft->udev, 0), 900 | 0xcf, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, 901 | cfg, sizeof(struct ft60x_config), 902 | USB_CTRL_SET_TIMEOUT); 903 | 904 | if (ret < 0) { 905 | ret = -EIO; 906 | goto exit; 907 | } 908 | exit: 909 | if (cfg) 910 | kfree(cfg); 911 | 912 | return ret; 913 | } 914 | 915 | static int ft60x_get_config(struct ft60x_device *ft) 916 | { 917 | struct ft60x_config *cfg = NULL; 918 | int retval; 919 | int ret; 920 | 921 | if (!ft) { 922 | retval = -EINVAL; 923 | goto exit; 924 | } 925 | 926 | cfg = kmalloc(sizeof(struct ft60x_config), GFP_NOIO); 927 | if (!cfg) { 928 | retval = -ENOMEM; 929 | goto exit; 930 | } 931 | 932 | ret = usb_control_msg(ft->udev, usb_rcvctrlpipe(ft->udev, 0), 933 | 0xcf, USB_TYPE_VENDOR | USB_DIR_IN, 1, 0, 934 | cfg, sizeof(struct ft60x_config), 935 | USB_CTRL_SET_TIMEOUT); 936 | if (ret < 0) { 937 | retval = -EIO; 938 | goto exit; 939 | } 940 | 941 | if (ret <= sizeof(struct ft60x_config)) { 942 | memcpy(&ft->ft60x_cfg, cfg, ret); 943 | } 944 | 945 | exit: 946 | if (cfg) { 947 | kfree(cfg); 948 | } 949 | return retval; 950 | } 951 | 952 | static int ft60x_get_unknown(struct ft60x_device *ft) 953 | { 954 | struct ft60x_config *cfg = NULL; 955 | int retval; 956 | int ret; 957 | unsigned int *val = NULL; 958 | if (!ft) { 959 | retval = -EINVAL; 960 | goto exit; 961 | } 962 | 963 | val = kmalloc(sizeof(unsigned int), GFP_NOIO); 964 | if (!val) { 965 | retval = -ENOMEM; 966 | goto exit; 967 | } 968 | 969 | ret = usb_control_msg(ft->udev, usb_rcvctrlpipe(ft->udev, 0), 970 | 0xf1, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, 971 | val, 4, USB_CTRL_SET_TIMEOUT); 972 | if (ret < 0) { 973 | retval = -EIO; 974 | printk(KERN_ERR "GOT ERROR1: %d\n", retval); 975 | goto exit; 976 | } 977 | 978 | exit: 979 | if (val) { 980 | kfree(cfg); 981 | } 982 | return retval; 983 | } 984 | 985 | static int ft60x_alloc_data_interface(struct usb_ft60x *dev, 986 | struct usb_host_interface *iface_desc) 987 | { 988 | int i; 989 | size_t buffer_size; 990 | int retval = 0; 991 | struct usb_endpoint_descriptor *endpoint; 992 | 993 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 994 | endpoint = &iface_desc->endpoint[i].desc; 995 | 996 | if (!dev->bulk_in_endpointAddr && 997 | usb_endpoint_is_bulk_in(endpoint)) { 998 | /* we found a bulk in endpoint */ 999 | buffer_size = usb_endpoint_maxp(endpoint); 1000 | dev->bulk_in_size = buffer_size; 1001 | dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; 1002 | dev->bulk_in_buffer = 1003 | kmalloc(buffer_size * 128, GFP_KERNEL); 1004 | if (!dev->bulk_in_buffer) { 1005 | retval = -ENOMEM; 1006 | goto error; 1007 | } 1008 | 1009 | dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL); 1010 | if (!dev->bulk_in_urb) { 1011 | retval = -ENOMEM; 1012 | goto error; 1013 | } 1014 | } 1015 | if (!dev->bulk_out_endpointAddr && 1016 | usb_endpoint_is_bulk_out(endpoint)) { 1017 | /* we found a bulk out endpoint */ 1018 | dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; 1019 | } 1020 | } 1021 | 1022 | error: 1023 | return retval; 1024 | } 1025 | 1026 | static int ft60x_alloc_ctrl_interface(struct usb_ft60x *dev, 1027 | struct usb_host_interface *iface_desc) 1028 | { 1029 | int i; 1030 | size_t buffer_size; 1031 | int retval = 0; 1032 | int maxp, pipe; 1033 | struct usb_endpoint_descriptor *endpoint; 1034 | 1035 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 1036 | endpoint = &iface_desc->endpoint[i].desc; 1037 | 1038 | if (!dev->int_in_endpointAddr && 1039 | usb_endpoint_is_int_in(endpoint)) { 1040 | /* we found a int in endpoint */ 1041 | buffer_size = usb_endpoint_maxp(endpoint); 1042 | dev->int_in_size = buffer_size; 1043 | dev->int_in_endpointAddr = endpoint->bEndpointAddress; 1044 | dev->int_in_buffer = usb_alloc_coherent(dev->udev, 1045 | dev-> 1046 | int_in_size, 1047 | GFP_ATOMIC, 1048 | &dev-> 1049 | int_in_data_dma); 1050 | if (!dev->int_in_buffer) { 1051 | retval = -ENOMEM; 1052 | goto error; 1053 | } 1054 | 1055 | dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); 1056 | if (!dev->int_in_urb) { 1057 | retval = -ENOMEM; 1058 | goto error; 1059 | } 1060 | 1061 | /* get a handle to the interrupt data pipe */ 1062 | pipe = usb_rcvintpipe(dev->udev, 1063 | endpoint->bEndpointAddress); 1064 | maxp = usb_maxpacket(dev->udev, pipe); 1065 | usb_fill_int_urb(dev->int_in_urb, dev->udev, pipe, 1066 | dev->int_in_buffer, maxp, 1067 | ft60x_int_callback, dev, 1068 | endpoint->bInterval); 1069 | dev->int_in_urb->transfer_dma = dev->int_in_data_dma; 1070 | dev->int_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 1071 | /* register our interrupt URB with the USB system */ 1072 | 1073 | if (usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) { 1074 | retval = -EIO; 1075 | goto error; 1076 | } 1077 | } 1078 | 1079 | if (!dev->bulk_out_endpointAddr && 1080 | usb_endpoint_is_bulk_out(endpoint)) { 1081 | /* we found a bulk out endpoint */ 1082 | dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; 1083 | } 1084 | } 1085 | 1086 | error: 1087 | return retval; 1088 | } 1089 | 1090 | static void ft60x_print_usb_log(struct usb_interface *intf, 1091 | const struct usb_device_id *id) 1092 | { 1093 | int i; 1094 | struct usb_host_interface *interface; 1095 | struct usb_endpoint_descriptor *endpoint; 1096 | 1097 | if (!intf || !id) { 1098 | printk(KERN_ERR "Invalid arg\n"); 1099 | return; 1100 | } 1101 | interface = intf->cur_altsetting; 1102 | printk(KERN_INFO "FT60x i/f %d now probed: (%04X:%04X)\n", 1103 | interface->desc.bInterfaceNumber, id->idVendor, id->idProduct); 1104 | printk(KERN_INFO "ID->bNumEndpoints: %02X\n", 1105 | interface->desc.bNumEndpoints); 1106 | printk(KERN_INFO "ID->bInterfaceClass: %02X\n", 1107 | interface->desc.bInterfaceClass); 1108 | 1109 | for (i = 0; i < interface->desc.bNumEndpoints; i++) { 1110 | endpoint = &interface->endpoint[i].desc; 1111 | printk(KERN_INFO "ED[%d]->bEndpointAddress: 0x%02X\n", 1112 | i, endpoint->bEndpointAddress); 1113 | printk(KERN_INFO "ED[%d]->bmAttributes: 0x%02X\n", 1114 | i, endpoint->bmAttributes); 1115 | printk(KERN_INFO "ED[%d]->wMaxPacketSize: 0x%04X (%d)\n", 1116 | i, endpoint->wMaxPacketSize, endpoint->wMaxPacketSize); 1117 | } 1118 | } 1119 | 1120 | static int ft60x_alloc_device(struct usb_device *device, 1121 | struct ft60x_device **ftout) 1122 | { 1123 | struct ft60x_device *ft = NULL; 1124 | 1125 | int retval = 0; 1126 | 1127 | if (!ftout || !device) { 1128 | retval = -EINVAL; 1129 | goto exit; 1130 | } 1131 | 1132 | ft = kzalloc(sizeof(struct ft60x_device), GFP_KERNEL); 1133 | if (!ft) { 1134 | retval = -ENOMEM; 1135 | goto exit; 1136 | } 1137 | ft->udev = device; 1138 | init_waitqueue_head(&ft->int_in_wait); 1139 | list_add_tail(&(ft->list), &(ft60x_list)); 1140 | exit: 1141 | *ftout = ft; 1142 | return retval; 1143 | } 1144 | 1145 | static int ft60x_find_device(struct usb_device *dev, 1146 | struct ft60x_device **ftout) 1147 | { 1148 | struct ft60x_device *ft = NULL; 1149 | int retval = 0; 1150 | 1151 | if (!dev || !ftout) { 1152 | retval = -EINVAL; 1153 | goto exit; 1154 | } 1155 | 1156 | list_for_each_entry(ft, &ft60x_list, list) { 1157 | if (ft->udev == dev) 1158 | goto exit; 1159 | } 1160 | ft = NULL; 1161 | exit: 1162 | *ftout = ft; 1163 | return retval; 1164 | } 1165 | 1166 | static int ft60x_probe(struct usb_interface *interface, 1167 | const struct usb_device_id *id) 1168 | { 1169 | struct usb_ft60x *dev = NULL; 1170 | struct usb_host_interface *iface_desc; 1171 | struct usb_device *device; 1172 | struct ft60x_device *ft = NULL; 1173 | int retval = -ENOMEM; 1174 | int ifn; 1175 | 1176 | device = interface_to_usbdev(interface); 1177 | ft60x_print_usb_log(interface, id); 1178 | 1179 | ft60x_find_device(device, &ft); 1180 | if (!ft) { 1181 | retval = ft60x_alloc_device(device, &ft); 1182 | if (retval) { 1183 | goto error; 1184 | } 1185 | } 1186 | 1187 | /* allocate memory for our device state and initialize it */ 1188 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1189 | if (!dev) 1190 | goto error; 1191 | dev->ft = ft; 1192 | ft->n_interface++; 1193 | kref_init(&dev->kref); 1194 | sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); 1195 | mutex_init(&dev->io_mutex); 1196 | 1197 | spin_lock_init(&dev->err_lock); 1198 | init_usb_anchor(&dev->submitted); 1199 | init_waitqueue_head(&dev->bulk_in_wait); 1200 | init_waitqueue_head(&dev->bulk_out_wait); 1201 | init_waitqueue_head(&dev->int_in_wait); 1202 | 1203 | dev->udev = usb_get_dev(device); 1204 | dev->interface = interface; 1205 | dev->done_reading = 1; 1206 | /* set up the endpoint information */ 1207 | /* use only the first bulk-in and bulk-out endpoints */ 1208 | iface_desc = interface->cur_altsetting; 1209 | 1210 | ifn = iface_desc->desc.bInterfaceNumber; 1211 | ft->ft60x_interface[ifn] = dev; 1212 | 1213 | if (ifn == 0) { 1214 | if ((retval = ft60x_alloc_ctrl_interface(dev, iface_desc)) < 0) 1215 | goto error; 1216 | if (!(dev->int_in_endpointAddr && dev->bulk_out_endpointAddr)) { 1217 | dev_err(&interface->dev, 1218 | "Could not find both int-in \n"); 1219 | goto error; 1220 | } 1221 | ft60x_get_config(ft); 1222 | /* 4 bytes the FTDI gets we don't know what it is (0x00000109) */ 1223 | ft60x_get_unknown(ft); 1224 | } else { 1225 | if ((retval = ft60x_alloc_data_interface(dev, iface_desc)) < 0) 1226 | goto error; 1227 | 1228 | if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { 1229 | dev_err(&interface->dev, 1230 | "Could not find both bulk-in and bulk-out endpoints\n"); 1231 | goto error; 1232 | } 1233 | /* we can register the device now, as it is ready */ 1234 | retval = usb_register_dev(interface, &ft60x_class); 1235 | if (retval) { 1236 | /* something prevented us from registering this driver */ 1237 | dev_err(&interface->dev, 1238 | "Not able to get a minor for this device.\n"); 1239 | usb_set_intfdata(interface, NULL); 1240 | goto error; 1241 | } 1242 | /* let the user know what node this device is now attached to */ 1243 | dev_info(&interface->dev, 1244 | "USB FT60x device now attached to ft60x-%d", 1245 | interface->minor); 1246 | } 1247 | 1248 | /* save our data pointer in this interface device */ 1249 | usb_set_intfdata(interface, dev); 1250 | 1251 | return 0; 1252 | 1253 | error: 1254 | if (dev) 1255 | /* this frees allocated memory */ 1256 | kref_put(&dev->kref, ft60x_delete); 1257 | return retval; 1258 | } 1259 | 1260 | static void ft60x_disconnect(struct usb_interface *interface) 1261 | { 1262 | struct usb_ft60x *dev; 1263 | int minor = interface->minor; 1264 | 1265 | dev = usb_get_intfdata(interface); 1266 | usb_set_intfdata(interface, NULL); 1267 | 1268 | /* give back our minor */ 1269 | usb_deregister_dev(interface, &ft60x_class); 1270 | 1271 | /* prevent more I/O from starting */ 1272 | mutex_lock(&dev->io_mutex); 1273 | dev->interface = NULL; 1274 | mutex_unlock(&dev->io_mutex); 1275 | 1276 | usb_kill_anchored_urbs(&dev->submitted); 1277 | 1278 | /* decrement our usage count */ 1279 | kref_put(&dev->kref, ft60x_delete); 1280 | 1281 | dev_info(&interface->dev, "USB FT60x #%d now disconnected", minor); 1282 | } 1283 | 1284 | static void ft60x_draw_down(struct usb_ft60x *dev) 1285 | { 1286 | int time; 1287 | 1288 | time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); 1289 | if (!time) 1290 | usb_kill_anchored_urbs(&dev->submitted); 1291 | usb_kill_urb(dev->bulk_in_urb); 1292 | } 1293 | 1294 | static int ft60x_suspend(struct usb_interface *intf, pm_message_t message) 1295 | { 1296 | struct usb_ft60x *dev = usb_get_intfdata(intf); 1297 | 1298 | if (!dev) 1299 | return 0; 1300 | ft60x_draw_down(dev); 1301 | return 0; 1302 | } 1303 | 1304 | static int ft60x_resume(struct usb_interface *intf) 1305 | { 1306 | return 0; 1307 | } 1308 | 1309 | static int ft60x_pre_reset(struct usb_interface *intf) 1310 | { 1311 | struct usb_ft60x *dev = usb_get_intfdata(intf); 1312 | 1313 | mutex_lock(&dev->io_mutex); 1314 | ft60x_draw_down(dev); 1315 | 1316 | return 0; 1317 | } 1318 | 1319 | static int ft60x_post_reset(struct usb_interface *intf) 1320 | { 1321 | struct usb_ft60x *dev = usb_get_intfdata(intf); 1322 | 1323 | /* we are sure no URBs are active - no locking needed */ 1324 | dev->errors = -EPIPE; 1325 | mutex_unlock(&dev->io_mutex); 1326 | 1327 | return 0; 1328 | } 1329 | 1330 | static struct usb_driver ft60x_driver = { 1331 | .name = "ft60x", 1332 | .probe = ft60x_probe, 1333 | .disconnect = ft60x_disconnect, 1334 | .suspend = ft60x_suspend, 1335 | .resume = ft60x_resume, 1336 | .pre_reset = ft60x_pre_reset, 1337 | .post_reset = ft60x_post_reset, 1338 | .id_table = ft60x_table, 1339 | .supports_autosuspend = 1, 1340 | }; 1341 | 1342 | module_usb_driver(ft60x_driver); 1343 | 1344 | MODULE_LICENSE("GPL"); 1345 | MODULE_AUTHOR("Ramtin Amin "); 1346 | MODULE_DESCRIPTION("FT60x Driver"); 1347 | -------------------------------------------------------------------------------- /ftconfig.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "ftd3xx.h" 9 | 10 | #define NUM_CPUCLOCK 4 11 | #define NUM_FIFOCLOCK 4 12 | #define NUM_FIFOMODE 2 13 | #define NUM_BCCONFIGURATION 4 14 | #define NUM_CHANNELCONFIG 5 15 | 16 | const char *g_szFIFOClock[NUM_FIFOCLOCK] = { 17 | "100 MHz", 18 | "66 MHz", 19 | "50 MHz", 20 | "40 MHz", 21 | }; 22 | 23 | const char *g_szFIFOMode[NUM_FIFOMODE] = { 24 | "245 Mode", 25 | "600 Mode", 26 | }; 27 | 28 | const char *g_szBCDConfiguration[NUM_BCCONFIGURATION] = { 29 | "00 (GPIO1=0, GPIO2=0)", 30 | "01 (GPIO1=0, GPIO2=1)", 31 | "10 (GPIO1=1, GPIO2=0)", 32 | "11 (GPIO1=1, GPIO2=1)", 33 | }; 34 | 35 | const char *g_szChannelConfig[NUM_CHANNELCONFIG] = { 36 | "4 Channels", 37 | "2 Channels", 38 | "1 Channel", 39 | "1 OUT Pipe", 40 | "1 IN Pipe", 41 | }; 42 | 43 | int show_config(FT_60XCONFIGURATION * a_pConfigurationData, int a_bRead) 44 | { 45 | printf("\n"); 46 | printf("\tDevice Descriptor\n"); 47 | printf("\tVendorID : 0x%04X\n", 48 | a_pConfigurationData->VendorID); 49 | printf("\tProductID : 0x%04X\n", 50 | a_pConfigurationData->ProductID); 51 | 52 | char ucTemp[128] = { 0 }; 53 | char *pOffset = NULL; 54 | printf("\n"); 55 | printf("\tString Descriptor\n"); 56 | pOffset = (char *)&a_pConfigurationData->StringDescriptors[0]; 57 | memset(ucTemp, 0, sizeof(ucTemp)); 58 | if (pOffset[0] - 2) { 59 | memcpy(ucTemp, &pOffset[2], pOffset[0] - 2); 60 | } 61 | printf("\tManufacturer : %s\n", ucTemp); 62 | pOffset += pOffset[0]; 63 | memset(ucTemp, 0, sizeof(ucTemp)); 64 | if (pOffset[0] - 2) { 65 | memcpy(ucTemp, &pOffset[2], pOffset[0] - 2); 66 | } 67 | printf("\tProduct Description : %s\n", ucTemp); 68 | pOffset += pOffset[0]; 69 | memset(ucTemp, 0, sizeof(ucTemp)); 70 | if (pOffset[0] - 2) { 71 | memcpy(ucTemp, &pOffset[2], pOffset[0] - 2); 72 | } 73 | printf("\tSerial Number : %s\n", ucTemp); 74 | 75 | printf("\n"); 76 | printf("\tConfiguration Descriptor\n"); 77 | printf("\tPowerAttributes : %s %s\n", 78 | FT_IS_BUS_POWERED(a_pConfigurationData-> 79 | PowerAttributes) ? "Self-powered " : 80 | "Bus-powered ", 81 | FT_IS_REMOTE_WAKEUP_ENABLED(a_pConfigurationData-> 82 | PowerAttributes) ? "Remote wakeup " : 83 | ""); 84 | printf("\tPowerConsumption : %d\n", 85 | a_pConfigurationData->PowerConsumption); 86 | 87 | printf("\n"); 88 | printf("\tData Transfer\n"); 89 | printf("\tFIFOClock : %s\n", 90 | g_szFIFOClock[a_pConfigurationData->FIFOClock]); 91 | printf("\tFIFOMode : %s\n", 92 | g_szFIFOMode[a_pConfigurationData->FIFOMode]); 93 | printf("\tChannelConfig : %s\n", 94 | g_szChannelConfig[a_pConfigurationData->ChannelConfig]); 95 | 96 | printf("\n"); 97 | printf("\tOptional Features\n"); 98 | printf("\tDisableCancelOnUnderrun : %d\n", 99 | (a_pConfigurationData-> 100 | OptionalFeatureSupport & 101 | CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN) >> 102 | 1); 103 | printf("\tNotificationEnabled : %d (%d %d %d %d)\n", 104 | (a_pConfigurationData-> 105 | OptionalFeatureSupport & 106 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL) 107 | >> 2, 108 | (a_pConfigurationData-> 109 | OptionalFeatureSupport & 110 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH1) 111 | ? 1 : 0, 112 | (a_pConfigurationData-> 113 | OptionalFeatureSupport & 114 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH2) 115 | ? 1 : 0, 116 | (a_pConfigurationData-> 117 | OptionalFeatureSupport & 118 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH3) 119 | ? 1 : 0, 120 | (a_pConfigurationData-> 121 | OptionalFeatureSupport & 122 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH4) 123 | ? 1 : 0); 124 | printf("\tBatteryChargingEnabled : %d\n", 125 | a_pConfigurationData-> 126 | OptionalFeatureSupport & 127 | CONFIGURATION_OPTIONAL_FEATURE_ENABLEBATTERYCHARGING); 128 | printf("\tBCGPIOPinConfig : 0x%02X\n", 129 | a_pConfigurationData->BatteryChargingGPIOConfig); 130 | 131 | if (a_bRead) { 132 | printf("\tFlashEEPROMDetection : 0x%02X\n\n", 133 | a_pConfigurationData->FlashEEPROMDetection); 134 | printf("\t\tMemory Type : %s\n", 135 | a_pConfigurationData-> 136 | FlashEEPROMDetection & (1 << 137 | CONFIGURATION_FLASH_ROM_BIT_ROM) 138 | ? "ROM" : "Flash"); 139 | printf("\t\tMemory Detected : %s\n", 140 | a_pConfigurationData-> 141 | FlashEEPROMDetection & (1 << 142 | CONFIGURATION_FLASH_ROM_BIT_MEMORY_NOTEXIST) 143 | ? "Not Exists" : "Exists"); 144 | printf("\t\tCustom Config Validity : %s\n", 145 | a_pConfigurationData-> 146 | FlashEEPROMDetection & (1 << 147 | CONFIGURATION_FLASH_ROM_BIT_CUSTOMDATA_INVALID) 148 | ? "Invalid" : "Valid"); 149 | printf("\t\tCustom Config Checksum : %s\n", 150 | a_pConfigurationData-> 151 | FlashEEPROMDetection & (1 << 152 | CONFIGURATION_FLASH_ROM_BIT_CUSTOMDATACHKSUM_INVALID) 153 | ? "Invalid" : "Valid"); 154 | printf("\t\tGPIO Input : %s\n", 155 | a_pConfigurationData-> 156 | FlashEEPROMDetection & (1 << 157 | CONFIGURATION_FLASH_ROM_BIT_GPIO_INPUT) 158 | ? "Used" : "Ignore"); 159 | printf("\t\tConfig Used : %s\n", 160 | a_pConfigurationData-> 161 | FlashEEPROMDetection & (1 << 162 | CONFIGURATION_FLASH_ROM_BIT_CUSTOM) 163 | ? "CUSTOM" : "DEFAULT"); 164 | if (a_pConfigurationData-> 165 | FlashEEPROMDetection & (1 << 166 | CONFIGURATION_FLASH_ROM_BIT_GPIO_INPUT)) 167 | { 168 | printf("\t\tGPIO Input : %s\n", 169 | a_pConfigurationData-> 170 | FlashEEPROMDetection & (1 << 171 | CONFIGURATION_FLASH_ROM_BIT_GPIO_INPUT) 172 | ? "Used" : "Ignore"); 173 | printf("\t\tGPIO 0 : %s\n", 174 | a_pConfigurationData-> 175 | FlashEEPROMDetection & (1 << 176 | CONFIGURATION_FLASH_ROM_BIT_GPIO_0) 177 | ? "High" : "Low"); 178 | printf("\t\tGPIO 1 : %s\n", 179 | a_pConfigurationData-> 180 | FlashEEPROMDetection & (1 << 181 | CONFIGURATION_FLASH_ROM_BIT_GPIO_1) 182 | ? "High" : "Low"); 183 | } 184 | } 185 | 186 | printf("\n"); 187 | printf("\tMSIO and GPIO configuration\n"); 188 | printf("\tMSIOControl : 0x%08X\n", 189 | a_pConfigurationData->MSIO_Control); 190 | printf("\tGPIOControl : 0x%08X\n", 191 | a_pConfigurationData->GPIO_Control); 192 | 193 | printf("\n"); 194 | } 195 | 196 | int main() 197 | { 198 | int in; 199 | unsigned char buf[0x100]; 200 | int i; 201 | FT_60XCONFIGURATION oConfigurationData = { 0 }; 202 | 203 | in = open("/dev/ft60x0", O_RDWR | O_CLOEXEC); 204 | ioctl(in, 0, &oConfigurationData); 205 | 206 | show_config(&oConfigurationData, 1); 207 | oConfigurationData.FIFOMode = CONFIGURATION_FIFO_MODE_245; 208 | oConfigurationData.ChannelConfig = CONFIGURATION_CHANNEL_CONFIG_1; 209 | oConfigurationData.OptionalFeatureSupport = 210 | CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN | 211 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL; 212 | //oConfigurationData.OptionalFeatureSupport = 0; //CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN ; 213 | 214 | ioctl(in, 1, &oConfigurationData); 215 | close(in); 216 | 217 | } 218 | -------------------------------------------------------------------------------- /ftd3xx.h: -------------------------------------------------------------------------------- 1 | #ifndef FTD3XX_H_DKFTHSPV 2 | #define FTD3XX_H_DKFTHSPV 3 | 4 | #ifdef __cplusplus 5 | #include 6 | #include 7 | #include 8 | #include 9 | #else 10 | #include 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | #if defined(_WIN32) || defined(_WIN64) 17 | 18 | #include 19 | #ifdef FTD3XX_EXPORTS 20 | #define FTD3XX_API __declspec(dllexport) 21 | #elif defined(FTD3XX_STATIC) 22 | #define FTD3XX_API 23 | #else 24 | #define FTD3XX_API __declspec(dllimport) 25 | #endif 26 | 27 | #else /* _WIN32 || _WIN64 */ 28 | 29 | #define WINAPI 30 | #ifndef FTD3XX_EXPORTS 31 | #define FTD3XX_API 32 | #else /* !FTD3XX_EXPORTS */ 33 | #define FTD3XX_API __attribute__((visibility("default"))) 34 | #endif /* FTD3XX_EXPORTS */ 35 | 36 | typedef uint16_t WORD; 37 | typedef uint32_t DWORD; 38 | typedef uint64_t DWORD64; 39 | typedef uint32_t * LPDWORD; 40 | typedef uint8_t BYTE; 41 | typedef uint8_t UCHAR; 42 | typedef uint16_t USHORT; 43 | typedef USHORT * PUSHORT; 44 | typedef unsigned int ULONG; 45 | typedef uint16_t WCHAR; 46 | typedef uint8_t * LPBYTE; 47 | typedef bool BOOL; 48 | typedef UCHAR *PUCHAR; 49 | typedef ULONG *PULONG; 50 | typedef const char * LPCSTR; 51 | 52 | typedef void VOID; 53 | typedef void * PVOID; 54 | typedef void * LPVOID; 55 | typedef char * PCHAR; 56 | typedef void * HANDLE; 57 | 58 | typedef struct _OVERLAPPED { 59 | DWORD Internal; 60 | DWORD InternalHigh; 61 | union { 62 | struct { 63 | DWORD Offset; 64 | DWORD OffsetHigh; 65 | }; 66 | PVOID Pointer; 67 | }; 68 | HANDLE hEvent; 69 | } OVERLAPPED, *LPOVERLAPPED; 70 | 71 | typedef struct _SECURITY_ATTRIBUTES { 72 | DWORD nLength; 73 | LPVOID lpSecurityDescriptor; 74 | BOOL bInheritHandle; 75 | } SECURITY_ATTRIBUTES , *LPSECURITY_ATTRIBUTES; 76 | 77 | #endif /* OTHER OS */ 78 | 79 | // 80 | // Standard Descriptor Types 81 | // 82 | #define FT_DEVICE_DESCRIPTOR_TYPE 0x01 83 | #define FT_CONFIGURATION_DESCRIPTOR_TYPE 0x02 84 | #define FT_STRING_DESCRIPTOR_TYPE 0x03 85 | #define FT_INTERFACE_DESCRIPTOR_TYPE 0x04 86 | 87 | // 88 | // Power Configuration 89 | // 90 | #define FT_IS_SELF_POWERED(bmAttributes) ((bmAttributes) == 0x40) 91 | #define FT_IS_BUS_POWERED(bmAttributes) (!((bmAttributes) == 0x40)) 92 | #define FT_IS_REMOTE_WAKEUP_ENABLED(bmAttributes) ((bmAttributes) == 0x20) 93 | 94 | // 95 | // Pipe Direction 96 | // 97 | #define FT_IS_READ_PIPE(ucEndpoint) ((ucEndpoint) & 0x80) 98 | #define FT_IS_WRITE_PIPE(ucEndpoint) (!((ucEndpoint) & 0x80)) 99 | 100 | // 101 | // Pipe type 102 | // 103 | #define FT_IS_BULK_PIPE(ucPipeType) ((ucPipeType) == 2) 104 | #define FT_IS_INTERRUPT_PIPE(ucPipeType) ((ucPipeType) == 3) 105 | #define FT_IS_ISOCHRONOUS_PIPE(ucPipeType) (0) 106 | 107 | // 108 | // Reserved pipes 109 | // 110 | #define FT_RESERVED_INTERFACE_INDEX 0x0 111 | #define FT_RESERVED_PIPE_INDEX_SESSION 0x0 112 | #define FT_RESERVED_PIPE_INDEX_NOTIFICATION 0x1 113 | #define FT_RESERVED_PIPE_SESSION 0x1 114 | #define FT_RESERVED_PIPE_NOTIFICATION 0x81 115 | 116 | // 117 | // Create flags 118 | // 119 | #define FT_OPEN_BY_SERIAL_NUMBER 0x00000001 120 | #define FT_OPEN_BY_DESCRIPTION 0x00000002 121 | #define FT_OPEN_BY_LOCATION 0x00000004 122 | #define FT_OPEN_BY_GUID 0x00000008 123 | #define FT_OPEN_BY_INDEX 0x00000010 124 | 125 | // 126 | // ListDevices flags 127 | // 128 | #define FT_LIST_ALL 0x20000000 129 | #define FT_LIST_BY_INDEX 0x40000000 130 | #define FT_LIST_NUMBER_ONLY 0x80000000 131 | 132 | 133 | // 134 | // GPIO direction, value 135 | // 136 | #define FT_GPIO_DIRECTION_IN 0 137 | #define FT_GPIO_DIRECTION_OUT 1 138 | #define FT_GPIO_VALUE_LOW 0 139 | #define FT_GPIO_VALUE_HIGH 1 140 | #define FT_GPIO_0 0 141 | #define FT_GPIO_1 1 142 | 143 | typedef PVOID FT_HANDLE, *PFT_HANDLE; 144 | 145 | enum _FT_STATUS { 146 | FT_OK, 147 | FT_INVALID_HANDLE, 148 | FT_DEVICE_NOT_FOUND, 149 | FT_DEVICE_NOT_OPENED, 150 | FT_IO_ERROR, 151 | FT_INSUFFICIENT_RESOURCES, 152 | FT_INVALID_PARAMETER, /* 6 */ 153 | FT_INVALID_BAUD_RATE, 154 | FT_DEVICE_NOT_OPENED_FOR_ERASE, 155 | FT_DEVICE_NOT_OPENED_FOR_WRITE, 156 | FT_FAILED_TO_WRITE_DEVICE, /* 10 */ 157 | FT_EEPROM_READ_FAILED, 158 | FT_EEPROM_WRITE_FAILED, 159 | FT_EEPROM_ERASE_FAILED, 160 | FT_EEPROM_NOT_PRESENT, 161 | FT_EEPROM_NOT_PROGRAMMED, 162 | FT_INVALID_ARGS, 163 | FT_NOT_SUPPORTED, 164 | 165 | FT_NO_MORE_ITEMS, 166 | FT_TIMEOUT, /* 19 */ 167 | FT_OPERATION_ABORTED, 168 | FT_RESERVED_PIPE, 169 | FT_INVALID_CONTROL_REQUEST_DIRECTION, 170 | FT_INVALID_CONTROL_REQUEST_TYPE, 171 | FT_IO_PENDING, 172 | FT_IO_INCOMPLETE, 173 | FT_HANDLE_EOF, 174 | FT_BUSY, 175 | FT_NO_SYSTEM_RESOURCES, 176 | FT_DEVICE_LIST_NOT_READY, 177 | FT_DEVICE_NOT_CONNECTED, 178 | FT_INCORRECT_DEVICE_PATH, 179 | 180 | FT_OTHER_ERROR, 181 | }; 182 | 183 | typedef ULONG FT_STATUS; 184 | #define FT_SUCCESS(status) ((status) == FT_OK) 185 | #define FT_FAILED(status) ((status) != FT_OK) 186 | 187 | typedef enum _FT_PIPE_TYPE { 188 | FTPipeTypeControl, 189 | FTPipeTypeIsochronous, 190 | FTPipeTypeBulk, 191 | FTPipeTypeInterrupt 192 | } FT_PIPE_TYPE; 193 | 194 | typedef struct _FT_COMMON_DESCRIPTOR { 195 | UCHAR bLength; 196 | UCHAR bDescriptorType; 197 | } FT_COMMON_DESCRIPTOR, *PFT_COMMON_DESCRIPTOR; 198 | 199 | typedef struct _FT_DEVICE_DESCRIPTOR{ 200 | UCHAR bLength; 201 | UCHAR bDescriptorType; 202 | USHORT bcdUSB; 203 | UCHAR bDeviceClass; 204 | UCHAR bDeviceSubClass; 205 | UCHAR bDeviceProtocol; 206 | UCHAR bMaxPacketSize0; 207 | USHORT idVendor; 208 | USHORT idProduct; 209 | USHORT bcdDevice; 210 | UCHAR iManufacturer; 211 | UCHAR iProduct; 212 | UCHAR iSerialNumber; 213 | UCHAR bNumConfigurations; 214 | } FT_DEVICE_DESCRIPTOR, *PFT_DEVICE_DESCRIPTOR; 215 | 216 | typedef struct _FT_CONFIGURATION_DESCRIPTOR { 217 | UCHAR bLength; 218 | UCHAR bDescriptorType; 219 | USHORT wTotalLength; 220 | UCHAR bNumInterfaces; 221 | UCHAR bConfigurationValue; 222 | UCHAR iConfiguration; 223 | UCHAR bmAttributes; 224 | UCHAR MaxPower; 225 | } FT_CONFIGURATION_DESCRIPTOR, *PFT_CONFIGURATION_DESCRIPTOR; 226 | 227 | typedef struct _FT_INTERFACE_DESCRIPTOR { 228 | UCHAR bLength; 229 | UCHAR bDescriptorType; 230 | UCHAR bInterfaceNumber; 231 | UCHAR bAlternateSetting; 232 | UCHAR bNumEndpoints; 233 | UCHAR bInterfaceClass; 234 | UCHAR bInterfaceSubClass; 235 | UCHAR bInterfaceProtocol; 236 | UCHAR iInterface; 237 | } FT_INTERFACE_DESCRIPTOR, *PFT_INTERFACE_DESCRIPTOR; 238 | 239 | typedef struct _FT_STRING_DESCRIPTOR { 240 | UCHAR bLength; 241 | UCHAR bDescriptorType; 242 | WCHAR szString[256]; 243 | } FT_STRING_DESCRIPTOR, *PFT_STRING_DESCRIPTOR; 244 | 245 | typedef struct _FT_PIPE_INFORMATION { 246 | FT_PIPE_TYPE PipeType; 247 | UCHAR PipeId; 248 | USHORT MaximumPacketSize; 249 | UCHAR Interval; 250 | } FT_PIPE_INFORMATION, *PFT_PIPE_INFORMATION; 251 | 252 | typedef struct _FT_SETUP_PACKET { 253 | UCHAR RequestType; 254 | UCHAR Request; 255 | USHORT Value; 256 | USHORT Index; 257 | USHORT Length; 258 | } FT_SETUP_PACKET, *PFT_SETUP_PACKET; 259 | 260 | typedef enum _E_FT_NOTIFICATION_CALLBACK_TYPE { 261 | E_FT_NOTIFICATION_CALLBACK_TYPE_DATA, 262 | E_FT_NOTIFICATION_CALLBACK_TYPE_GPIO, 263 | E_FT_NOTIFICATION_CALLBACK_TYPE_INTERRUPT, 264 | } E_FT_NOTIFICATION_CALLBACK_TYPE; 265 | 266 | typedef struct _FT_NOTIFICATION_CALLBACK_INFO_DATA { 267 | ULONG ulRecvNotificationLength; 268 | UCHAR ucEndpointNo; 269 | } FT_NOTIFICATION_CALLBACK_INFO_DATA; 270 | 271 | typedef struct _FT_NOTIFICATION_CALLBACK_INFO_GPIO { 272 | BOOL bGPIO0; 273 | BOOL bGPIO1; 274 | } FT_NOTIFICATION_CALLBACK_INFO_GPIO; 275 | 276 | typedef VOID(*FT_NOTIFICATION_CALLBACK)(PVOID pvCallbackContext, 277 | E_FT_NOTIFICATION_CALLBACK_TYPE eCallbackType, PVOID pvCallbackInfo); 278 | 279 | // 280 | // Chip configuration - FlashEEPROMDetection 281 | // 282 | #define CONFIGURATION_FLASH_ROM_BIT_ROM 0 283 | #define CONFIGURATION_FLASH_ROM_BIT_MEMORY_NOTEXIST 1 284 | #define CONFIGURATION_FLASH_ROM_BIT_CUSTOMDATA_INVALID 2 285 | #define CONFIGURATION_FLASH_ROM_BIT_CUSTOMDATACHKSUM_INVALID 3 286 | #define CONFIGURATION_FLASH_ROM_BIT_CUSTOM 4 287 | #define CONFIGURATION_FLASH_ROM_BIT_GPIO_INPUT 5 288 | #define CONFIGURATION_FLASH_ROM_BIT_GPIO_0 6 289 | #define CONFIGURATION_FLASH_ROM_BIT_GPIO_1 7 290 | 291 | // 292 | // Chip configuration - Battery charging 293 | // 294 | #define CONFIGURATION_BATCHG_BIT_OFFSET_DCP 6 // Bit 6 and Bit 7 295 | #define CONFIGURATION_BATCHG_BIT_OFFSET_CDP 4 // Bit 4 and Bit 5 296 | #define CONFIGURATION_BATCHG_BIT_OFFSET_SDP 2 // Bit 2 and Bit 3 297 | #define CONFIGURATION_BATCHG_BIT_OFFSET_DEF 0 // Bit 0 and Bit 1 298 | #define CONFIGURATION_BATCHG_BIT_MASK 3 // 2 bits 299 | 300 | // 301 | // Chip configuration - FIFO Clock Speed 302 | // 303 | typedef enum { 304 | CONFIGURATION_FIFO_CLK_100, 305 | CONFIGURATION_FIFO_CLK_66, 306 | CONFIGURATION_FIFO_CLK_50, 307 | CONFIGURATION_FIFO_CLK_40, 308 | } CONFIGURATION_FIFO_CLK; 309 | 310 | // 311 | // Chip configuration - FIFO Mode 312 | // 313 | typedef enum { 314 | CONFIGURATION_FIFO_MODE_245, 315 | CONFIGURATION_FIFO_MODE_600, 316 | CONFIGURATION_FIFO_MODE_COUNT, 317 | } CONFIGURATION_FIFO_MODE; 318 | 319 | // 320 | // Chip configuration - Channel Configuration 321 | // 322 | typedef enum { 323 | CONFIGURATION_CHANNEL_CONFIG_4, 324 | CONFIGURATION_CHANNEL_CONFIG_2, 325 | CONFIGURATION_CHANNEL_CONFIG_1, 326 | CONFIGURATION_CHANNEL_CONFIG_1_OUTPIPE, 327 | CONFIGURATION_CHANNEL_CONFIG_1_INPIPE, 328 | CONFIGURATION_CHANNEL_CONFIG_COUNT, 329 | } CONFIGURATION_CHANNEL_CONFIG; 330 | 331 | // 332 | // Chip configuration - Optional Feature Support 333 | // 334 | typedef enum { 335 | CONFIGURATION_OPTIONAL_FEATURE_DISABLEALL = 0, 336 | CONFIGURATION_OPTIONAL_FEATURE_ENABLEBATTERYCHARGING = 1, 337 | CONFIGURATION_OPTIONAL_FEATURE_DISABLECANCELSESSIONUNDERRUN = 2, 338 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH1 = 4, 339 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH2 = 8, 340 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH3 = 0x10, 341 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCH4 = 0x20, 342 | CONFIGURATION_OPTIONAL_FEATURE_ENABLENOTIFICATIONMESSAGE_INCHALL = 0x3C, 343 | CONFIGURATION_OPTIONAL_FEATURE_DISABLEUNDERRUN_INCH1 = (0x1 << 6), 344 | CONFIGURATION_OPTIONAL_FEATURE_DISABLEUNDERRUN_INCH2 = (0x1 << 7), 345 | CONFIGURATION_OPTIONAL_FEATURE_DISABLEUNDERRUN_INCH3 = (0x1 << 8), 346 | CONFIGURATION_OPTIONAL_FEATURE_DISABLEUNDERRUN_INCH4 = (0x1 << 9), 347 | CONFIGURATION_OPTIONAL_FEATURE_DISABLEUNDERRUN_INCHALL = (0xF << 6), 348 | } CONFIGURATION_OPTIONAL_FEATURE_SUPPORT; 349 | 350 | // 351 | // Chip configuration - Default values 352 | // 353 | #define CONFIGURATION_DEFAULT_VENDORID 0x0403 354 | #define CONFIGURATION_DEFAULT_PRODUCTID_600 0x601E 355 | #define CONFIGURATION_DEFAULT_PRODUCTID_601 0x601F 356 | #define CONFIGURATION_DEFAULT_POWERATTRIBUTES 0xE0 357 | #define CONFIGURATION_DEFAULT_POWERCONSUMPTION 0x60 358 | #define CONFIGURATION_DEFAULT_FIFOCLOCK \ 359 | CONFIGURATION_FIFO_CLK_100 360 | #define CONFIGURATION_DEFAULT_FIFOMODE \ 361 | CONFIGURATION_FIFO_MODE_600 362 | #define CONFIGURATION_DEFAULT_CHANNELCONFIG \ 363 | CONFIGURATION_CHANNEL_CONFIG_4 364 | #define CONFIGURATION_DEFAULT_OPTIONALFEATURE \ 365 | CONFIGURATION_OPTIONAL_FEATURE_DISABLEALL 366 | #define CONFIGURATION_DEFAULT_BATTERYCHARGING 0xE4 367 | #define CONFIGURATION_DEFAULT_BATTERYCHARGING_TYPE_DCP 0x3 368 | #define CONFIGURATION_DEFAULT_BATTERYCHARGING_TYPE_CDP 0x2 369 | #define CONFIGURATION_DEFAULT_BATTERYCHARGING_TYPE_SDP 0x1 370 | #define CONFIGURATION_DEFAULT_BATTERYCHARGING_TYPE_OFF 0x0 371 | #define CONFIGURATION_DEFAULT_FLASHDETECTION 0x0 372 | #define CONFIGURATION_DEFAULT_MSIOCONTROL 0x10800 373 | #define CONFIGURATION_DEFAULT_GPIOCONTROL 0x0 374 | 375 | // 376 | // 377 | // Chip configuration structure 378 | // 379 | typedef struct { 380 | // Device Descriptor 381 | USHORT VendorID; 382 | USHORT ProductID; 383 | 384 | // String Descriptors 385 | UCHAR StringDescriptors[128]; 386 | 387 | // Configuration Descriptor 388 | UCHAR Reserved; 389 | UCHAR PowerAttributes; 390 | USHORT PowerConsumption; 391 | 392 | // Data Transfer Configuration 393 | UCHAR reserved; 394 | UCHAR FIFOClock; 395 | UCHAR FIFOMode; 396 | UCHAR ChannelConfig; 397 | 398 | // Optional Feature Support 399 | USHORT OptionalFeatureSupport; 400 | UCHAR BatteryChargingGPIOConfig; 401 | UCHAR FlashEEPROMDetection; // Read-only 402 | 403 | // MSIO and GPIO Configuration 404 | ULONG MSIO_Control; 405 | ULONG GPIO_Control; 406 | } FT_60XCONFIGURATION, *PFT_60XCONFIGURATION; 407 | 408 | // 409 | // Device types 410 | // 411 | typedef enum _FT_DEVICE { 412 | FT_DEVICE_UNKNOWN = 3, 413 | FT_DEVICE_600 = 600, 414 | FT_DEVICE_601 = 601, 415 | FT_DEVICE_602 = 602, 416 | FT_DEVICE_603 = 603, 417 | } FT_DEVICE; 418 | 419 | // 420 | // Device information 421 | // 422 | typedef enum _FT_FLAGS { 423 | FT_FLAGS_OPENED = 1, 424 | FT_FLAGS_HISPEED = 2, 425 | FT_FLAGS_SUPERSPEED = 4 426 | } FT_FLAGS; 427 | 428 | typedef struct _FT_DEVICE_LIST_INFO_NODE { 429 | ULONG Flags; // FT_FLAGS 430 | ULONG Type; 431 | ULONG ID; 432 | DWORD LocId; 433 | char SerialNumber[32]; 434 | char Description[32]; 435 | FT_HANDLE ftHandle; 436 | } FT_DEVICE_LIST_INFO_NODE; 437 | 438 | enum FT_GPIO_PULL { 439 | GPIO_PULL_50K_PD, 440 | GPIO_PULL_HIZ, 441 | GPIO_PULL_50K_PU, 442 | GPIO_PULL_DEFAULT = GPIO_PULL_50K_PD 443 | }; 444 | 445 | enum FT_PIPE_DIRECTION { 446 | FT_PIPE_DIR_IN, 447 | FT_PIPE_DIR_OUT, 448 | FT_PIPE_DIR_COUNT, 449 | }; 450 | 451 | struct FT_PIPE_TRANSFER_CONF { 452 | /* set to true PIPE is not used, default set to FALSE */ 453 | BOOL fPipeNotUsed; 454 | 455 | /* Enable non thread safe transfer to increase throughput, set this flag 456 | * if guarantee only single thread access the pipe at a time, default 457 | * set to FALSE */ 458 | BOOL fNonThreadSafeTransfer; 459 | 460 | /* Concurrent URB request number, 8 by default, set value < 2 to use 461 | * default value */ 462 | BYTE bURBCount; 463 | 464 | /* 256 by default, set value < 2 to use default value */ 465 | WORD wURBBufferCount; 466 | 467 | /* 32K by default, set value < 512 to use default value */ 468 | DWORD dwURBBufferSize; 469 | 470 | /* 1G by default, used by FT600 and FT601 only, set 0 to use 471 | * default value */ 472 | DWORD dwStreamingSize; 473 | }; 474 | 475 | typedef struct _FT_TRANSFER_CONF { 476 | /* structure size: sizeof(FT_TRANSFER_CONF) */ 477 | WORD wStructSize; 478 | 479 | /* Please refer to struture FT_PIPE_TRANSFER_CONF */ 480 | struct FT_PIPE_TRANSFER_CONF pipe[FT_PIPE_DIR_COUNT]; 481 | 482 | /* Stop reading next URB buffer if current buffer is not fully filled, 483 | * default set to FALSE */ 484 | BOOL fStopReadingOnURBUnderrun; 485 | 486 | /* Enable Bit Bang Mode */ 487 | BOOL fBitBangMode; 488 | 489 | /* Do not flush device side residue buffer after reopen the 490 | * device, default set to FALSE */ 491 | BOOL fKeepDeviceSideBufferAfterReopen; 492 | } FT_TRANSFER_CONF; 493 | 494 | #ifdef __cplusplus 495 | extern "C" { 496 | #endif 497 | 498 | /********************************************************************** 499 | * Linux only APIs * 500 | **********************************************************************/ 501 | 502 | /* Set transfer parameters for each FIFO channel 503 | * Must be called before FT_Create is called. Need to be called again 504 | * after FT_Close(), otherwise default parameters will be used. 505 | * 506 | * Default value will be used for each FIFO channel if this function 507 | * is not been called. Please refer to structure defines for default 508 | * value. 509 | * 510 | * pConf: Please refer to structure FT_TRANSFER_CONF 511 | * dwFifoID: FIFO interface ID. Valid values are 0-3 which represents 512 | * FIFO channel 1-4 for FT600 and FT601 */ 513 | FTD3XX_API FT_STATUS WINAPI FT_SetTransferParams( 514 | FT_TRANSFER_CONF *pConf, 515 | DWORD dwFifoID); 516 | 517 | /* ReadPipe with timeout 518 | * 519 | * dwFifoID: FIFO interface ID. Valid values are 0-3 which represents 520 | * FIFO channel 1-4 for FT600 and FT601 521 | * dwTimeoutInMs: timeout in milliseconds, 0 means return immediately 522 | * if no data */ 523 | FTD3XX_API FT_STATUS WINAPI FT_ReadPipeEx( 524 | FT_HANDLE ftHandle, 525 | UCHAR ucFifoID, 526 | PUCHAR pucBuffer, 527 | ULONG ulBufferLength, 528 | PULONG pulBytesTransferred, 529 | DWORD dwTimeoutInMs); 530 | 531 | /* WritePipe with timeout 532 | * 533 | * dwFifoID: FIFO interface ID. Valid values are 0-3 which represents 534 | * FIFO channel 1-4 for FT600 and FT601 535 | * dwTimeoutInMs: timeout in milliseconds, 0 means return immediately 536 | * if no data */ 537 | FTD3XX_API FT_STATUS WINAPI FT_WritePipeEx( 538 | FT_HANDLE ftHandle, 539 | UCHAR ucFifoID, 540 | PUCHAR pucBuffer, 541 | ULONG ulBufferLength, 542 | PULONG pulBytesTransferred, 543 | DWORD dwTimeoutInMs); 544 | 545 | /* Get total unread buffer length in library's queue 546 | * 547 | * dwFifoID: FIFO interface ID. Valid values are 0-3 which represents 548 | * FIFO channel 1-4 for FT600 and FT601 */ 549 | FTD3XX_API FT_STATUS WINAPI FT_GetReadQueueStatus( 550 | FT_HANDLE ftHandle, 551 | UCHAR ucFifoID, 552 | LPDWORD lpdwAmountInQueue); 553 | 554 | /* Get total unsent buffer length in library's queue 555 | * 556 | * dwFifoID: FIFO interface ID. Valid values are 0-3 which represents 557 | * FIFO channel 1-4 for FT600 and FT601 */ 558 | FTD3XX_API FT_STATUS WINAPI FT_GetWriteQueueStatus( 559 | FT_HANDLE ftHandle, 560 | UCHAR ucFifoID, 561 | LPDWORD lpdwAmountInQueue); 562 | 563 | /* Read unsent buffer for OUT pipe 564 | * Set byBuffer to NULL first to close the pipe to get accurate buffer 565 | * length, allocate buffer with the length, then call this function 566 | * again with the allocated buffer to read out all buffers 567 | * 568 | * dwFifoID: FIFO interface ID. Valid values are 0-3 which represents 569 | * FIFO channel 1-4 for FT600 and FT601 570 | * byBuffer: User allocated buffer 571 | * lpdwBufferLength: Pointer to receive the size of buffer if byBuffer 572 | * is NULL. Size of buffer if byBuffer is not NULL. */ 573 | FTD3XX_API FT_STATUS WINAPI FT_GetUnsentBuffer( 574 | FT_HANDLE ftHandle, 575 | UCHAR ucFifoID, 576 | BYTE *byBuffer, 577 | LPDWORD lpdwBufferLength); 578 | 579 | FTD3XX_API FT_STATUS WINAPI FT_SetPipeTimeout( 580 | FT_HANDLE ftHandle, 581 | UCHAR ucEndpoint, 582 | DWORD dwTimeoutInMs 583 | ); 584 | 585 | FTD3XX_API FT_STATUS WINAPI FT_CreateDeviceInfoList( 586 | LPDWORD lpdwNumDevs 587 | ); 588 | 589 | FTD3XX_API FT_STATUS WINAPI FT_GetDeviceInfoList( 590 | FT_DEVICE_LIST_INFO_NODE *ptDest, 591 | LPDWORD lpdwNumDevs 592 | ); 593 | 594 | FTD3XX_API FT_STATUS WINAPI FT_ListDevices( 595 | PVOID pArg1, 596 | PVOID pArg2, 597 | DWORD Flags 598 | ); 599 | 600 | FTD3XX_API FT_STATUS WINAPI FT_Create( 601 | PVOID pvArg, 602 | DWORD dwFlags, 603 | FT_HANDLE *pftHandle 604 | ); 605 | 606 | FTD3XX_API FT_STATUS WINAPI FT_Close( 607 | FT_HANDLE ftHandle); 608 | 609 | FTD3XX_API FT_STATUS WINAPI FT_GetVIDPID( 610 | FT_HANDLE ftHandle, 611 | PUSHORT puwVID, 612 | PUSHORT puwPID 613 | ); 614 | 615 | FTD3XX_API FT_STATUS WINAPI FT_WritePipe( 616 | FT_HANDLE ftHandle, 617 | UCHAR ucEndpoint, 618 | PUCHAR pucBuffer, 619 | ULONG ulBufferLength, 620 | PULONG pulBytesTransferred, 621 | LPOVERLAPPED pOverlapped 622 | ); 623 | 624 | FTD3XX_API FT_STATUS WINAPI FT_ReadPipe( 625 | FT_HANDLE ftHandle, 626 | UCHAR ucEndpoint, 627 | PUCHAR pucBuffer, 628 | ULONG ulBufferLength, 629 | PULONG pulBytesTransferred, 630 | LPOVERLAPPED pOverlapped 631 | ); 632 | 633 | FTD3XX_API FT_STATUS WINAPI FT_GetOverlappedResult( 634 | FT_HANDLE ftHandle, 635 | LPOVERLAPPED pOverlapped, 636 | PULONG pulBytesTransferred, 637 | BOOL bWait 638 | ); 639 | 640 | FTD3XX_API FT_STATUS WINAPI FT_InitializeOverlapped( 641 | FT_HANDLE ftHandle, 642 | LPOVERLAPPED pOverlapped 643 | ); 644 | 645 | FTD3XX_API FT_STATUS WINAPI FT_ReleaseOverlapped( 646 | FT_HANDLE ftHandle, 647 | LPOVERLAPPED pOverlapped 648 | ); 649 | 650 | FTD3XX_API FT_STATUS WINAPI FT_SetStreamPipe( 651 | FT_HANDLE ftHandle, 652 | BOOL bAllWritePipes, 653 | BOOL bAllReadPipes, 654 | UCHAR ucEndpoint, 655 | ULONG ulStreamSize 656 | ); 657 | 658 | FTD3XX_API FT_STATUS WINAPI FT_ClearStreamPipe( 659 | FT_HANDLE ftHandle, 660 | BOOL bAllWritePipes, 661 | BOOL bAllReadPipes, 662 | UCHAR ucEndpoint 663 | ); 664 | 665 | FTD3XX_API FT_STATUS WINAPI FT_FlushPipe( 666 | FT_HANDLE ftHandle, 667 | UCHAR ucEndpoint 668 | ); 669 | 670 | FTD3XX_API FT_STATUS WINAPI FT_AbortPipe( 671 | FT_HANDLE ftHandle, 672 | UCHAR ucEndpoint 673 | ); 674 | 675 | FTD3XX_API FT_STATUS WINAPI FT_GetDeviceDescriptor( 676 | FT_HANDLE ftHandle, 677 | PFT_DEVICE_DESCRIPTOR ptDescriptor 678 | ); 679 | 680 | FTD3XX_API FT_STATUS WINAPI FT_GetConfigurationDescriptor( 681 | FT_HANDLE ftHandle, 682 | PFT_CONFIGURATION_DESCRIPTOR ptDescriptor 683 | ); 684 | 685 | FTD3XX_API FT_STATUS WINAPI FT_GetInterfaceDescriptor( 686 | FT_HANDLE ftHandle, 687 | UCHAR ucInterfaceIndex, 688 | PFT_INTERFACE_DESCRIPTOR ptDescriptor 689 | ); 690 | 691 | FTD3XX_API FT_STATUS WINAPI FT_GetPipeInformation( 692 | FT_HANDLE ftHandle, 693 | UCHAR ucInterfaceIndex, 694 | UCHAR ucEndpoint, 695 | PFT_PIPE_INFORMATION ptPipeInformation 696 | ); 697 | 698 | FTD3XX_API FT_STATUS WINAPI FT_GetStringDescriptor( 699 | FT_HANDLE ftHandle, 700 | UCHAR ucStringIndex, 701 | PFT_STRING_DESCRIPTOR ptDescriptor 702 | ); 703 | 704 | FTD3XX_API FT_STATUS WINAPI FT_GetDescriptor( 705 | FT_HANDLE ftHandle, 706 | UCHAR ucDescriptorType, 707 | UCHAR ucIndex, 708 | PUCHAR pucBuffer, 709 | ULONG ulBufferLength, 710 | PULONG pulLengthTransferred 711 | ); 712 | 713 | FTD3XX_API FT_STATUS WINAPI FT_ControlTransfer( 714 | FT_HANDLE ftHandle, 715 | FT_SETUP_PACKET tSetupPacket, 716 | PUCHAR pucBuffer, 717 | ULONG ulBufferLength, 718 | PULONG pulLengthTransferred 719 | ); 720 | 721 | FTD3XX_API FT_STATUS WINAPI FT_SetGPIO( 722 | FT_HANDLE ftHandle, 723 | UCHAR ucDirection, 724 | UCHAR ucValue 725 | ); 726 | 727 | FTD3XX_API FT_STATUS WINAPI FT_GetGPIO( 728 | FT_HANDLE ftHandle, 729 | UCHAR ucDirection, 730 | FT_NOTIFICATION_CALLBACK pCallback, 731 | PVOID pvCallbackContext, 732 | USHORT uwCallbackLatency 733 | ); 734 | 735 | FTD3XX_API FT_STATUS WINAPI FT_SetNotificationCallback( 736 | FT_HANDLE ftHandle, 737 | FT_NOTIFICATION_CALLBACK pCallback, 738 | PVOID pvCallbackContext 739 | ); 740 | 741 | FTD3XX_API VOID WINAPI FT_ClearNotificationCallback( 742 | FT_HANDLE ftHandle 743 | ); 744 | 745 | FTD3XX_API FT_STATUS WINAPI FT_GetChipConfiguration( 746 | FT_HANDLE ftHandle, 747 | PVOID pvConfiguration 748 | ); 749 | 750 | FTD3XX_API FT_STATUS WINAPI FT_SetChipConfiguration( 751 | FT_HANDLE ftHandle, 752 | PVOID pvConfiguration 753 | ); 754 | 755 | FTD3XX_API FT_STATUS WINAPI FT_GetFirmwareVersion( 756 | FT_HANDLE ftHandle, 757 | PULONG pulFirmwareVersion 758 | ); 759 | 760 | FTD3XX_API FT_STATUS WINAPI FT_ResetDevicePort( 761 | FT_HANDLE ftHandle 762 | ); 763 | 764 | FTD3XX_API FT_STATUS WINAPI FT_CycleDevicePort( 765 | FT_HANDLE ftHandle 766 | ); 767 | 768 | FTD3XX_API FT_STATUS WINAPI FT_GetDeviceInfoDetail( 769 | DWORD dwIndex, 770 | LPDWORD lpdwFlags, 771 | LPDWORD lpdwType, 772 | LPDWORD lpdwID, 773 | LPDWORD lpdwLocId, 774 | LPVOID lpSerialNumber, 775 | LPVOID lpDescription, 776 | FT_HANDLE *pftHandle 777 | ); 778 | 779 | FTD3XX_API FT_STATUS WINAPI FT_IsDevicePath( 780 | FT_HANDLE ftHandle, 781 | LPCSTR pucDevicePath 782 | ); 783 | 784 | FTD3XX_API FT_STATUS WINAPI FT_GetDriverVersion( 785 | FT_HANDLE ftHandle, 786 | LPDWORD lpdwVersion 787 | ); 788 | 789 | FTD3XX_API FT_STATUS WINAPI FT_GetLibraryVersion( 790 | LPDWORD lpdwVersion 791 | ); 792 | 793 | /* Enable GPIOs 794 | * Each bit represents one GPIO setting, GPIO0-GPIO2 from LSB to MSB 795 | * 796 | * dwMask: set bit to 0 to skip the GPIO, 1 to enable the GPIO 797 | * dwDirection: set bit to 0 for input, 1 for output */ 798 | FTD3XX_API FT_STATUS WINAPI FT_EnableGPIO( 799 | FT_HANDLE ftHandle, 800 | DWORD dwMask, 801 | DWORD dwDirection 802 | ); 803 | 804 | /* Set GPIO level 805 | * Each bit represents one GPIO setting, GPIO0-GPIO2 from LSB to MSB 806 | * 807 | * dwMask: set bit to 0 to skip the GPIO, 1 to enable the GPIO 808 | * dwDirection: set bit to 0 for low, 1 for high */ 809 | FTD3XX_API FT_STATUS WINAPI FT_WriteGPIO( 810 | FT_HANDLE ftHandle, 811 | DWORD dwMask, 812 | DWORD dwLevel 813 | ); 814 | 815 | /* Get level of all GPIOs 816 | * Each bit represents one GPIO setting, GPIO0-GPIO2, RD_N, OE_N from 817 | * LSB to MSB */ 818 | FTD3XX_API FT_STATUS WINAPI FT_ReadGPIO( 819 | FT_HANDLE ftHandle, 820 | DWORD *pdwData 821 | ); 822 | 823 | /* Set GPIO internal pull resisters 824 | * dwMask: Each bit represents one GPIO setting, GPIO0-GPIO2 from 825 | * LSB to MSB 826 | * dwPull: Each two bits represents one GPIO setting, GPIO0-GPIO2 from 827 | * LSB to MSB 828 | * 829 | * dwMask: set bit to 0 to skip the GPIO, 1 to enable the GPIO 830 | * dwPull: refer to enum FT_GPIO_PULL */ 831 | FTD3XX_API FT_STATUS WINAPI FT_SetGPIOPull( 832 | FT_HANDLE ftHandle, 833 | DWORD dwMask, 834 | DWORD dwPull 835 | ); 836 | 837 | #ifdef __cplusplus 838 | } 839 | #endif 840 | 841 | #endif /* end of include guard: FTD3XX_H_DKFTHSPV */ 842 | -------------------------------------------------------------------------------- /loopback.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | void udp_callback(int fd, short event, void *arg) 11 | { 12 | int val; 13 | 14 | char *buf = malloc(1024 * 128); 15 | val = read(fd, buf, 4096); 16 | printf("GOT EVENT %d!\n", val); 17 | 18 | if (val > 0) 19 | write(fd, buf, val); 20 | free(buf); 21 | } 22 | 23 | int main(int argc, char **argv) 24 | { 25 | struct event_base *base; 26 | struct event *ev; 27 | 28 | int in; 29 | unsigned char *buf; 30 | int i; 31 | 32 | buf = malloc(128 * 1024); 33 | 34 | for (i = 0; i < 128 * 1024; i++) { 35 | buf[i] = 'A' + (i % 64); 36 | } 37 | 38 | in = open("/dev/ft60x0", O_RDWR | O_CLOEXEC); 39 | 40 | base = event_base_new(); 41 | if (!base) { 42 | return 1; 43 | } 44 | 45 | ev = event_new(base, in, EV_READ | EV_PERSIST, &udp_callback, NULL); 46 | event_add(ev, NULL); 47 | 48 | write(in, buf, 2048); 49 | write(in, buf, 2048); 50 | write(in, buf, 2048); 51 | write(in, buf, 2048); 52 | 53 | event_base_dispatch(base); 54 | event_base_free(base); 55 | 56 | return 0; 57 | } 58 | --------------------------------------------------------------------------------