Hi Oliver, On May 3, 2010, at 11:35 PM, Oliver Neukum wrote: > Without your driver, syslog and/or lsusb -v nothing can be said. Here is the lsusb -v output: Bus 003 Device 002: ID 04e2:1410 Exar Corp. Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 ? bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x04e2 Exar Corp. idProduct 0x1410 bcdDevice 0.99 iManufacturer 0 iProduct 0 iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 75 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xc0 Self Powered MaxPower 30mA UNRECOGNIZED: 08 0b 00 02 02 02 00 00 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 1 AT-commands (v.25ter) iInterface 0 CDC Header: bcdCDC 1.10 CDC ACM: bmCapabilities 0x06 sends break line coding and serial state CDC Union: bMasterInterface 0 bSlaveInterface 1 CDC Call Management: bmCapabilities 0x01 call management bDataInterface 1 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x85 EP 5 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 2 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 Unused bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Here is the syslog output: May 4 10:47:46 localhost kernel: USB Serial support registered for Exar serial May 4 10:47:46 localhost kernel: usbcore: registered new interface driver exar May 4 10:47:46 localhost kernel: USB Driver for Exar USB serial ports: v1.00a May 4 10:47:55 localhost kernel: hub 1-0:1.0: state 7 ports 6 chg 0000 evt 0008 May 4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: GetStatus port 3 status 001803 POWER sig=j CSC CONNECT May 4 10:47:55 localhost kernel: hub 1-0:1.0: port 3, status 0501, change 0001, 480 Mb/s May 4 10:47:55 localhost kernel: hub 1-0:1.0: debounce: port 3: total 100ms stable 100ms status 0x501 May 4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: port 3 full speed --> companion May 4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: GetStatus port 3 status 003001 POWER OWNER sig=se0 CONNECT May 4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: auto-wakeup root hub May 4 10:47:55 localhost kernel: hub 1-0:1.0: port 3 not reset yet, waiting 50ms May 4 10:47:55 localhost kernel: ehci_hcd 0000:00:02.1: GetStatus port 3 status 003802 POWER OWNER sig=j CSC May 4 10:47:55 localhost kernel: hub 3-0:1.0: state 7 ports 6 chg 0000 evt 0008 May 4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: GetStatus roothub.portstatus [2] = 0x00010101 CSC PPS CCS May 4 10:47:55 localhost kernel: hub 3-0:1.0: port 3, status 0101, change 0001, 12 Mb/s May 4 10:47:55 localhost kernel: hub 3-0:1.0: debounce: port 3: total 100ms stable 100ms status 0x101 May 4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: GetStatus roothub.portstatus [2] = 0x00100103 PRSC PPS PES CCS May 4 10:47:55 localhost kernel: usb 3-3: new full speed USB device using ohci_hcd and address 2 May 4 10:47:55 localhost kernel: ohci_hcd 0000:00:02.0: GetStatus roothub.portstatus [2] = 0x00100103 PRSC PPS PES CCS May 4 10:47:55 localhost kernel: usb 3-3: skipped 1 descriptor after configuration May 4 10:47:55 localhost kernel: usb 3-3: skipped 4 descriptors after interface May 4 10:47:55 localhost kernel: usb 3-3: udev 2, busnum 3, minor = 257 May 4 10:47:55 localhost kernel: usb 3-3: New USB device found, idVendor=04e2, idProduct=1410 May 4 10:47:55 localhost kernel: usb 3-3: New USB device strings: Mfr=0, Product=0, SerialNumber=0 May 4 10:47:56 localhost kernel: usb 3-3: usb_probe_device May 4 10:47:56 localhost kernel: usb 3-3: configuration #1 chosen from 1 choice May 4 10:47:56 localhost kernel: usb 3-3: adding 3-3:1.0 (config #1, interface 0) May 4 10:47:56 localhost kernel: usbserial_generic 3-3:1.0: usb_probe_interface May 4 10:47:56 localhost kernel: usbserial_generic 3-3:1.0: usb_probe_interface - got id May 4 10:47:56 localhost kernel: exar 3-3:1.0: usb_probe_interface May 4 10:47:56 localhost kernel: exar 3-3:1.0: usb_probe_interface - got id May 4 10:47:56 localhost kernel: exar 3-3:1.0: Skipping management interface May 4 10:47:56 localhost kernel: usb 3-3: adding 3-3:1.1 (config #1, interface 1) May 4 10:47:56 localhost kernel: usbserial_generic 3-3:1.1: usb_probe_interface May 4 10:47:56 localhost kernel: usbserial_generic 3-3:1.1: usb_probe_interface - got id May 4 10:47:56 localhost kernel: exar 3-3:1.1: usb_probe_interface May 4 10:47:56 localhost kernel: exar 3-3:1.1: usb_probe_interface - got id May 4 10:47:56 localhost kernel: exar 3-3:1.1: Using management interface interrupt endpoints May 4 10:47:56 localhost kernel: exar 3-3:1.1: Taking interrupt endpoint from management interface May 4 10:47:56 localhost kernel: exar 3-3:1.1: Exar serial converter detected May 4 10:47:56 localhost kernel: usb 3-3: exar_attach: 1410 May 4 10:47:56 localhost kernel: usb 3-3: Exar serial converter now attached to ttyUSB0 May 4 10:47:56 localhost kernel: drivers/usb/core/inode.c: creating file '002' May 4 10:47:56 localhost kernel: hub 1-0:1.0: state 7 ports 6 chg 0000 evt 0008 May 4 10:47:56 localhost kernel: hub 3-0:1.0: state 7 ports 6 chg 0000 evt 0008 And the driver source follows: Rob. /* * USB driver for Exar serial converters * * Copyright (C) 2010 Rob Duncan <rob.duncan@xxxxxxxx> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * Heavily based on the option.c Usb serial driver by Matthias Urlichs * <smurf@xxxxxxxxxxxxxx>. See that file for more information. */ #define DEBUG #include <linux/kernel.h> #include <linux/tty.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/usb/serial.h> #include <linux/usb/cdc.h> #include <asm/unaligned.h> #include <linux/slab.h> #include <linux/tty_flip.h> #include "exar.h" #define DRIVER_VERSION "v1.00a" #define DRIVER_AUTHOR "Rob Duncan <rob.duncan@xxxxxxxx>" #define DRIVER_DESC "USB Driver for Exar USB serial ports" static int debug; #define TR_CONFIG 0x00000001 #define TR_REG 0x00000002 #define TR_INTERRUPT 0x00000004 #define TR_FUNC 0x00000008 #define TR_URB 0x00000010 #define TR_DATA 0x00000020 #define TRACE_X(X, DEV, MSG...) if (debug & X) dev_dbg(DEV, MSG) #define TRACE_CONFIG(DEV, MSG...) TRACE_X(TR_CONFIG, DEV, MSG) #define TRACE_REG(DEV, MSG...) TRACE_X(TR_REG, DEV, MSG) #define TRACE_INTERRUPT(DEV, MSG...) TRACE_X(TR_INTERRUPT, DEV, MSG) #define TRACE_FUNC(DEV, MSG...) TRACE_X(TR_FUNC, DEV, MSG) #define TRACE_URB(DEV, MSG...) TRACE_X(TR_URB, DEV, MSG) #define TRACE_DATA(DEV, MSG...) TRACE_X(TR_DATA, DEV, MSG) #define TRACE(DEV, MSG...) TRACE_X(-1, DEV, MSG) static void exar_indat_callback(struct urb *urb); static void exar_outdat_callback(struct urb *urb); /* ---------------------------------------------------------------- */ #define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) #define ACM_CTRL_DTR 0x01 #define ACM_CTRL_RTS 0x02 #define ACM_CTRL_DCD 0x01 #define ACM_CTRL_DSR 0x02 #define ACM_CTRL_BRK 0x04 #define ACM_CTRL_RI 0x08 #define ACM_CTRL_FRAMING 0x10 #define ACM_CTRL_PARITY 0x20 #define ACM_CTRL_OVERRUN 0x40 #define N_IN_URB 4 #define IN_BUFLEN PAGE_SIZE #define N_OUT_URB 4 #define OUT_BUFLEN PAGE_SIZE /* *INDENT-OFF* */ struct exar_port_private { int product_id; int block; int clocal; int ctrlin; int ctrlout; spinlock_t susp_lock; unsigned int suspended:1; int in_flight; struct urb *in_urbs[N_IN_URB]; u8 *in_buffer[N_IN_URB]; struct urb *out_urbs[N_OUT_URB]; u8 *out_buffer[N_OUT_URB]; unsigned long out_busy; /* Bit vector of URBs in use */ int opened; struct usb_anchor delayed; unsigned long tx_start_time[N_OUT_URB]; }; /* *INDENT-ON* */ /* ---------------------------------------------------------------- */ #define V_URM_REG_BLOCK 4 #define V_PRM_REG_BLOCK 5 #define V_UART_ENABLE 0x003 #define V_UART_ENABLE_TX_M 0x1 #define V_UART_ENABLE_TX_S 0 #define V_UART_ENABLE_TX 0x001 #define V_UART_ENABLE_RX_M 0x1 #define V_UART_ENABLE_RX_S 1 #define V_UART_ENABLE_RX 0x002 #define V_UART_CLOCK_DIVISOR_0 0x004 #define V_UART_CLOCK_DIVISOR_1 0x005 #define V_UART_CLOCK_DIVISOR_2 0x006 #define V_UART_TX_CLOCK_MASK_0 0x007 #define V_UART_TX_CLOCK_MASK_1 0x008 #define V_UART_RX_CLOCK_MASK_0 0x009 #define V_UART_RX_CLOCK_MASK_1 0x00a #define V_UART_FORMAT 0x00b #define V_UART_FORMAT_SIZE_M 0xf #define V_UART_FORMAT_SIZE_S 0 #define V_UART_FORMAT_SIZE 0x00f #define V_UART_FORMAT_SIZE_7 (0x7 << V_UART_FORMAT_SIZE_S) #define V_UART_FORMAT_SIZE_8 (0x8 << V_UART_FORMAT_SIZE_S) #define V_UART_FORMAT_SIZE_9 (0x9 << V_UART_FORMAT_SIZE_S) #define V_UART_FORMAT_PARITY_M 0x7 #define V_UART_FORMAT_PARITY_S 4 #define V_UART_FORMAT_PARITY 0x070 #define V_UART_FORMAT_PARITY_NONE (0x0 << V_UART_FORMAT_PARITY_S) #define V_UART_FORMAT_PARITY_ODD (0x1 << V_UART_FORMAT_PARITY_S) #define V_UART_FORMAT_PARITY_EVEN (0x2 << V_UART_FORMAT_PARITY_S) #define V_UART_FORMAT_PARITY_1 (0x3 << V_UART_FORMAT_PARITY_S) #define V_UART_FORMAT_PARITY_0 (0x4 << V_UART_FORMAT_PARITY_S) #define V_UART_FORMAT_STOP_M 0x1 #define V_UART_FORMAT_STOP_S 7 #define V_UART_FORMAT_STOP 0x080 #define V_UART_FORMAT_STOP_1 (0x0 << V_UART_FORMAT_STOP_S) #define V_UART_FORMAT_STOP_2 (0x1 << V_UART_FORMAT_STOP_S) #define V_UART_FORMAT_MODE_7N1 0 #define V_UART_FORMAT_MODE_RES1 1 #define V_UART_FORMAT_MODE_RES2 2 #define V_UART_FORMAT_MODE_RES3 3 #define V_UART_FORMAT_MODE_7N2 4 #define V_UART_FORMAT_MODE_7P1 5 #define V_UART_FORMAT_MODE_8N1 6 #define V_UART_FORMAT_MODE_RES7 7 #define V_UART_FORMAT_MODE_7P2 8 #define V_UART_FORMAT_MODE_8N2 9 #define V_UART_FORMAT_MODE_8P1 10 #define V_UART_FORMAT_MODE_9N1 11 #define V_UART_FORMAT_MODE_8P2 12 #define V_UART_FORMAT_MODE_RESD 13 #define V_UART_FORMAT_MODE_RESE 14 #define V_UART_FORMAT_MODE_9N2 15 #define V_UART_FLOW 0x00c #define V_UART_FLOW_MODE_M 0x7 #define V_UART_FLOW_MODE_S 0 #define V_UART_FLOW_MODE 0x007 #define V_UART_FLOW_MODE_NONE (0x0 << V_UART_FLOW_MODE_S) #define V_UART_FLOW_MODE_HW (0x1 << V_UART_FLOW_MODE_S) #define V_UART_FLOW_MODE_SW (0x2 << V_UART_FLOW_MODE_S) #define V_UART_FLOW_MODE_ADDR_MATCH (0x3 << V_UART_FLOW_MODE_S) #define V_UART_FLOW_MODE_ADDR_MATCH_TX (0x4 << V_UART_FLOW_MODE_S) #define V_UART_FLOW_HALF_DUPLEX_M 0x1 #define V_UART_FLOW_HALF_DUPLEX_S 3 #define V_UART_FLOW_HALF_DUPLEX 0x008 #define V_UART_XON_CHAR 0x010 #define V_UART_XOFF_CHAR 0x011 #define V_UART_GPIO_MODE 0x01a #define V_UART_GPIO_MODE_SEL_M 0x7 #define V_UART_GPIO_MODE_SEL_S 0 #define V_UART_GPIO_MODE_SEL 0x007 #define V_UART_GPIO_MODE_SEL_GPIO (0x0 << V_UART_GPIO_MODE_SEL_S) #define V_UART_GPIO_MODE_SEL_RTS_CTS (0x1 << V_UART_GPIO_MODE_SEL_S) #define V_UART_GPIO_MODE_SEL_DTR_DSR (0x2 << V_UART_GPIO_MODE_SEL_S) #define V_UART_GPIO_MODE_SEL_XCVR_EN_ACT (0x3 << V_UART_GPIO_MODE_SEL_S) #define V_UART_GPIO_MODE_SEL_XCVR_EN_FLOW (0x4 << V_UART_GPIO_MODE_SEL_S) #define V_UART_GPIO_MODE_XCVR_EN_POL_M 0x1 #define V_UART_GPIO_MODE_XCVR_EN_POL_S 3 #define V_UART_GPIO_MODE_XCVR_EN_POL 0x008 #define V_UART_GPIO_SET_RI 0x1 #define V_UART_GPIO_SET_CD 0x2 #define V_UART_GPIO_SET_DSR 0x4 #define V_UART_GPIO_SET_DTR 0x8 #define V_UART_GPIO_SET_CTS 0x10 #define V_UART_GPIO_SET_RTS 0x20 #define V_UART_GPIO_ITCH_MASK 0x1c #define V_URM_ENABLE_BASE 0x010 #define V_URM_ENABLE_0 0x010 #define V_URM_ENABLE_0_TX_M 0x1 #define V_URM_ENABLE_0_TX_S 0 #define V_URM_ENABLE_0_TX 0x001 #define V_URM_ENABLE_0_RX_M 0x1 #define V_URM_ENABLE_0_RX_S 1 #define V_URM_ENABLE_0_RX 0x002 #define V_PRM_RX_ITCH_BASE 0x014 /* ---------------------------------------------------------------- */ #define EXAR_SET_REG 0 #define EXAR_GET_REG 1 #if 0 static int exar_v_get_reg(struct usb_serial_port *port, int block, int reg, int *value) { struct usb_serial *serial = port->serial; int result; u8 data; result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), EXAR_GET_REG, USB_DIR_IN | USB_TYPE_VENDOR, 0, reg | (block << 8), &data, 1, USB_CTRL_GET_TIMEOUT); *value = data; TRACE_REG(&port->dev, "%s 0x%02x:0x%02x = 0x%02x\n", __func__, block, reg, *value); return result; } #endif static int exar_v_set_reg(struct usb_serial_port *port, int block, int reg, int value) { struct usb_serial *serial = port->serial; int result; TRACE_REG(&port->dev, "%s 0x%02x:0x%02x = 0x%02x\n", __func__, block, reg, value); result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), EXAR_SET_REG, USB_DIR_OUT | USB_TYPE_VENDOR, value, reg | (block << 8), NULL, 0, USB_CTRL_SET_TIMEOUT); return result; } /* ---------------------------------------------------------------- */ static int exar_v_disable(struct usb_serial_port *port) { struct exar_port_private *portdata = usb_get_serial_port_data(port); int block = portdata->block; exar_v_set_reg(port, block, V_UART_ENABLE, 0); exar_v_set_reg(port, V_URM_REG_BLOCK, V_URM_ENABLE_BASE + block, 0); return 0; } static int exar_v_enable(struct usb_serial_port *port) { struct exar_port_private *portdata = usb_get_serial_port_data(port); int block = portdata->block; exar_v_set_reg(port, V_URM_REG_BLOCK, V_URM_ENABLE_BASE + block, V_URM_ENABLE_0_TX); exar_v_set_reg(port, block, V_UART_ENABLE, V_UART_ENABLE_TX | V_UART_ENABLE_RX); exar_v_set_reg(port, V_URM_REG_BLOCK, V_URM_ENABLE_BASE + block, V_URM_ENABLE_0_TX | V_URM_ENABLE_0_RX); return 0; } /* ---------------------------------------------------------------- */ static int acm_ctrl_msg(struct usb_serial_port *port, int request, int value, void *buf, int len) { struct usb_serial *serial = port->serial; int retval = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), request, USB_RT_ACM, value, serial->interface->cur_altsetting->desc. bInterfaceNumber, buf, len, 5000); TRACE_FUNC(&port->dev, "%s rq: 0x%02x val: %#x len: %#x result: %d\n", __func__, request, value, len, retval); return retval < 0 ? retval : 0; } #define acm_set_control(port, control) \ acm_ctrl_msg(port, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) #define acm_set_line(port, line) \ acm_ctrl_msg(port, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) #define acm_send_break(port, ms) \ acm_ctrl_msg(port, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) struct exar_v_baud_rate { unsigned int tx; unsigned int rx0; unsigned int rx1; }; static struct exar_v_baud_rate exar_v_baud_rates[] = { {0x000, 0x000, 0x000}, {0x000, 0x000, 0x000}, {0x100, 0x000, 0x100}, {0x020, 0x400, 0x020}, {0x010, 0x100, 0x010}, {0x208, 0x040, 0x208}, {0x104, 0x820, 0x108}, {0x844, 0x210, 0x884}, {0x444, 0x110, 0x444}, {0x122, 0x888, 0x224}, {0x912, 0x448, 0x924}, {0x492, 0x248, 0x492}, {0x252, 0x928, 0x292}, {0X94A, 0X4A4, 0XA52}, {0X52A, 0XAA4, 0X54A}, {0XAAA, 0x954, 0X4AA}, {0XAAA, 0x554, 0XAAA}, {0x555, 0XAD4, 0X5AA}, {0XB55, 0XAB4, 0X55A}, {0X6B5, 0X5AC, 0XB56}, {0X5B5, 0XD6C, 0X6D6}, {0XB6D, 0XB6A, 0XDB6}, {0X76D, 0X6DA, 0XBB6}, {0XEDD, 0XDDA, 0X76E}, {0XDDD, 0XBBA, 0XEEE}, {0X7BB, 0XF7A, 0XDDE}, {0XF7B, 0XEF6, 0X7DE}, {0XDF7, 0XBF6, 0XF7E}, {0X7F7, 0XFEE, 0XEFE}, {0XFDF, 0XFBE, 0X7FE}, {0XF7F, 0XEFE, 0XFFE}, {0XFFF, 0XFFE, 0XFFD}, }; static int exar_v_set_baud_rate(struct usb_serial_port *port, unsigned int rate) { struct exar_port_private *portdata = usb_get_serial_port_data(port); int block = portdata->block; unsigned int div = 48000000 / rate; unsigned int i = ((32 * 48000000) / rate) & 0x1f; unsigned int tx = exar_v_baud_rates[i].tx; unsigned int rx = (div & 1) ? exar_v_baud_rates[i].rx1 : exar_v_baud_rates[i].rx0; TRACE_CONFIG(&port->dev, "Setting baud rate to %d: i=%u div=%u tx=%03x rx=%03x\n", rate, i, div, tx, rx); exar_v_set_reg(port, block, V_UART_CLOCK_DIVISOR_0, (div >> 0) & 0xff); exar_v_set_reg(port, block, V_UART_CLOCK_DIVISOR_1, (div >> 8) & 0xff); exar_v_set_reg(port, block, V_UART_CLOCK_DIVISOR_2, (div >> 16) & 0xff); exar_v_set_reg(port, block, V_UART_TX_CLOCK_MASK_0, (tx >> 0) & 0xff); exar_v_set_reg(port, block, V_UART_TX_CLOCK_MASK_1, (tx >> 8) & 0xff); exar_v_set_reg(port, block, V_UART_RX_CLOCK_MASK_0, (rx >> 0) & 0xff); exar_v_set_reg(port, block, V_UART_RX_CLOCK_MASK_1, (rx >> 8) & 0xff); return -EINVAL; } static void exar_v_set_termios(struct tty_struct *tty_param, struct usb_serial_port *port, struct ktermios *old_termios) { struct exar_port_private *portdata = usb_get_serial_port_data(port); unsigned int cflag, block; speed_t rate; unsigned int format_size, format_parity, format_stop, flow, gpio_mode, itch; struct tty_struct *tty; char cs, p, sb, *fl; TRACE_FUNC(&port->dev, "%s\n", __func__); tty = port->port.tty; cflag = tty->termios->c_cflag; portdata->clocal = ((cflag & CLOCAL) != 0); block = portdata->block; exar_v_disable(port); if ((cflag & CSIZE) == CS7) { format_size = V_UART_FORMAT_SIZE_7; cs = '7'; } else { format_size = V_UART_FORMAT_SIZE_8; cs = '8'; } if (cflag & PARENB) { if (cflag & PARODD) { if (cflag & CMSPAR) { format_parity = V_UART_FORMAT_PARITY_1; p = 'M'; } else { format_parity = V_UART_FORMAT_PARITY_ODD; p = 'O'; } } else { if (cflag & CMSPAR) { format_parity = V_UART_FORMAT_PARITY_0; p = 'S'; } else { format_parity = V_UART_FORMAT_PARITY_EVEN; p = 'E'; } } } else { format_parity = V_UART_FORMAT_PARITY_NONE; p = 'n'; } if (cflag & CSTOPB) { format_stop = V_UART_FORMAT_STOP_2; sb = '2'; } else { format_stop = V_UART_FORMAT_STOP_1; sb = '1'; } exar_v_set_reg(port, block, V_UART_FORMAT, format_size | format_parity | format_stop); if (cflag & CRTSCTS) { flow = V_UART_FLOW_MODE_HW; gpio_mode = V_UART_GPIO_MODE_SEL_RTS_CTS; itch = (V_UART_GPIO_SET_RI | V_UART_GPIO_SET_CD | V_UART_GPIO_SET_DSR | V_UART_GPIO_SET_DTR); fl = "rtscts"; } else if (I_IXOFF(tty) || I_IXON(tty)) { unsigned char start_char = START_CHAR(tty); unsigned char stop_char = STOP_CHAR(tty); flow = V_UART_FLOW_MODE_SW; gpio_mode = V_UART_GPIO_MODE_SEL_GPIO; itch = (V_UART_GPIO_SET_RI | V_UART_GPIO_SET_CD | V_UART_GPIO_SET_DSR | V_UART_GPIO_SET_DTR | V_UART_GPIO_SET_CTS | V_UART_GPIO_SET_RTS); exar_v_set_reg(port, block, V_UART_XON_CHAR, start_char); exar_v_set_reg(port, block, V_UART_XOFF_CHAR, stop_char); fl = "xonxoff"; } else { flow = V_UART_FLOW_MODE_NONE; gpio_mode = V_UART_GPIO_MODE_SEL_GPIO; itch = (V_UART_GPIO_SET_RI | V_UART_GPIO_SET_CD | V_UART_GPIO_SET_DSR | V_UART_GPIO_SET_DTR | V_UART_GPIO_SET_CTS | V_UART_GPIO_SET_RTS); fl = "none"; } TRACE_CONFIG(&port->dev, "Setting line to %c%c%c %s\n", cs, p, sb, fl); exar_v_set_reg(port, block, V_UART_FLOW, flow); exar_v_set_reg(port, block, V_UART_GPIO_MODE, gpio_mode); exar_v_set_reg(port, block, V_UART_GPIO_ITCH_MASK, itch); rate = tty_get_baud_rate(tty); exar_v_set_baud_rate(port, rate); exar_v_enable(port); } static void exar_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { TRACE_FUNC(&port->dev, "%s\n", __func__); exar_v_set_termios(tty, port, old_termios); } static int exar_tiocmget(struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; struct exar_port_private *portdata = usb_get_serial_port_data(port); TRACE_FUNC(&port->dev, "%s\n", __func__); return (portdata->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | (portdata->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | (portdata->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | (portdata->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | (portdata->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | TIOCM_CTS; } static int exar_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct exar_port_private *portdata = usb_get_serial_port_data(port); unsigned int newctrl; TRACE_FUNC(&port->dev, "%s\n", __func__); newctrl = portdata->ctrlout; set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); newctrl = (newctrl & ~clear) | set; if (portdata->ctrlout == newctrl) return 0; return acm_set_control(port, portdata->ctrlout = newctrl); } /* ---------------------------------------------------------------- */ static struct urb *exar_setup_urb(struct usb_serial *serial, int endpoint, int dir, void *ctx, char *buf, int len, void (*callback) (struct urb *)) { struct urb *urb; urb = usb_alloc_urb(0, GFP_KERNEL); if (urb == NULL) { TRACE_URB(&serial->dev->dev, "%s: alloc for endpoint %d failed.", __func__, endpoint); return NULL; } usb_fill_bulk_urb(urb, serial->dev, usb_sndbulkpipe(serial->dev, endpoint) | dir, buf, len, callback, ctx); return urb; } static int exar_attach(struct usb_serial *serial) { struct usb_serial_port *port; struct exar_port_private *portdata; u8 *buffer; struct urb *urb; int j, err; TRACE_FUNC(&serial->dev->dev, "%s: %04x\n", __func__, serial->dev->descriptor.idProduct); usb_enable_autosuspend(serial->dev); port = serial->port[0]; portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); if (!portdata) { dev_err(&port->dev, "%s: kzalloc for exar_port_private failed!\n", __func__); return 1; } portdata->product_id = serial->dev->descriptor.idProduct; /* Bulk OUT endpoints 0x1..0x4 map to register blocks 0..3 */ portdata->block = port->bulk_out_endpointAddress - 1; usb_set_serial_port_data(port, portdata); for (j = 0; j < N_IN_URB; j++) { buffer = (u8 *) __get_free_page(GFP_KERNEL); portdata->in_buffer[j] = buffer; urb = exar_setup_urb(serial, port->bulk_in_endpointAddress, USB_DIR_IN, port, portdata->in_buffer[j], IN_BUFLEN, exar_indat_callback); portdata->in_urbs[j] = urb; if (!buffer || !urb) { dev_err(&port->dev, "%s: IN allocation failed buffer=%p urb=%p", __func__, buffer, urb); goto bail_out_error; } } for (j = 0; j < N_OUT_URB; j++) { buffer = (u8 *) __get_free_page(GFP_KERNEL); portdata->out_buffer[j] = buffer; urb = exar_setup_urb(serial, port->bulk_out_endpointAddress, USB_DIR_OUT, port, portdata->out_buffer[j], OUT_BUFLEN, exar_outdat_callback); portdata->out_urbs[j] = urb; if (!buffer || !urb) { dev_err(&port->dev, "%s: OUT allocation failed buffer=%p urb=%p", __func__, buffer, urb); goto bail_out_error; } } if (port->interrupt_in_urb) { err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (err) dev_err(&port->dev, "%s: submit irq_in urb failed %d", __func__, err); } return 0; bail_out_error: for (j = 0; j < N_IN_URB; j++) { if (portdata->in_buffer[j]) free_page((unsigned long)portdata->in_buffer[j]); if (portdata->in_urbs[j]) usb_free_urb(portdata->in_urbs[j]); } for (j = 0; j < N_OUT_URB; j++) { if (portdata->out_buffer[j]) free_page((unsigned long)portdata->out_buffer[j]); if (portdata->out_urbs[j]) usb_free_urb(portdata->out_urbs[j]); } kfree(portdata); return 1; } static void stop_read_write_urbs(struct usb_serial *serial) { struct usb_serial_port *port; struct exar_port_private *portdata; int i, j; for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; portdata = usb_get_serial_port_data(port); for (j = 0; j < N_IN_URB; j++) usb_kill_urb(portdata->in_urbs[j]); for (j = 0; j < N_OUT_URB; j++) usb_kill_urb(portdata->out_urbs[j]); } } static void exar_disconnect(struct usb_serial *serial) { TRACE_FUNC(&serial->dev->dev, "%s\n", __func__); stop_read_write_urbs(serial); } static void exar_release(struct usb_serial *serial) { struct usb_serial_port *port; struct exar_port_private *portdata; int j; TRACE_FUNC(&serial->dev->dev, "%s\n", __func__); port = serial->port[0]; portdata = usb_get_serial_port_data(port); return; for (j = 0; j < N_IN_URB; j++) { if (portdata->in_urbs[j]) { usb_free_urb(portdata->in_urbs[j]); free_page((unsigned long) portdata->in_buffer[j]); portdata->in_urbs[j] = NULL; } } for (j = 0; j < N_OUT_URB; j++) { if (portdata->out_urbs[j]) { usb_free_urb(portdata->out_urbs[j]); free_page((unsigned long) portdata->out_buffer[j]); portdata->out_urbs[j] = NULL; } } kfree(usb_get_serial_port_data(port)); } /* ---------------------------------------------------------------- */ static int exar_open(struct tty_struct *tty, struct usb_serial_port *port) { struct exar_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; struct urb *urb; int i, err; TRACE_FUNC(&port->dev, "%s\n", __func__); for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; if (!urb) continue; err = usb_submit_urb(urb, GFP_KERNEL); if (err) { dev_err(&port->dev, "%s: submit urb %d failed (%d) %d", __func__, i, err, urb->transfer_buffer_length); } } /* serial->interface->needs_remote_wakeup = 1; */ spin_lock_irq(&portdata->susp_lock); portdata->opened = 1; spin_unlock_irq(&portdata->susp_lock); /* usb_autopm_put_interface(serial->interface); */ return 0; } static void exar_close(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct exar_port_private *portdata = usb_get_serial_port_data(port); int i; TRACE_FUNC(&port->dev, "%s\n", __func__); if (serial->dev) { /* Stop reading/writing urbs */ spin_lock_irq(&portdata->susp_lock); portdata->opened = 0; spin_unlock_irq(&portdata->susp_lock); for (i = 0; i < N_IN_URB; i++) usb_kill_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); /* usb_autopm_get_interface(serial->interface); */ /* serial->interface->needs_remote_wakeup = 0; */ } } /* ---------------------------------------------------------------- */ static void exar_outdat_callback(struct urb *urb) { struct usb_serial_port *port; struct exar_port_private *portdata; int i; port = urb->context; TRACE_DATA(&port->dev, "%s\n", __func__); usb_serial_port_softint(port); usb_autopm_put_interface_async(port->serial->interface); portdata = usb_get_serial_port_data(port); spin_lock(&portdata->susp_lock); portdata->in_flight--; spin_unlock(&portdata->susp_lock); for (i = 0; i < N_OUT_URB; ++i) { if (portdata->out_urbs[i] == urb) { smp_mb__before_clear_bit(); clear_bit(i, &portdata->out_busy); break; } } } static int exar_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { struct exar_port_private *portdata; struct urb *this_urb; unsigned long flags; int i, err, left, todo; portdata = usb_get_serial_port_data(port); TRACE_FUNC(&port->dev, "%s: write (%d chars)\n", __func__, count); i = 0; left = count; for (i = 0; left > 0 && i < N_OUT_URB; i++) { todo = left; if (todo > OUT_BUFLEN) todo = OUT_BUFLEN; this_urb = portdata->out_urbs[i]; if (test_and_set_bit(i, &portdata->out_busy)) { if (time_before(jiffies, portdata->tx_start_time[i] + 10 * HZ)) continue; usb_unlink_urb(this_urb); continue; } err = usb_autopm_get_interface_async(port->serial->interface); if (err < 0) break; /* send the data */ memcpy(this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; spin_lock_irqsave(&portdata->susp_lock, flags); if (portdata->suspended) { usb_anchor_urb(this_urb, &portdata->delayed); spin_unlock_irqrestore(&portdata->susp_lock, flags); } else { portdata->in_flight++; spin_unlock_irqrestore(&portdata->susp_lock, flags); err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err) { TRACE_URB(&port->dev, "usb_submit_urb %p (write bulk) failed %d\n", this_urb, err); clear_bit(i, &portdata->out_busy); spin_lock_irqsave(&portdata->susp_lock, flags); portdata->in_flight--; spin_unlock_irqrestore(&portdata->susp_lock, flags); continue; } } portdata->tx_start_time[i] = jiffies; buf += todo; left -= todo; } count -= left; TRACE_DATA(&port->dev, "%s: wrote (did %d)\n", __func__, count); return count; } static int exar_write_room(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct exar_port_private *portdata; struct urb *this_urb; int data_len = 0; int i; TRACE_FUNC(&port->dev, "%s\n", __func__); portdata = usb_get_serial_port_data(port); for (i = 0; i < N_OUT_URB; i++) { this_urb = portdata->out_urbs[i]; if (this_urb && !test_bit(i, &portdata->out_busy)) data_len += OUT_BUFLEN; } TRACE_DATA(&port->dev, "%s: %d\n", __func__, data_len); return data_len; } static int exar_chars_in_buffer(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct exar_port_private *portdata; struct urb *this_urb; int data_len = 0; int i; TRACE_FUNC(&port->dev, "%s\n", __func__); portdata = usb_get_serial_port_data(port); for (i = 0; i < N_OUT_URB; i++) { this_urb = portdata->out_urbs[i]; /* FIXME: This locking is insufficient as this_urb may go unused during the test */ if (this_urb && test_bit(i, &portdata->out_busy)) data_len += this_urb->transfer_buffer_length; } TRACE_DATA(&port->dev, "%s: %d\n", __func__, data_len); return data_len; } static void exar_indat_callback(struct urb *urb) { struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int status = urb->status; int err, endpoint; port = urb->context; TRACE_DATA(&port->dev, "%s: %p\n", __func__, urb); endpoint = usb_pipeendpoint(urb->pipe); if (status) { TRACE_URB(&port->dev, "%s: nonzero status: %d on endpoint %02x\n", __func__, status, endpoint); } else { tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } else TRACE_DATA(&port->dev, "%s: empty read urb received\n", __func__); tty_kref_put(tty); /* Resubmit urb so we continue receiving */ if (status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err && err != -EPERM) dev_err(&port->dev, "%s: resubmit read urb failed with %d\n", __func__, err); else usb_mark_last_busy(port->serial->dev); } } } static void exar_int_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct exar_port_private *portdata = usb_get_serial_port_data(port); struct usb_cdc_notification *dr = urb->transfer_buffer; unsigned char *data; int newctrl; int status; switch (urb->status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ TRACE_INTERRUPT(&port->dev, "urb shutting down with status: %d\n", urb->status); return; default: dev_err(&port->dev, "nonzero urb status received: %d\n", urb->status); goto exit; } data = (unsigned char *)(dr + 1); switch (dr->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: TRACE_INTERRUPT(&port->dev, "%s network\n", dr-> wValue ? "connected to" : "disconnected from"); break; case USB_CDC_NOTIFY_SERIAL_STATE: newctrl = le16_to_cpu(get_unaligned((__le16 *) data)); if (!portdata->clocal && (portdata->ctrlin & ~newctrl & ACM_CTRL_DCD)) { struct tty_struct *tty = tty_port_tty_get(&port->port); TRACE_INTERRUPT(&port->dev, "calling hangup\n"); tty_hangup(tty); } portdata->ctrlin = newctrl; TRACE_INTERRUPT(&port->dev, "input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c\n", portdata->ctrlin & ACM_CTRL_DCD ? '+' : '-', portdata->ctrlin & ACM_CTRL_DSR ? '+' : '-', portdata->ctrlin & ACM_CTRL_BRK ? '+' : '-', portdata->ctrlin & ACM_CTRL_RI ? '+' : '-', portdata->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', portdata->ctrlin & ACM_CTRL_PARITY ? '+' : '-', portdata-> ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); break; default: TRACE_INTERRUPT(&port->dev, "unknown notification %d received: index %d len %d data0 %d data1 %d\n", dr->bNotificationType, dr->wIndex, dr->wLength, data[0], data[1]); break; } exit: TRACE_URB(&port->dev, "resubmitting interrupt IN urb %p\n", urb); status = usb_submit_urb(urb, GFP_ATOMIC); if (status) dev_err(&port->dev, "usb_submit_urb failed with result %d", status); } #ifdef CONFIG_PM static int exar_suspend(struct usb_serial *serial, pm_message_t message) { struct usb_serial_port *port; struct exar_port_private *portdata; int b; TRACE_FUNC(&serial->dev->dev, "%s\n", __func__); port = serial->port[0]; portdata = usb_get_serial_port_data(port); if (message.event & PM_EVENT_AUTO) { spin_lock_irq(&portdata->susp_lock); b = portdata->in_flight; spin_unlock_irq(&portdata->susp_lock); if (b) return -EBUSY; } spin_lock_irq(&portdata->susp_lock); portdata->suspended = 1; spin_unlock_irq(&portdata->susp_lock); stop_read_write_urbs(serial); return 0; } static void play_delayed(struct usb_serial_port *port) { struct exar_port_private *portdata; struct urb *urb; int err; TRACE_FUNC(&port->dev, "%s\n", __func__); portdata = usb_get_serial_port_data(port); while ((urb = usb_get_from_anchor(&portdata->delayed))) { err = usb_submit_urb(urb, GFP_ATOMIC); if (!err) portdata->in_flight++; } } static int exar_resume(struct usb_serial *serial) { struct usb_serial_port *port; struct exar_port_private *portdata; struct urb *urb; int err = 0, j; TRACE_FUNC(&serial->dev->dev, "%s\n", __func__); port = serial->port[0]; portdata = usb_get_serial_port_data(port); /* get the interrupt URBs resubmitted unconditionally */ if (port->interrupt_in_urb) { err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); if (err < 0) { dev_err(&port->dev, "%s: Error %d for interrupt URB of port", __func__, err); goto err_out; } } /* skip closed ports */ spin_lock_irq(&portdata->susp_lock); if (portdata->opened) { for (j = 0; j < N_IN_URB; j++) { urb = portdata->in_urbs[j]; err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { dev_err(&port->dev, "%s: Error %d for bulk URB\n", __func__, err); spin_unlock_irq(&portdata->susp_lock); goto err_out; } } play_delayed(port); } portdata->suspended = 0; spin_unlock_irq(&portdata->susp_lock); err_out: return err; } #endif static const struct usb_device_id id_table[] = { {USB_DEVICE(EXAR_VENDOR_ID, EXAR_PRODUCT_ID_XRV1410)}, {USB_DEVICE(EXAR_VENDOR_ID, EXAR_PRODUCT_ID_XRV1412)}, {USB_DEVICE(EXAR_VENDOR_ID, EXAR_PRODUCT_ID_XRV1414)}, {}, }; MODULE_DEVICE_TABLE(usb, id_table); /* *INDENT-OFF* */ static struct usb_driver exar_driver = { .name = "exar", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, #ifdef CONFIG_PM .suspend = usb_serial_suspend, .resume = usb_serial_resume, .supports_autosuspend = 1, #endif .no_dynamic_id = 1, }; static struct usb_serial_driver exar_device = { .description = "Exar serial", .driver = { .owner = THIS_MODULE, .name = "exar", }, .id_table = id_table, .usb_driver = &exar_driver, .num_ports = 1, .set_termios = exar_set_termios, .tiocmget = exar_tiocmget, .tiocmset = exar_tiocmset, .attach = exar_attach, .disconnect = exar_disconnect, .release = exar_release, .open = exar_open, .close = exar_close, .write = exar_write, .write_room = exar_write_room, .chars_in_buffer = exar_chars_in_buffer, .read_int_callback = exar_int_callback, #ifdef CONFIG_PM .suspend = exar_suspend, .resume = exar_resume, #endif }; /* *INDENT-ON* */ static int __init exar_init(void) { int retval; retval = usb_serial_register(&exar_device); if (retval) return retval; retval = usb_register(&exar_driver); if (retval) usb_serial_deregister(&exar_device); printk(KERN_INFO DRIVER_DESC ": " DRIVER_VERSION "\n"); return retval; } static void __exit exar_exit(void) { usb_deregister(&exar_driver); usb_serial_deregister(&exar_device); } module_init(exar_init); module_exit(exar_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug flags"); The information and any attached documents contained in this message may be confidential and/or legally privileged. The message is intended solely for the addressee(s). If you are not the intended recipient, you are hereby notified that any use, dissemination, or reproduction is strictly prohibited and may be unlawful. If you are not the intended recipient, please contact the sender immediately by return e-mail and destroy all copies of the original message. -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html