diff -u src.orig/ftdi.c src/ftdi.c --- src.orig/ftdi.c 2012-09-14 05:37:53.000000000 +0000 +++ src/ftdi.c 2012-09-15 03:13:17.000000000 +0000 @@ -105,6 +105,9 @@ ftdi->error_str = NULL; ftdi->module_detach_mode = AUTO_DETACH_SIO_MODULE; + ftdi->modem_status_change_cb = NULL; + ftdi->last_modem_status = 0xffff; /* hope this init value is good enought */ + if (libusb_init(&ftdi->usb_ctx) < 0) ftdi_error_return(-3, "libusb_init() failed"); @@ -1397,11 +1400,33 @@ struct ftdi_transfer_control *tc = (struct ftdi_transfer_control *) transfer->user_data; struct ftdi_context *ftdi = tc->ftdi; int packet_size, actual_length, num_of_chunks, chunk_remains, i, ret; + unsigned short modem_status; + int cancel_requested = 0; packet_size = ftdi->max_packet_size; actual_length = transfer->actual_length; + if (actual_length >= 2) + { + /* callback on modem status change */ + modem_status = (ftdi->readbuffer[1] << 8) | (ftdi->readbuffer[0] & 0xFF); + if (modem_status != ftdi->last_modem_status) + { + if (ftdi->modem_status_change_cb != NULL) + { + cancel_requested = (*ftdi->modem_status_change_cb)(ftdi, modem_status); // call our callback function + if(cancel_requested && transfer->status != LIBUSB_TRANSFER_COMPLETED + && transfer->status != LIBUSB_TRANSFER_CANCELLED) // check for cancel request + { + /* submit request to cancel transfer */ + libusb_cancel_transfer(transfer); + } + } + ftdi->last_modem_status = modem_status; + } + } + if (actual_length > 2) { // skip FTDI status bytes. @@ -1468,9 +1493,14 @@ } } } - ret = libusb_submit_transfer (transfer); - if (ret < 0) + if (cancel_requested || transfer->status == LIBUSB_TRANSFER_CANCELLED) tc->completed = 1; + else + { + ret = libusb_submit_transfer (transfer); + if (ret < 0) + tc->completed = 1; + } } @@ -1663,14 +1693,14 @@ while (!tc->completed) { - ret = libusb_handle_events(tc->ftdi->usb_ctx); + ret = libusb_handle_events_completed(tc->ftdi->usb_ctx, &tc->completed); if (ret < 0) { if (ret == LIBUSB_ERROR_INTERRUPTED) continue; libusb_cancel_transfer(tc->transfer); while (!tc->completed) - if (libusb_handle_events(tc->ftdi->usb_ctx) < 0) + if (libusb_handle_events_completed(tc->ftdi->usb_ctx, &tc->completed) < 0) break; libusb_free_transfer(tc->transfer); free (tc); @@ -1750,6 +1780,7 @@ int offset = 0, ret, i, num_of_chunks, chunk_remains; int packet_size = ftdi->max_packet_size; int actual_length = 1; + unsigned short modem_status; if (ftdi == NULL || ftdi->usb_dev == NULL) ftdi_error_return(-666, "USB device unavailable"); @@ -1789,6 +1820,18 @@ if (ret < 0) ftdi_error_return(ret, "usb bulk read failed"); + if (actual_length >= 2) + { + /* callback on modem status change */ + modem_status = (ftdi->readbuffer[1] << 8) | (ftdi->readbuffer[0] & 0xFF); + if (modem_status != ftdi->last_modem_status) + { + if (ftdi->modem_status_change_cb != NULL) + (void)(*ftdi->modem_status_change_cb)(ftdi, modem_status); // call back if registered (not NULL) + ftdi->last_modem_status = modem_status; // save fresh status to ftdi_context + } + } + if (actual_length > 2) { // skip FTDI status bytes. @@ -2096,6 +2139,13 @@ *status = (usb_val[1] << 8) | (usb_val[0] & 0xFF); + if (*status != ftdi->last_modem_status) + { + if (ftdi->modem_status_change_cb != NULL) + (void)(*ftdi->modem_status_change_cb)(ftdi, *status); // calling back if registered + ftdi->last_modem_status = *status; + } + return 0; } @@ -2278,6 +2328,60 @@ } /** + Register modem status lines change callback function + + \param ftdi - pointer to ftdi_context + \param modem_status_change_cb - pointer to callback fuction + callback function should be defined as: + int callback_funct (struct ftdi_context *, unsigned short modem_status) + called with modem_status bitmap(same as filled with ftdi_poll_modem_status()) + If at async reading modem status was changed and callback function + return NON ZERO int value, then current transfer will be canceled. + \retval 0: all fine + \retval -2: USB device unavailable + \retval -1: attempt to assign callback function to NULL pointer +*/ + +int ftdi_register_modem_status_change_cb(struct ftdi_context *ftdi, + int (*modem_status_change_cb)(struct ftdi_context*, unsigned short)) +{ + if (ftdi == NULL || ftdi->usb_dev == NULL) + ftdi_error_return(-2, "USB device unavailable"); + if (modem_status_change_cb == NULL) + ftdi_error_return(-1, "attempt to assign callback function to NULL pointer"); + + /* trying to assign current modem status to last_modem_status */ + + if (ftdi_poll_modem_status(ftdi, &ftdi->last_modem_status) < 0) + { + ftdi->last_modem_status = 0xffff; // if unsuccessfull + } + + ftdi->modem_status_change_cb = modem_status_change_cb; + + return 0; +} + +/** + Get last received modem lines status. + + \param ftdi pointer to ftdi_context + \param modem_status Pointer to store modem status + + \retval 0: all fine + \retval -1: FTDI context invalid +*/ +int ftdi_get_last_modem_status(struct ftdi_context *ftdi, unsigned short *modem_status) +{ + if (ftdi == NULL) + ftdi_error_return(-1, "FTDI context invalid"); + + *modem_status = ftdi->last_modem_status; + return 0; +} + + +/** Init eeprom with default values for the connected device \param ftdi pointer to ftdi_context \param manufacturer String to use as Manufacturer diff -u src.orig/ftdi.h src/ftdi.h --- src.orig/ftdi.h 2012-09-14 05:37:53.000000000 +0000 +++ src/ftdi.h 2012-09-15 02:14:19.000000000 +0000 @@ -224,7 +224,11 @@ unsigned int writebuffer_chunksize; /** maximum packet size. Needed for filtering modem status bytes every n packets. */ unsigned int max_packet_size; - + /** last received modem lines status */ + unsigned short last_modem_status; + /** pointer to callback function. Called when modem status changed */ + int (*modem_status_change_cb) (struct ftdi_context *, unsigned short); + /* FTDI FT2232C requirecments */ /** FT2232C interface number: 0 or 1 */ int interface; /* 0 or 1 */ @@ -499,6 +503,12 @@ int ftdi_set_event_char(struct ftdi_context *ftdi, unsigned char eventch, unsigned char enable); int ftdi_set_error_char(struct ftdi_context *ftdi, unsigned char errorch, unsigned char enable); + /* modem status */ + int ftdi_register_modem_status_change_cb(struct ftdi_context *ftdi, + int (*modem_status_change_cb)(struct ftdi_context *, unsigned short)); + int ftdi_get_last_modem_status(struct ftdi_context *ftdi, unsigned short *modem_status); + + /* init eeprom for the given FTDI type */ int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi, char * manufacturer, char *product,