/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Copyright (c) 2015 Exar Corporation, Inc. * * This driver will work with any USB UART function in these Exar devices: * XR21V1410/1412/1414 * XR21B1411 * XR21B1420/1422/1424 * XR22801/802/804 * * The driver has been tested on various kernel versions from 3.6.x to 3.17.x. * This driver may work on newer versions as well. There is a different driver available * from www.exar.com that will work with kernel versions 2.6.18 to 3.4.x. * * ChangeLog: * Version 1B - Initial released version. * Version 1C - Add 9-bit mode support * Version 1D-pog422 - Table-8 GPIO interrupt + WAIT_GPIO1 ioctl (POG422 fixture) * Version 1F-pog422 - RS485 auto (GPIO_MODE 0x0339); SET_GPIO7 ioctl disabled * Version 1G-pog422 - MARK/SPACE parity flip without UART disable (RS485_EN_ACT hold) * Version 1H-pog422 - Fix preciseflags orphan byte across URBs; RX stats ioctls * Version 1I-pog422 - DTtty driver RX ring (bypass kernel tty flip for capture) * Version 1J-pog422 - Kernel-side wide-mode TX inject (INJECT_TX ioctl) * Version 1K-pog422 - INJECT_TX waits TX_FIFO empty before wide off (RS485 DE hold) * Version 1L-pog422 - INJECT_TX wire-time hold before wide off (replaces 1K FIFO poll) * Version 1M-pog422 - INJECT_TX wide off without UART disable (1G-style RS485 hold) * Version 1P-pog422 - INJECT_TX MANUAL_DE: wire wait before wide off (DE in C) * Version 1O-pog422 - INJECT_TX manual DE: skip kernel wire wait (0xFFFFFFFE) * Version 1Q-pog422 - Wide/preciseflags only: no per-inject wide toggle in INJECT_TX * Version 1R-pog422 - SET_PRECISE_FLAGS ioctl returns 0 (was ENOTTY) * Version 1S-pog422 - INJECT_TX reports TX_FIFO + usb_actual around bulk (diag) * Version 1T-pog422 - Fix TX_FIFO get_reg success check (usb_control_msg returns len) * Version 1U-pog422 - TX_FIFO via get_reg_ext UART block (ep-4), not get_reg *2 * Version 1V-pog422 - TX_FIFO USB buffer kmalloc (stack DMA fails → 0xFFFFFFFF) * Version 2.2.1.12 - BOM: hybrid DE release (min TEMT, air+margin) * Version 2.2.1.13 - BOM: MANUAL_DE asserts GPIO7 inside INJECT_TX (after mutex) * Version 2.2.2.12 - INJECT_TX EXTERNAL_DE: skip kernel wire usleep (C owns DE) * Version 2.2.2.11 - INJECT_TX EXTERNAL_DE: skip FIFO get_reg, never GPIO7 * Version 2.2.2.10 - MANUAL_DE hard-cap + GPIO7 100ms USB ctrl (no 5s DE) * Version 2.2.2.9 - Runtime-gated HS/RX timing ring (SET/DRAIN_TIMING_TRACE) * Version 2.2.2.8 - INJECT_TX MANUAL_DE wall-clock DE drop (orphan HS) * Version 2.2.2.7 - BOM: Partial 0x0E wire len 598 (live HS) * Version 2.2.2.6 - BOM: Partial Transfer 0x04 wire len 32 * Version 2.2.2.5 - BOM: INJECT_TX chunk to writesize (MTC 0x09 USB timeout) * Version 2.2.2.4 - BOM: INJECT_TX frame max 2048 (MTC 0x09 @1170) * Version 2.2.2.3 - BOM: MTC 0x09 wire len 1170 (live HS; 1094 alt) * Version 2.2.2.1 - BOM: inject-window peer TxD UART→ETH (Python); driver path unchanged * Version 2.2.1.17 - BOM: SCR transaction path (Python); driver path unchanged * Version 2.2.1.16 - BOM: uartd CS5/9-bit so wide RX flag0 = 9th data bit * Version 2.2.1.15 - BOM: always-on wide RX tee + HS bit 0x10 in flags * Version 2.2.1.14 - BOM: restore cheap SLV-IN samples during DE (misalign truth) * Version 2.2.1.6 - BOM: HW debug Table-8 GPIO1/GPIO5 edge ring (compile-out) * Version 2.2.1.5 - BOM: no pre-TX settle (inject C); driver path unchanged * Version 2.2.1.4 - BOM: no post-DE settle (inject C); driver path unchanged * Version 2.2.1.3 - BOM match (uartd wire-len sync; driver path unchanged) * Version 2.2.1.0 - WAIT_TX_EMPTY via USB interrupt IN (GPIO5 TX-sense) * Version 2.2.0.0 - MANUAL_DE skips air usleep; WAIT_TX_EMPTY (FIFO poll) * Version 2.1.0.0 - EG BOM; TX_FIFO errno encode retained from 1W * Version 1W-pog422 - TX_FIFO fail encodes ch+errno; retry UART block 2 */ //#undef DEBUG #undef VERBOSE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/version.h" #include "xr_usb_serial_common.h" #include "xr_usb_serial_ioctl.h" #define DRIVER_AUTHOR "" #define DRIVER_DESC "Exar USB UART (serial port) driver" static struct usb_driver xr_usb_serial_driver; static struct tty_driver *xr_usb_serial_tty_driver; static struct xr_usb_serial *xr_usb_serial_table[XR_USB_SERIAL_TTY_MINORS]; static DEFINE_MUTEX(xr_usb_serial_table_lock); /* * xr_usb_serial_table accessors */ /* * Look up an XR_USB_SERIAL structure by index. If found and not disconnected, increment * its refcount and return it with its mutex held. */ static struct xr_usb_serial *xr_usb_serial_get_by_index(unsigned index) { struct xr_usb_serial *xr_usb_serial; mutex_lock(&xr_usb_serial_table_lock); xr_usb_serial = xr_usb_serial_table[index]; if (xr_usb_serial) { mutex_lock(&xr_usb_serial->mutex); if (xr_usb_serial->disconnected) { mutex_unlock(&xr_usb_serial->mutex); xr_usb_serial = NULL; } else { tty_port_get(&xr_usb_serial->port); mutex_unlock(&xr_usb_serial->mutex); } } mutex_unlock(&xr_usb_serial_table_lock); return xr_usb_serial; } /* * Try to find an available minor number and if found, associate it with 'xr_usb_serial'. */ static int xr_usb_serial_alloc_minor(struct xr_usb_serial *xr_usb_serial) { int minor; mutex_lock(&xr_usb_serial_table_lock); for (minor = 0; minor < XR_USB_SERIAL_TTY_MINORS; minor++) { if (!xr_usb_serial_table[minor]) { xr_usb_serial_table[minor] = xr_usb_serial; break; } } mutex_unlock(&xr_usb_serial_table_lock); return minor; } /* Release the minor number associated with 'xr_usb_serial'. */ static void xr_usb_serial_release_minor(struct xr_usb_serial *xr_usb_serial) { mutex_lock(&xr_usb_serial_table_lock); xr_usb_serial_table[xr_usb_serial->minor] = NULL; mutex_unlock(&xr_usb_serial_table_lock); } /* * Functions for XR_USB_SERIAL control messages. */ static int xr_usb_serial_ctrl_msg(struct xr_usb_serial *xr_usb_serial, int request, int value, void *buf, int len) { int retval = usb_control_msg(xr_usb_serial->dev, usb_sndctrlpipe(xr_usb_serial->dev, 0), request, USB_RT_XR_USB_SERIAL, value, xr_usb_serial->control->altsetting[0].desc.bInterfaceNumber, buf, len, 5000); dev_dbg(&xr_usb_serial->control->dev, "%s - rq 0x%02x, val %#x, len %#x, result %d\n", __func__, request, value, len, retval); return retval < 0 ? retval : 0; } #include "xr_usb_serial_hal.c" /* * Write buffer management. * All of these assume proper locks taken by the caller. */ static int xr_usb_serial_wb_alloc(struct xr_usb_serial *xr_usb_serial) { int i, wbn; struct xr_usb_serial_wb *wb; wbn = 0; i = 0; for (;;) { wb = &xr_usb_serial->wb[wbn]; if (!wb->use) { wb->use = 1; return wbn; } wbn = (wbn + 1) % XR_USB_SERIAL_NW; if (++i >= XR_USB_SERIAL_NW) return -1; } } static int xr_usb_serial_wb_is_avail(struct xr_usb_serial *xr_usb_serial) { int i, n; unsigned long flags; n = XR_USB_SERIAL_NW; spin_lock_irqsave(&xr_usb_serial->write_lock, flags); for (i = 0; i < XR_USB_SERIAL_NW; i++) n -= xr_usb_serial->wb[i].use; spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); return n; } /* * Finish write. Caller must hold xr_usb_serial->write_lock */ static void xr_usb_serial_write_done(struct xr_usb_serial *xr_usb_serial, struct xr_usb_serial_wb *wb) { wb->use = 0; xr_usb_serial->transmitting--; usb_autopm_put_interface_async(xr_usb_serial->control); } /* * Poke write. * * the caller is responsible for locking */ static int xr_usb_serial_start_wb(struct xr_usb_serial *xr_usb_serial, struct xr_usb_serial_wb *wb) { int rc; xr_usb_serial->transmitting++; wb->urb->transfer_buffer = wb->buf; wb->urb->transfer_dma = wb->dmah; wb->urb->transfer_buffer_length = wb->len; wb->urb->dev = xr_usb_serial->dev; rc = usb_submit_urb(wb->urb, GFP_ATOMIC); if (rc < 0) { dev_err(&xr_usb_serial->data->dev, "%s - usb_submit_urb(write bulk) failed: %d\n", __func__, rc); xr_usb_serial_write_done(xr_usb_serial, wb); } return rc; } /* * attributes exported through sysfs */ static ssize_t show_caps (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); return sprintf(buf, "%d", xr_usb_serial->ctrl_caps); } static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); static ssize_t show_country_codes (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); memcpy(buf, xr_usb_serial->country_codes, xr_usb_serial->country_code_size); return xr_usb_serial->country_code_size; } static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); static ssize_t show_country_rel_date (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); return sprintf(buf, "%d", xr_usb_serial->country_rel_date); } static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); static ssize_t set_rs485_422_en(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); int error, value = 0; error = kstrtoint(buf, 0, &value); if (error) return error; if (value == 0) { xr_usb_serial->rs485_422_en = false; } else if (value == 1) { // RS485,RS422 HD/FD mode xr_usb_serial->rs485_422_en = true; } return count; } static ssize_t show_rs485_422_en(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); if (xr_usb_serial->rs485_422_en == false) { return sprintf(buf, "0"); } else if (xr_usb_serial->rs485_422_en == true) { // RS485,RS422 HD/FD mode return sprintf(buf, "1"); } return 0; } static DEVICE_ATTR(bRS485_422_en, 0644, show_rs485_422_en, set_rs485_422_en); /* * Interrupt handlers for various XR_USB_SERIAL device responses */ static void xr_usb_serial_pog422_fill_event(struct xr_usb_serial *xr_usb_serial, struct xr_pog422_gpio1_event *ev) { unsigned long flags; spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); ev->timestamp_ns = xr_usb_serial->last_gpio1_event_ns; ev->gpio_state = xr_usb_serial->last_gpio_state; ev->gpio_int = xr_usb_serial->last_gpio_int; ev->slv_in_active = xr_usb_serial->last_slv_in_active; memset(ev->_pad, 0, sizeof(ev->_pad)); spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); } /* Caller holds gpio1_lock. */ static void pog422_timing_push_locked(struct xr_usb_serial *xr_usb_serial, u32 type, u64 ts_ns, u16 gpio_state, u16 gpio_int, u8 flags, u16 nbytes, u64 rx_chars) { u32 slot; if (!xr_usb_serial->timing_enabled) return; slot = xr_usb_serial->timing_head; xr_usb_serial->timing_ts[slot] = ts_ns; xr_usb_serial->timing_type[slot] = type; xr_usb_serial->timing_gpio_state[slot] = gpio_state; xr_usb_serial->timing_gpio_int[slot] = gpio_int; xr_usb_serial->timing_flags[slot] = flags; xr_usb_serial->timing_nbytes[slot] = nbytes; xr_usb_serial->timing_rx_chars[slot] = rx_chars; xr_usb_serial->timing_head = (slot + 1u) % POG422_TIMING_RING_MAX; if (xr_usb_serial->timing_count < POG422_TIMING_RING_MAX) xr_usb_serial->timing_count++; else xr_usb_serial->timing_dropped++; xr_usb_serial->timing_total_pushed++; } static void xr_usb_serial_pog422_handle_gpio_irq(struct xr_usb_serial *xr_usb_serial, unsigned char *buf, int len) { u16 gpio_state, gpio_int; u8 slv_in_active; u8 gpio5_active; u8 gpio5_fell = 0; u8 prev_slv; u8 prev_gpio5; u8 flags_tm = 0; unsigned long flags; #if defined(POG422_HW_DEBUG) && (POG422_HW_DEBUG) u8 flags_hw = 0; u32 slot; #endif if (len < 4) return; gpio_state = get_unaligned_le16(buf); gpio_int = get_unaligned_le16(buf + 2); slv_in_active = (gpio_state & POG422_GPIO1_STATUS_BIT) ? 0 : 1; /* RS485_EN_ACT active-high on GPIO5: high while UART still transmitting. */ gpio5_active = (gpio_state & POG422_GPIO5_BIT) ? 1 : 0; spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); prev_slv = xr_usb_serial->last_slv_in_active; prev_gpio5 = xr_usb_serial->last_gpio5_tx_active; if (xr_usb_serial->last_gpio5_tx_active && !gpio5_active) gpio5_fell = 1; else if ((gpio_int & POG422_GPIO5_BIT) && !gpio5_active) gpio5_fell = 1; xr_usb_serial->last_gpio_state = gpio_state; xr_usb_serial->last_gpio_int = gpio_int; xr_usb_serial->last_slv_in_active = slv_in_active; xr_usb_serial->last_gpio5_tx_active = gpio5_active; xr_usb_serial->last_gpio1_event_ns = ktime_get_ns(); xr_usb_serial->gpio1_event_count++; if (gpio5_fell) xr_usb_serial->tx_empty_event_count++; if (prev_slv != slv_in_active || (gpio_int & POG422_GPIO1_STATUS_BIT)) flags_tm |= POG422_TIMING_F_GPIO1; if (prev_gpio5 != gpio5_active || (gpio_int & POG422_GPIO5_BIT)) flags_tm |= POG422_TIMING_F_GPIO5; if (gpio5_fell) flags_tm |= POG422_TIMING_F_GPIO5_FELL; if (slv_in_active) flags_tm |= POG422_TIMING_F_SLV_IN; if (flags_tm & (POG422_TIMING_F_GPIO1 | POG422_TIMING_F_GPIO5)) pog422_timing_push_locked(xr_usb_serial, POG422_TIMING_EV_GPIO_INT, xr_usb_serial->last_gpio1_event_ns, gpio_state, gpio_int, flags_tm, 0, 0); #if defined(POG422_HW_DEBUG) && (POG422_HW_DEBUG) flags_hw = flags_tm; if (flags_hw & (POG422_HWDBG_F_GPIO1 | POG422_HWDBG_F_GPIO5)) { slot = xr_usb_serial->hwdbg_head; xr_usb_serial->hwdbg_ts[slot] = xr_usb_serial->last_gpio1_event_ns; xr_usb_serial->hwdbg_gpio_state[slot] = gpio_state; xr_usb_serial->hwdbg_gpio_int[slot] = gpio_int; xr_usb_serial->hwdbg_flags[slot] = flags_hw; xr_usb_serial->hwdbg_head = (slot + 1u) % POG422_HWDBG_EDGE_MAX; if (xr_usb_serial->hwdbg_count < POG422_HWDBG_EDGE_MAX) xr_usb_serial->hwdbg_count++; else xr_usb_serial->hwdbg_dropped++; } #endif spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); if (slv_in_active) xr_usb_serial->ctrlin |= XR_USB_SERIAL_CTRL_DCD; else xr_usb_serial->ctrlin &= ~XR_USB_SERIAL_CTRL_DCD; wake_up_interruptible(&xr_usb_serial->gpio1_wait); if (gpio5_fell) wake_up_interruptible(&xr_usb_serial->tx_empty_wait); } static int xr_usb_serial_copy_gpio1_event(struct xr_usb_serial *xr_usb_serial, struct xr_pog422_gpio1_event __user *uarg) { struct xr_pog422_gpio1_event ev; if (!uarg) return -EINVAL; xr_usb_serial_pog422_fill_event(xr_usb_serial, &ev); if (copy_to_user(uarg, &ev, sizeof(ev))) return -EFAULT; return 0; } static int xr_usb_serial_wait_gpio1(struct xr_usb_serial *xr_usb_serial, struct xr_pog422_gpio1_event __user *uarg) { u32 start_count; int ret; if (!xr_usb_serial->customized_int_enabled) return -EOPNOTSUPP; start_count = READ_ONCE(xr_usb_serial->gpio1_event_count); ret = wait_event_interruptible(xr_usb_serial->gpio1_wait, READ_ONCE(xr_usb_serial->gpio1_event_count) != start_count || xr_usb_serial->disconnected); if (ret) return ret; if (xr_usb_serial->disconnected) return -ENODEV; return xr_usb_serial_copy_gpio1_event(xr_usb_serial, uarg); } /* control interface reports status changes with "interrupt" transfers */ static void xr_usb_serial_ctrl_irq(struct urb *urb) { struct xr_usb_serial *xr_usb_serial = urb->context; struct usb_cdc_notification *dr = urb->transfer_buffer; #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) #else struct tty_struct *tty; #endif unsigned char *data; int newctrl; int retval; int status = urb->status; int i; unsigned char *p; switch (status) { case 0: p = (unsigned char *)(urb->transfer_buffer); for(i=0;iactual_length;i++) { dev_dbg(&xr_usb_serial->control->dev,"0x%02x\n",p[i]); } /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ dev_dbg(&xr_usb_serial->control->dev, "%s - urb shutting down with status: %d\n", __func__, status); return; default: dev_dbg(&xr_usb_serial->control->dev, "%s - nonzero urb status received: %d\n", __func__, status); goto exit; } usb_mark_last_busy(xr_usb_serial->dev); if (xr_usb_serial->customized_int_enabled) { if (urb->actual_length >= 4) xr_usb_serial_pog422_handle_gpio_irq(xr_usb_serial, (unsigned char *)urb->transfer_buffer, urb->actual_length); goto exit; } data = (unsigned char *)(dr + 1); switch (dr->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: dev_dbg(&xr_usb_serial->control->dev, "%s - network connection: %d\n", __func__, dr->wValue); break; case USB_CDC_NOTIFY_SERIAL_STATE: #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) newctrl = get_unaligned_le16(data); if (!xr_usb_serial->clocal && (xr_usb_serial->ctrlin & ~newctrl & XR_USB_SERIAL_CTRL_DCD)) { dev_dbg(&xr_usb_serial->control->dev, "%s - calling hangup\n", __func__); tty_port_tty_hangup(&xr_usb_serial->port, false); } #else tty = tty_port_tty_get(&xr_usb_serial->port); newctrl = get_unaligned_le16(data); if (tty) { if (!xr_usb_serial->clocal && (xr_usb_serial->ctrlin & ~newctrl & XR_USB_SERIAL_CTRL_DCD)) { dev_dbg(&xr_usb_serial->control->dev, "%s - calling hangup\n", __func__); tty_hangup(tty); } tty_kref_put(tty); } #endif xr_usb_serial->ctrlin = newctrl; dev_dbg(&xr_usb_serial->control->dev, "%s - input control lines: dcd%c dsr%c break%c " "ring%c framing%c parity%c overrun%c\n", __func__, xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_DCD ? '+' : '-', xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_DSR ? '+' : '-', xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_BRK ? '+' : '-', xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_RI ? '+' : '-', xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_FRAMING ? '+' : '-', xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_PARITY ? '+' : '-', xr_usb_serial->ctrlin & XR_USB_SERIAL_CTRL_OVERRUN ? '+' : '-'); break; default: dev_dbg(&xr_usb_serial->control->dev, "%s - unknown notification %d received: index %d " "len %d data0 %d data1 %d\n", __func__, dr->bNotificationType, dr->wIndex, dr->wLength, data[0], data[1]); break; } exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&xr_usb_serial->control->dev, "%s - usb_submit_urb failed: %d\n", __func__, retval); } static int xr_usb_serial_submit_read_urb(struct xr_usb_serial *xr_usb_serial, int index, gfp_t mem_flags) { int res; if (!test_and_clear_bit(index, &xr_usb_serial->read_urbs_free)) return 0; dev_vdbg(&xr_usb_serial->data->dev, "%s - urb %d\n", __func__, index); res = usb_submit_urb(xr_usb_serial->read_urbs[index], mem_flags); if (res) { if (res != -EPERM) { dev_err(&xr_usb_serial->data->dev, "%s - usb_submit_urb failed: %d\n", __func__, res); } set_bit(index, &xr_usb_serial->read_urbs_free); return res; } return 0; } static int xr_usb_serial_submit_read_urbs(struct xr_usb_serial *xr_usb_serial, gfp_t mem_flags) { int res; int i; for (i = 0; i < xr_usb_serial->rx_buflimit; ++i) { res = xr_usb_serial_submit_read_urb(xr_usb_serial, i, mem_flags); if (res) return res; } return 0; } static unsigned int pog422_dttty_avail_locked(struct xr_usb_serial *xr_usb_serial) { return (xr_usb_serial->dttty_head - xr_usb_serial->dttty_tail) & (POG422_DTTTY_RX_SIZE - 1); } static void pog422_dttty_push_locked(struct xr_usb_serial *xr_usb_serial, u8 ch) { unsigned int next = (xr_usb_serial->dttty_head + 1) & (POG422_DTTTY_RX_SIZE - 1); if (next == xr_usb_serial->dttty_tail) { xr_usb_serial->dttty_overruns++; return; } xr_usb_serial->dttty_buf[xr_usb_serial->dttty_head] = ch; xr_usb_serial->dttty_head = next; xr_usb_serial->dttty_total_written++; } static void pog422_dttty_flush_locked(struct xr_usb_serial *xr_usb_serial) { xr_usb_serial->dttty_tail = xr_usb_serial->dttty_head; } static unsigned int pog422_wide_rx_avail_bytes_locked(struct xr_usb_serial *xr_usb_serial) { unsigned int pairs = (xr_usb_serial->wide_rx_head - xr_usb_serial->wide_rx_tail) & (POG422_WIDE_RX_SIZE - 1); return pairs * 2u; } static void pog422_wide_rx_push_locked(struct xr_usb_serial *xr_usb_serial, u8 ch, u8 flags) { unsigned int next = (xr_usb_serial->wide_rx_head + 1) & (POG422_WIDE_RX_SIZE - 1); unsigned int idx; if (next == xr_usb_serial->wide_rx_tail) { xr_usb_serial->wide_rx_overruns++; return; } idx = xr_usb_serial->wide_rx_head; xr_usb_serial->wide_rx_buf[idx * 2u] = ch; xr_usb_serial->wide_rx_buf[idx * 2u + 1u] = flags; xr_usb_serial->wide_rx_head = next; xr_usb_serial->wide_rx_total_written++; } static void pog422_wide_rx_flush_locked(struct xr_usb_serial *xr_usb_serial) { xr_usb_serial->wide_rx_tail = xr_usb_serial->wide_rx_head; } static void xr_pog422_deliver_rx_byte(struct xr_usb_serial *xr_usb_serial, u8 ch, char tty_flag) { #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) #else struct tty_struct *tty; #endif unsigned long flags; if (xr_usb_serial->dttty_rx) { spin_lock_irqsave(&xr_usb_serial->read_lock, flags); pog422_dttty_push_locked(xr_usb_serial, ch); spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); atomic64_inc(&xr_usb_serial->rx_chars_inserted); return; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) tty_insert_flip_char(&xr_usb_serial->port, ch, tty_flag); tty_flip_buffer_push(&xr_usb_serial->port); #else tty = tty_port_tty_get(&xr_usb_serial->port); if (!tty) return; tty_insert_flip_char(&xr_usb_serial->port, ch, tty_flag); tty_flip_buffer_push(tty); tty_kref_put(tty); #endif atomic64_inc(&xr_usb_serial->rx_chars_inserted); } static void xr_pog422_deliver_rx_bulk(struct xr_usb_serial *xr_usb_serial, const u8 *buf, int len) { #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) #else struct tty_struct *tty; #endif unsigned long flags; int i; if (len <= 0) return; if (xr_usb_serial->dttty_rx) { spin_lock_irqsave(&xr_usb_serial->read_lock, flags); for (i = 0; i < len; i++) pog422_dttty_push_locked(xr_usb_serial, buf[i]); spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); atomic64_add(len, &xr_usb_serial->rx_chars_inserted); return; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) tty_insert_flip_string(&xr_usb_serial->port, buf, len); tty_flip_buffer_push(&xr_usb_serial->port); #else tty = tty_port_tty_get(&xr_usb_serial->port); if (!tty) return; tty_insert_flip_string(tty, buf, len); tty_flip_buffer_push(tty); tty_kref_put(tty); #endif atomic64_add(len, &xr_usb_serial->rx_chars_inserted); } static void xr_usb_serial_process_read_urb(struct xr_usb_serial *xr_usb_serial, struct urb *urb) { #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) #else struct tty_struct *tty; #endif int preciseflags = xr_usb_serial->preciseflags; int have_extra_byte; int length; int carried_extra; if (!urb->actual_length) return; atomic64_inc(&xr_usb_serial->rx_urbs_completed); if (preciseflags) { char *dp = urb->transfer_buffer; int i, ch, ch_flags; length = urb->actual_length; carried_extra = xr_usb_serial->have_extra_byte; length = length + (carried_extra ? 1 : 0); have_extra_byte = (preciseflags && (length & 1)); length = (preciseflags) ? (length / 2) : length; for (i = 0; i < length; ++i) { char tty_flag; if (i == 0) { if (carried_extra) { ch = xr_usb_serial->extra_byte; } else { ch = *dp++; } } else { ch = *dp++; } ch_flags = *dp++; if (ch_flags & RAMCTL_BUFFER_PARITY) tty_flag = TTY_PARITY; else if (ch_flags & RAMCTL_BUFFER_BREAK) tty_flag = TTY_BREAK; else if (ch_flags & RAMCTL_BUFFER_FRAME) tty_flag = TTY_FRAME; else if (ch_flags & RAMCTL_BUFFER_OVERRUN) tty_flag = TTY_OVERRUN; else tty_flag = TTY_NORMAL; if (xr_usb_serial->wide_rx) { u8 store_flags = (u8)ch_flags; unsigned long wflags; if (READ_ONCE(xr_usb_serial->last_slv_in_active)) store_flags |= (u8)POG422_RAMCTL_HS; spin_lock_irqsave(&xr_usb_serial->read_lock, wflags); pog422_wide_rx_push_locked(xr_usb_serial, (u8)ch, store_flags); spin_unlock_irqrestore(&xr_usb_serial->read_lock, wflags); } xr_pog422_deliver_rx_byte(xr_usb_serial, ch, tty_flag); } if (have_extra_byte) { xr_usb_serial->extra_byte = *dp; xr_usb_serial->have_extra_byte = 1; } else { xr_usb_serial->have_extra_byte = 0; } } else { xr_pog422_deliver_rx_bulk(xr_usb_serial, urb->transfer_buffer, urb->actual_length); } if (READ_ONCE(xr_usb_serial->timing_enabled)) { unsigned long tflags; u64 ts = ktime_get_ns(); u64 chars = (u64)atomic64_read(&xr_usb_serial->rx_chars_inserted); spin_lock_irqsave(&xr_usb_serial->gpio1_lock, tflags); pog422_timing_push_locked(xr_usb_serial, POG422_TIMING_EV_RX_URB, ts, 0, 0, 0, (u16)urb->actual_length, chars); spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, tflags); } } static void xr_usb_serial_read_bulk_callback(struct urb *urb) { struct xr_usb_serial_rb *rb = urb->context; struct xr_usb_serial *xr_usb_serial = rb->instance; unsigned long flags; dev_vdbg(&xr_usb_serial->data->dev, "%s - urb %d, len %d\n", __func__, rb->index, urb->actual_length); set_bit(rb->index, &xr_usb_serial->read_urbs_free); if (!xr_usb_serial->dev) { dev_dbg(&xr_usb_serial->data->dev, "%s - disconnected\n", __func__); return; } usb_mark_last_busy(xr_usb_serial->dev); if (urb->status) { dev_dbg(&xr_usb_serial->data->dev, "%s - non-zero urb status: %d\n", __func__, urb->status); //return; } xr_usb_serial_process_read_urb(xr_usb_serial, urb); /* throttle device if requested by tty */ spin_lock_irqsave(&xr_usb_serial->read_lock, flags); xr_usb_serial->throttled = xr_usb_serial->throttle_req; if (!xr_usb_serial->throttled && !xr_usb_serial->susp_count) { spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); xr_usb_serial_submit_read_urb(xr_usb_serial, rb->index, GFP_ATOMIC); } else { spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); } } /* data interface wrote those outgoing bytes */ static void xr_usb_serial_write_bulk(struct urb *urb) { struct xr_usb_serial_wb *wb = urb->context; struct xr_usb_serial *xr_usb_serial = wb->instance; unsigned long flags; if (urb->status || (urb->actual_length != urb->transfer_buffer_length)) dev_vdbg(&xr_usb_serial->data->dev, "%s - len %d/%d, status %d\n", __func__, urb->actual_length, urb->transfer_buffer_length, urb->status); spin_lock_irqsave(&xr_usb_serial->write_lock, flags); xr_usb_serial_write_done(xr_usb_serial, wb); spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); schedule_work(&xr_usb_serial->work); } static void xr_usb_serial_softint(struct work_struct *work) { struct xr_usb_serial *xr_usb_serial = container_of(work, struct xr_usb_serial, work); #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) #else struct tty_struct *tty; #endif dev_vdbg(&xr_usb_serial->data->dev, "%s\n", __func__); #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) tty_port_tty_wakeup(&xr_usb_serial->port); #else tty = tty_port_tty_get(&xr_usb_serial->port); if (!tty) return; tty_wakeup(tty); tty_kref_put(tty); #endif } /* * TTY handlers */ static int xr_usb_serial_tty_install(struct tty_driver *driver, struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial; int retval; dev_dbg(tty->dev, "%s\n", __func__); xr_usb_serial = xr_usb_serial_get_by_index(tty->index); if (!xr_usb_serial) return -ENODEV; retval = tty_standard_install(driver, tty); if (retval) goto error_init_termios; tty->driver_data = xr_usb_serial; return 0; error_init_termios: tty_port_put(&xr_usb_serial->port); return retval; } static int xr_usb_serial_tty_open(struct tty_struct *tty, struct file *filp) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; int result; result = xr_usb_serial_fifo_reset(xr_usb_serial); dev_dbg(tty->dev, "%s\n", __func__); return tty_port_open(&xr_usb_serial->port, tty, filp); } static int xr_usb_serial_port_activate(struct tty_port *port, struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = container_of(port, struct xr_usb_serial, port); int retval = -ENODEV; dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); pr_info("Activated function...Mitesh\n"); mutex_lock(&xr_usb_serial->mutex); if (xr_usb_serial->disconnected) goto disconnected; retval = usb_autopm_get_interface(xr_usb_serial->control); if (retval) goto error_get_interface; /* * FIXME: Why do we need this? Allocating 64K of physically contiguous * memory is really nasty... */ set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); xr_usb_serial->control->needs_remote_wakeup = 1; xr_usb_serial->ctrlurb->dev = xr_usb_serial->dev; if (usb_submit_urb(xr_usb_serial->ctrlurb, GFP_KERNEL)) { dev_err(&xr_usb_serial->control->dev, "%s - usb_submit_urb(ctrl irq) failed\n", __func__); goto error_submit_urb; } xr_usb_serial->ctrlout = XR_USB_SERIAL_CTRL_DTR | XR_USB_SERIAL_CTRL_RTS; if (xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout) < 0 && (xr_usb_serial->ctrl_caps & USB_CDC_CAP_LINE)) goto error_set_control; usb_autopm_put_interface(xr_usb_serial->control); /* if (xr_usb_serial->DeviceProduct == 0x1422) { xr_usb_serial->rs485_422_en = 1; dev_info(&xr_usb_serial->control->dev, "XR21B1422 RS485 forced ON\n"); pr_info("RS485 is on...Mitesh\n"); } */ /* * Unthrottle device in case the TTY was closed while throttled. */ spin_lock_irq(&xr_usb_serial->read_lock); xr_usb_serial->throttled = 0; xr_usb_serial->throttle_req = 0; spin_unlock_irq(&xr_usb_serial->read_lock); if (xr_usb_serial_submit_read_urbs(xr_usb_serial, GFP_KERNEL)) goto error_submit_read_urbs; mutex_unlock(&xr_usb_serial->mutex); return 0; error_submit_read_urbs: xr_usb_serial->ctrlout = 0; xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout); error_set_control: usb_kill_urb(xr_usb_serial->ctrlurb); error_submit_urb: usb_autopm_put_interface(xr_usb_serial->control); error_get_interface: disconnected: mutex_unlock(&xr_usb_serial->mutex); return retval; } static void xr_usb_serial_port_destruct(struct tty_port *port) { struct xr_usb_serial *xr_usb_serial = container_of(port, struct xr_usb_serial, port); dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) tty_unregister_device(xr_usb_serial_tty_driver, xr_usb_serial->minor); #endif xr_usb_serial_release_minor(xr_usb_serial); usb_put_intf(xr_usb_serial->control); kfree(xr_usb_serial->country_codes); kfree(xr_usb_serial); } static void xr_usb_serial_port_shutdown(struct tty_port *port) { struct xr_usb_serial *xr_usb_serial = container_of(port, struct xr_usb_serial, port); int i; dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); mutex_lock(&xr_usb_serial->mutex); if (!xr_usb_serial->disconnected) { usb_autopm_get_interface(xr_usb_serial->control); xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout = 0); usb_kill_urb(xr_usb_serial->ctrlurb); for (i = 0; i < XR_USB_SERIAL_NW; i++) usb_kill_urb(xr_usb_serial->wb[i].urb); for (i = 0; i < xr_usb_serial->rx_buflimit; i++) usb_kill_urb(xr_usb_serial->read_urbs[i]); xr_usb_serial->control->needs_remote_wakeup = 0; usb_autopm_put_interface(xr_usb_serial->control); } mutex_unlock(&xr_usb_serial->mutex); } static void xr_usb_serial_tty_cleanup(struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); tty_port_put(&xr_usb_serial->port); } static void xr_usb_serial_tty_hangup(struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); tty_port_hangup(&xr_usb_serial->port); } static void xr_usb_serial_tty_close(struct tty_struct *tty, struct file *filp) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); tty_port_close(&xr_usb_serial->port, tty, filp); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) static ssize_t xr_usb_serial_tty_write(struct tty_struct *tty, const unsigned char *buf, size_t count) #else static int xr_usb_serial_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) #endif { struct xr_usb_serial *xr_usb_serial = tty->driver_data; int stat; unsigned long flags; int wbn; struct xr_usb_serial_wb *wb; if (!count) return 0; //dev_vdbg(&xr_usb_serial->data->dev, "%s - count %d\n", __func__, count); spin_lock_irqsave(&xr_usb_serial->write_lock, flags); wbn = xr_usb_serial_wb_alloc(xr_usb_serial); if (wbn < 0) { spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); return 0; } wb = &xr_usb_serial->wb[wbn]; if (!xr_usb_serial->dev) { wb->use = 0; spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); return -ENODEV; } count = (count > xr_usb_serial->writesize) ? xr_usb_serial->writesize : count; //dev_vdbg(&xr_usb_serial->data->dev, "%s - write %d\n", __func__, count); memcpy(wb->buf, buf, count); wb->len = count; usb_autopm_get_interface_async(xr_usb_serial->control); if (xr_usb_serial->susp_count) { if (!xr_usb_serial->delayed_wb) xr_usb_serial->delayed_wb = wb; else usb_autopm_put_interface_async(xr_usb_serial->control); spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); return count; /* A white lie */ } usb_mark_last_busy(xr_usb_serial->dev); stat = xr_usb_serial_start_wb(xr_usb_serial, wb); spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); if (stat < 0) return stat; return count; } static unsigned int xr_usb_serial_tty_write_room(struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; /* * Do not let the line discipline to know that we have a reserve, * or it might get too enthusiastic. */ return xr_usb_serial_wb_is_avail(xr_usb_serial) ? xr_usb_serial->writesize : 0; } static unsigned int xr_usb_serial_tty_chars_in_buffer(struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; /* * if the device was unplugged then any remaining characters fell out * of the connector ;) */ if (xr_usb_serial->disconnected) return 0; /* * This is inaccurate (overcounts), but it works. */ return (XR_USB_SERIAL_NW - xr_usb_serial_wb_is_avail(xr_usb_serial)) * xr_usb_serial->writesize; } static void xr_usb_serial_tty_throttle(struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; spin_lock_irq(&xr_usb_serial->read_lock); xr_usb_serial->throttle_req = 1; spin_unlock_irq(&xr_usb_serial->read_lock); } static void xr_usb_serial_tty_unthrottle(struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; unsigned int was_throttled; spin_lock_irq(&xr_usb_serial->read_lock); was_throttled = xr_usb_serial->throttled; xr_usb_serial->throttled = 0; xr_usb_serial->throttle_req = 0; spin_unlock_irq(&xr_usb_serial->read_lock); if (was_throttled) xr_usb_serial_submit_read_urbs(xr_usb_serial, GFP_KERNEL); } static int xr_usb_serial_tty_break_ctl(struct tty_struct *tty, int state) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; int retval; retval = xr_usb_serial_send_break(xr_usb_serial, state ? 0xffff : 0); if (retval < 0) dev_err(&xr_usb_serial->control->dev, "%s - send break failed\n", __func__); return retval; } static int xr_usb_serial_tty_tiocmget(struct tty_struct *tty) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; //dev_dbg(&xr_usb_serial->control->dev, "xr_usb_serial_tty_tiocmget\n"); return xr_usb_serial_tiocmget(xr_usb_serial); } static int xr_usb_serial_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; //dev_dbg(&xr_usb_serial->control->dev, "xr_usb_serial_tty_tiocmset set=0x%x clear=0x%x\n",set,clear); return xr_usb_serial_tiocmset(xr_usb_serial,set,clear); } static int get_serial_info(struct xr_usb_serial *xr_usb_serial, struct serial_struct __user *info) { struct serial_struct tmp; if (!info) return -EINVAL; memset(&tmp, 0, sizeof(tmp)); tmp.flags = ASYNC_LOW_LATENCY; tmp.xmit_fifo_size = xr_usb_serial->writesize; tmp.baud_base = le32_to_cpu(xr_usb_serial->line.dwDTERate); tmp.close_delay = xr_usb_serial->port.close_delay / 10; tmp.closing_wait = xr_usb_serial->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : xr_usb_serial->port.closing_wait / 10; if (copy_to_user(info, &tmp, sizeof(tmp))) return -EFAULT; else return 0; } static int set_serial_info(struct xr_usb_serial *xr_usb_serial, struct serial_struct __user *newinfo) { struct serial_struct new_serial; unsigned int closing_wait, close_delay; int retval = 0; if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) return -EFAULT; close_delay = new_serial.close_delay * 10; closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; mutex_lock(&xr_usb_serial->port.mutex); if (!capable(CAP_SYS_ADMIN)) { if ((close_delay != xr_usb_serial->port.close_delay) || (closing_wait != xr_usb_serial->port.closing_wait)) retval = -EPERM; else retval = -EOPNOTSUPP; } else { xr_usb_serial->port.close_delay = close_delay; xr_usb_serial->port.closing_wait = closing_wait; } mutex_unlock(&xr_usb_serial->port.mutex); return retval; } static int xr_usb_serial_is_b142x(const struct xr_usb_serial *xr_usb_serial) { return (xr_usb_serial->DeviceProduct == 0x1420) || (xr_usb_serial->DeviceProduct == 0x1422) || (xr_usb_serial->DeviceProduct == 0x1424); } /* XR21B142x UART TX_FIFO_COUNT — diagnostic only (1K proved unreliable as a drain gate). */ #define XR21_TX_FIFO_COUNT_REG 0x041 /* * Map stored bEndpointAddress (probe #else path) to UART block index used by * userspace GET_REG (MST=0, SLV=2). get_reg()'s `(channel-4)*2` yields 4 for * ttyXRUSB1 and fails TX_FIFO reads (1T still saw 0xFFFFFFFF). */ static int pog422_b142x_uart_block(struct xr_usb_serial *xr_usb_serial) { int ep = xr_usb_serial->channel; if (ep >= 4) return ep - 4; /* 4→0, 5→1, 6→2, 7→3 */ return ep; } /* 1W: encode get_reg failure into the out-param so Dropbox shows why (not opaque FFFFFFFF). */ static u32 pog422_tx_fifo_fail(int ch, int rv) { u32 err = (rv < 0) ? (u32)(-rv) : 0xffffu; return 0xE0000000u | (((u32)ch & 0xffu) << 16) | (err & 0xffffu); } static int pog422_read_tx_fifo_reg(struct xr_usb_serial *xr_usb_serial, int ch, u32 *count_out) { short *data; int rv; data = kmalloc(2, GFP_KERNEL); if (!data) return -ENOMEM; rv = xr_usb_serial_get_reg_ext(xr_usb_serial, ch, XR21_TX_FIFO_COUNT_REG, data); if (rv < 0) { kfree(data); return rv; } *count_out = ((u16)le16_to_cpu(*data)) & 0x3ffu; kfree(data); return 0; } static u32 pog422_read_tx_fifo_count(struct xr_usb_serial *xr_usb_serial) { int ch = pog422_b142x_uart_block(xr_usb_serial); u32 count = 0; int rv; rv = pog422_read_tx_fifo_reg(xr_usb_serial, ch, &count); if (rv == 0) return count; /* * 1V still failed with matched module — try userspace's hard-coded SLV block. * Encode which channel/errno failed into the sample for Dropbox eval. */ if (ch != 2) { int rv2 = pog422_read_tx_fifo_reg(xr_usb_serial, 2, &count); if (rv2 == 0) return count; dev_err_ratelimited(&xr_usb_serial->control->dev, "POG422 TX_FIFO get_reg fail ep_ch=%d rv=%d; ch2 rv=%d (stored_ep=%u)\n", ch, rv, rv2, xr_usb_serial->channel); return pog422_tx_fifo_fail(2, rv2); } dev_err_ratelimited(&xr_usb_serial->control->dev, "POG422 TX_FIFO get_reg fail ch=%d rv=%d (stored_ep=%u)\n", ch, rv, xr_usb_serial->channel); return pog422_tx_fifo_fail(ch, rv); } /* * EG 2.2.2.10: MANUAL_DE hard-cap. usb_bulk_msg wall-clock alone cannot drop DE * while a later SET_GPIO7/GET_REG control transfer is wedged on the 5s default * timeout. Arm a delayed work at DE assert; cancel on any intentional release. */ static void pog422_de_cap_work_fn(struct work_struct *work) { struct xr_usb_serial *xr_usb_serial = container_of(to_delayed_work(work), struct xr_usb_serial, de_cap_work); if (!atomic_xchg(&xr_usb_serial->de_manual_held, 0)) return; if (xr_usb_serial->disconnected) return; (void)pog422_set_gpio7_sl_out(xr_usb_serial, 0); dev_warn(&xr_usb_serial->control->dev, "POG422: MANUAL_DE hard-cap dropped GPIO7 (orphan HS guard)\n"); } static void pog422_de_cap_arm(struct xr_usb_serial *xr_usb_serial, unsigned int cap_ms) { if (!cap_ms) cap_ms = 20u; atomic_set(&xr_usb_serial->de_manual_held, 1); mod_delayed_work(system_wq, &xr_usb_serial->de_cap_work, msecs_to_jiffies(cap_ms)); } static void pog422_de_cap_disarm(struct xr_usb_serial *xr_usb_serial) { atomic_set(&xr_usb_serial->de_manual_held, 0); cancel_delayed_work(&xr_usb_serial->de_cap_work); } /* * EG 2.2.1.7: WAIT_TX_EMPTY blocks on USB interrupt IN (Table-8). * GPIO5=RS485_EN_ACT sense falls when the shifter finishes; then confirm * TX_FIFO_COUNT==0. Manual GPIO7 DE is separate — not driven by RS485_EN_ACT. * B1422 has no Tx-empty USB IRQ and no LSR — reason codes only. */ static int xr_usb_serial_pog422_wait_tx_empty(struct xr_usb_serial *xr_usb_serial, struct xr_pog422_tx_empty __user *uarg) { struct xr_pog422_tx_empty req; ktime_t t0; unsigned long timeout_j; unsigned int timeout_ms; u32 fifo; u32 start_count; int rv = 0; u8 gpio5_active; unsigned long flags; if (copy_from_user(&req, uarg, sizeof(req))) return -EFAULT; timeout_ms = req.timeout_ms ? req.timeout_ms : 500u; req.tx_fifo = 0; req.wait_ns = 0; req.reason = POG422_TX_EMPTY_REASON_NONE; req.temt = 0; req.thre = 0; req._pad = 0; if (!xr_usb_serial_is_b142x(xr_usb_serial)) return -EOPNOTSUPP; if (xr_usb_serial->disconnected) return -ENODEV; if (!xr_usb_serial->customized_int_enabled) return -EOPNOTSUPP; t0 = ktime_get(); timeout_j = msecs_to_jiffies(timeout_ms); if (timeout_j == 0) timeout_j = 1; /* Already idle (FIFO empty + GPIO5/EN_ACT low). */ fifo = pog422_read_tx_fifo_count(xr_usb_serial); spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); gpio5_active = xr_usb_serial->last_gpio5_tx_active; spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); if (!(fifo & 0xE0000000u) && (fifo & 0x3ffu) == 0u && !gpio5_active) { req.tx_fifo = fifo; req.thre = 1; req.temt = 1; req.reason = POG422_TX_EMPTY_REASON_ALREADY_IDLE; req.wait_ns = (u32)ktime_to_ns(ktime_sub(ktime_get(), t0)); if (copy_to_user(uarg, &req, sizeof(req))) return -EFAULT; return 0; } start_count = READ_ONCE(xr_usb_serial->tx_empty_event_count); rv = wait_event_interruptible_timeout(xr_usb_serial->tx_empty_wait, READ_ONCE(xr_usb_serial->tx_empty_event_count) != start_count || xr_usb_serial->disconnected, timeout_j); if (xr_usb_serial->disconnected) { rv = -ENODEV; goto out_copy; } if (rv < 0) goto out_copy; /* -ERESTARTSYS */ if (rv == 0) { /* Timeout — sample once more for forensics. */ fifo = pog422_read_tx_fifo_count(xr_usb_serial); req.tx_fifo = fifo; spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); gpio5_active = xr_usb_serial->last_gpio5_tx_active; spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); if (!(fifo & 0xE0000000u) && (fifo & 0x3ffu) == 0u) { req.thre = 1; if (!gpio5_active) { req.temt = 1; req.reason = POG422_TX_EMPTY_REASON_TIMEOUT_OK; rv = 0; goto out_copy; } } rv = -ETIMEDOUT; goto out_copy; } /* USB interrupt IN saw GPIO5/EN_ACT fall — confirm FIFO empty. */ fifo = pog422_read_tx_fifo_count(xr_usb_serial); req.tx_fifo = fifo; if (!(fifo & 0xE0000000u) && (fifo & 0x3ffu) == 0u) { req.thre = 1; req.temt = 1; req.reason = POG422_TX_EMPTY_REASON_EN_ACT_FALL; rv = 0; } else { req.thre = 0; req.temt = 0; req.reason = POG422_TX_EMPTY_REASON_SPURIOUS; rv = -EAGAIN; } out_copy: req.wait_ns = (u32)ktime_to_ns(ktime_sub(ktime_get(), t0)); if (copy_to_user(uarg, &req, sizeof(req))) return -EFAULT; return rv; } static int pog422_inject_skip_fifo(u32 post_margin_us) { return post_margin_us == POG422_INJECT_POST_MANUAL_DE || post_margin_us == POG422_INJECT_POST_EXTERNAL_DE; } static unsigned int pog422_inject_drain_margin_us(u32 post_margin_us) { if (post_margin_us == POG422_INJECT_POST_EXTERNAL_DE || post_margin_us == 0) return 400u; return post_margin_us; } static int xr_usb_serial_pog422_inject_tx(struct xr_usb_serial *xr_usb_serial, struct xr_pog422_inject_tx __user *uarg) { struct xr_pog422_inject_tx req; u8 *frame = NULL; u8 *wide = NULL; u32 frame_len, wide_len, i; unsigned int pipe; unsigned long flags; int rv, actual; ktime_t t0; ktime_t t_de0 = 0; int got_pm = 0; int manual_de_on = 0; unsigned int wall_ms; if (copy_from_user(&req, uarg, sizeof(req))) return -EFAULT; if (req.frame_len == 0 || req.frame_len > POG422_INJECT_FRAME_MAX) return -EINVAL; if (!req.user_frame) return -EINVAL; /* Clear out-params; ignore any garbage from userspace. */ req.tx_ns = 0; req.drain_wait_ns = 0; req.tx_fifo_before = 0; req.tx_fifo_after_bulk = 0; req.tx_fifo_after_drain = 0; req.usb_actual = 0; if (!xr_usb_serial_is_b142x(xr_usb_serial)) return -EOPNOTSUPP; if (xr_usb_serial->disconnected) return -ENODEV; frame_len = req.frame_len; wide_len = frame_len * 2; frame = kmalloc(frame_len, GFP_KERNEL); wide = kmalloc(wide_len, GFP_KERNEL); if (!frame || !wide) { rv = -ENOMEM; goto out_free; } if (copy_from_user(frame, (void __user *)(uintptr_t)req.user_frame, frame_len)) { rv = -EFAULT; goto out_free; } for (i = 0; i < frame_len; i++) { wide[i * 2] = frame[i]; wide[i * 2 + 1] = (i == 0) ? (u8)RAMCTL_BUFFER_PARITY : 0; } mutex_lock(&xr_usb_serial->mutex); if (xr_usb_serial->disconnected) { rv = -ENODEV; goto out_unlock; } spin_lock_irqsave(&xr_usb_serial->write_lock, flags); if (xr_usb_serial->write_used || xr_usb_serial->transmitting) { spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); rv = -EBUSY; goto out_unlock; } spin_unlock_irqrestore(&xr_usb_serial->write_lock, flags); if (!xr_usb_serial->preciseflags && !xr_usb_serial->trans9) { rv = -EINVAL; goto out_unlock; } rv = usb_autopm_get_interface(xr_usb_serial->control); if (rv < 0) goto out_unlock; got_pm = 1; pipe = xr_usb_serial->wb[0].urb->pipe; /* * EG 2.2.1.13: MANUAL_DE — assert GPIO7 only after mutex/pm, immediately * before bulk. Userspace must not assert DE before this ioctl (mutex wait * and FIFO get_reg would hold the bus idle with DE high). */ if (req.post_margin_us == POG422_INJECT_POST_MANUAL_DE) { unsigned int de_cap_ms; rv = pog422_set_gpio7_sl_out(xr_usb_serial, 1); if (rv < 0) goto out_pm; manual_de_on = 1; t_de0 = ktime_get(); /* Skip pre-bulk FIFO get_reg — saves a USB control on the DE-high path. */ req.tx_fifo_before = 0; /* Wire air + 20 ms; never wait for Python's 5s inject budget. */ de_cap_ms = (unsigned int)((frame_len * 11u * 1000u) / 57600u) + 20u; if (de_cap_ms < 15u) de_cap_ms = 15u; if (de_cap_ms > 300u) de_cap_ms = 300u; pog422_de_cap_arm(xr_usb_serial, de_cap_ms); } else if (req.post_margin_us == POG422_INJECT_POST_EXTERNAL_DE) { /* BCM11 already high. Skip FIFO get_reg — same USB-control tax as MANUAL_DE. */ req.tx_fifo_before = 0; } else { req.tx_fifo_before = pog422_read_tx_fifo_count(xr_usb_serial); } t0 = ktime_get(); actual = 0; /* * EG 2.2.2.5: chunk wide OUT to writesize. A single usb_*_msg of MTC-sized * frames (1170×2=2340) timed out (~ETIMEDOUT ~160 ms) on live Pi; SCR-sized * bursts fit one URB. Keep MANUAL_DE asserted across chunks. * EG 3.0.0.9 / driver 2.2.2.8: wall-clock from DE-assert — if bulk stalls * past timeout_ms+100ms, drop DE and return -ETIMEDOUT (pos5 02:51 orphan HS). */ wall_ms = req.timeout_ms ? req.timeout_ms : 500u; wall_ms += 100u; { u32 offset = 0; u32 chunk_max = xr_usb_serial->writesize; unsigned int chunk_timeout_ms = req.timeout_ms ? req.timeout_ms : 500u; /* Per-chunk: enough for this slice at 57600 9-bit + USB slack. */ unsigned int air_ms = (unsigned int)((frame_len * 11u * 1000u) / 57600u) + 300u; if (chunk_timeout_ms < air_ms) chunk_timeout_ms = air_ms; if (chunk_max < 64u) chunk_max = 64u; chunk_max &= ~1u; /* wide pairs */ rv = 0; while (offset < wide_len) { u32 chunk = wide_len - offset; int chunk_actual = 0; unsigned int this_timeout_ms = chunk_timeout_ms; if (manual_de_on) { s64 elapsed_ms = ktime_to_ms(ktime_sub(ktime_get(), t_de0)); if (elapsed_ms < 0) elapsed_ms = 0; if ((unsigned int)elapsed_ms >= wall_ms) { rv = -ETIMEDOUT; break; } { unsigned int rem = wall_ms - (unsigned int)elapsed_ms; if (this_timeout_ms > rem) this_timeout_ms = rem ? rem : 1u; } } if (chunk > chunk_max) chunk = chunk_max; if (usb_pipeint(pipe)) { rv = usb_interrupt_msg(xr_usb_serial->dev, pipe, wide + offset, chunk, &chunk_actual, msecs_to_jiffies(this_timeout_ms)); } else { rv = usb_bulk_msg(xr_usb_serial->dev, pipe, wide + offset, chunk, &chunk_actual, msecs_to_jiffies(this_timeout_ms)); } if (chunk_actual > 0) actual += chunk_actual; if (rv != 0) break; if (chunk_actual != (int)chunk) { rv = -EIO; break; } offset += chunk; } } req.tx_ns = (u32)ktime_to_ns(ktime_sub(ktime_get(), t0)); req.drain_wait_ns = 0; req.usb_actual = (actual > 0) ? (u32)actual : 0; if (rv == 0 && actual != (int)wide_len) rv = -EIO; /* * EG 2.2: MANUAL_DE skips kernel air usleep — C owns DE release (air/TEMT). * EG 4.1.0.5 / driver 2.2.2.12: EXTERNAL_DE same — return after bulk. * Prior EXTERNAL_DE fell into wait_tx_wire_idle (blind air usleep) then C * waited air again → SCR ioctl wall ≈ air while tx_ns stayed ~100 µs. * Skip post-bulk FIFO get_reg (USB control would stretch DE-high). * On MANUAL_DE bulk failure or wall-clock exceed, drop GPIO7 here. */ if (req.post_margin_us == POG422_INJECT_POST_MANUAL_DE || req.post_margin_us == POG422_INJECT_POST_EXTERNAL_DE) { req.drain_wait_ns = 0; req.tx_fifo_after_bulk = 0; req.tx_fifo_after_drain = 0; if (req.post_margin_us == POG422_INJECT_POST_MANUAL_DE) { if (rv == 0 && manual_de_on) { s64 elapsed_ms = ktime_to_ms(ktime_sub(ktime_get(), t_de0)); if (elapsed_ms < 0) elapsed_ms = 0; if ((unsigned int)elapsed_ms >= wall_ms) rv = -ETIMEDOUT; } if (rv != 0 && manual_de_on) { pog422_de_cap_disarm(xr_usb_serial); (void)pog422_set_gpio7_sl_out(xr_usb_serial, 0); manual_de_on = 0; } else { /* Success: userspace owns DE release; hard-cap stays armed. */ manual_de_on = 0; } } /* EXTERNAL_DE: BCM DE already high; C owns air/TEMT + release. */ } else { if (pog422_inject_skip_fifo(req.post_margin_us)) req.tx_fifo_after_bulk = 0; else req.tx_fifo_after_bulk = pog422_read_tx_fifo_count(xr_usb_serial); if (rv == 0) { ktime_t t_drain0 = ktime_get(); unsigned int drain_timeout_ms = req.timeout_ms ? req.timeout_ms : 500u; unsigned int margin_us = pog422_inject_drain_margin_us(req.post_margin_us); int drain_rv; drain_rv = pog422_b142x_wait_tx_wire_idle(xr_usb_serial, frame_len, drain_timeout_ms, margin_us); req.drain_wait_ns = (u32)ktime_to_ns(ktime_sub(ktime_get(), t_drain0)); if (pog422_inject_skip_fifo(req.post_margin_us)) req.tx_fifo_after_drain = 0; else req.tx_fifo_after_drain = pog422_read_tx_fifo_count(xr_usb_serial); if (drain_rv < 0) rv = drain_rv; } else { req.tx_fifo_after_drain = req.tx_fifo_after_bulk; } } usb_autopm_put_interface(xr_usb_serial->control); got_pm = 0; out_unlock: if (got_pm) usb_autopm_put_interface(xr_usb_serial->control); if (manual_de_on) { pog422_de_cap_disarm(xr_usb_serial); (void)pog422_set_gpio7_sl_out(xr_usb_serial, 0); } mutex_unlock(&xr_usb_serial->mutex); /* Always return diag samples when we reached bulk (even on EIO). */ if (copy_to_user(uarg, &req, sizeof(req))) rv = rv ? rv : -EFAULT; out_free: kfree(frame); kfree(wide); return rv; out_pm: usb_autopm_put_interface(xr_usb_serial->control); got_pm = 0; goto out_unlock; } static int xr_usb_serial_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; int rv = -ENOIOCTLCMD; unsigned int channel, reg, val,preciseflags; int baud_rate = 0; struct usb_cdc_line_coding newline; short *data; switch (cmd) { case TIOCGSERIAL: /* gets serial port data */ rv = get_serial_info(xr_usb_serial, (struct serial_struct __user *) arg); break; case TIOCSSERIAL: rv = set_serial_info(xr_usb_serial, (struct serial_struct __user *) arg); break; case XR_USB_SERIAL_GET_REG: if (get_user(channel, (int __user *)arg)) return -EFAULT; if (get_user(reg, (int __user *)(arg + sizeof(int)))) return -EFAULT; data = kmalloc(2, GFP_KERNEL); if (data == NULL) { dev_err(&xr_usb_serial->control->dev, "%s - Cannot allocate USB buffer.\n", __func__); return -ENOMEM; } if (channel == -1) { rv = xr_usb_serial_get_reg(xr_usb_serial,reg, data); } else { rv = xr_usb_serial_get_reg_ext(xr_usb_serial,channel,reg, data); } if (rv < 0) { dev_err(&xr_usb_serial->control->dev, "Cannot get register (%d)\n", rv); kfree(data); return -EFAULT; } if (put_user(le16_to_cpu(*data), (int __user *)(arg + 2 * sizeof(int)))) { dev_err(&xr_usb_serial->control->dev, "Cannot put user result\n"); kfree(data); return -EFAULT; } rv = 0; kfree(data); break; case XR_USB_SERIAL_SET_REG: if (get_user(channel, (int __user *)arg)) return -EFAULT; if (get_user(reg, (int __user *)(arg + sizeof(int)))) return -EFAULT; if (get_user(val, (int __user *)(arg + 2 * sizeof(int)))) return -EFAULT; if (channel == -1) { rv = xr_usb_serial_set_reg(xr_usb_serial,reg, val); } else { rv = xr_usb_serial_set_reg_ext(xr_usb_serial,channel,reg, val); } if (rv < 0) return -EFAULT; rv = 0; break; case XR_USB_SERIAL_LOOPBACK: if (get_user(channel, (int __user *)arg)) return -EFAULT; if (channel == -1) channel = xr_usb_serial->channel; rv = xr_usb_serial_set_loopback(xr_usb_serial,channel); if (rv < 0) return -EFAULT; rv = 0; break; case XR_USB_SERIAL_SET_GPIO_MODE_REG: xr_usb_serial_disable(xr_usb_serial); if (get_user(channel, (int __user *)arg)) return -EFAULT; if (get_user(val, (int __user *)(arg + sizeof(int)))) return -EFAULT; if (channel == -1) { //block = portdata->block; rv = xr_usb_serial_set_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_mode_addr, val); } else { rv = xr_usb_serial_set_reg_ext(xr_usb_serial,channel,xr_usb_serial->reg_map.uart_gpio_mode_addr, val); } dev_dbg(&xr_usb_serial->control->dev, "XR_USB_SERIAL_SET_GPIO_MODE_REG 0x%x val:0x%x \n", xr_usb_serial->reg_map.uart_gpio_mode_addr,val); xr_usb_serial_enable(xr_usb_serial); if (rv < 0) return -EFAULT; break; case XR_USB_SERIAL_GET_GPIO_MODE_REG: xr_usb_serial_disable(xr_usb_serial); if (get_user(channel, (int __user *)arg)) return -EFAULT; data = kmalloc(2, GFP_KERNEL); if (data == NULL) { dev_err(&xr_usb_serial->control->dev, "%s - Cannot allocate USB buffer.\n", __func__); return -ENOMEM; } if (channel == -1) { rv = xr_usb_serial_get_reg(xr_usb_serial,xr_usb_serial->reg_map.uart_gpio_mode_addr, data); } else { rv = xr_usb_serial_get_reg_ext(xr_usb_serial,channel,xr_usb_serial->reg_map.uart_gpio_mode_addr,data); } xr_usb_serial_enable(xr_usb_serial); dev_dbg(&xr_usb_serial->control->dev, "XR_USB_SERIAL_GET_GPIO_MODE_REG 0x%x val:0x%x \n", xr_usb_serial->reg_map.uart_gpio_mode_addr,*data); if (rv < 0 ) { dev_err(&xr_usb_serial->control->dev, "Cannot get register (%d) channel=%d \n", rv,channel); kfree(data); return -EFAULT; } if (put_user(data[0], (int __user *)(arg + sizeof(int)))) { dev_err(&xr_usb_serial->control->dev, "Cannot put user result\n"); kfree(data); return -EFAULT; } kfree(data); break; case XRIOC_SET_ANY_BAUD_RATE: if (get_user(baud_rate, (int __user *)arg)) { dev_dbg(&xr_usb_serial->control->dev, "get_user errot \n"); return -EFAULT; } xr_usb_serial->line.dwDTERate = baud_rate; memcpy(&newline,&(xr_usb_serial->line),sizeof(struct usb_cdc_line_coding)); xr_usb_serial_disable(xr_usb_serial); rv = xr_usb_serial_set_line(xr_usb_serial,&newline); xr_usb_serial_enable(xr_usb_serial); dev_dbg(&xr_usb_serial->control->dev, "XRIOC_SET_ANY_BAUD_RATE set baud_rate:%d ret=%d\n", baud_rate,rv); break; case XRIOC_SET_PRECISE_FLAGS: preciseflags = arg; dev_dbg(&xr_usb_serial->control->dev, "%s VIOC_SET_PRECISE_FLAGS %d\n", __func__, preciseflags); xr_usb_serial_disable(xr_usb_serial); if (preciseflags) { xr_usb_serial->preciseflags = 1; xr_usb_serial->have_extra_byte = 0; } else { xr_usb_serial->preciseflags = 0; xr_usb_serial->have_extra_byte = 0; } xr_usb_serial_set_wide_mode(xr_usb_serial,xr_usb_serial->preciseflags); xr_usb_serial_enable(xr_usb_serial); rv = 0; break; case XR_USB_SERIAL_SET_GPIO7: { int val; if (get_user(val, (int __user *)arg)) return -EFAULT; if (!val) pog422_de_cap_disarm(xr_usb_serial); rv = pog422_set_gpio7_sl_out(xr_usb_serial, val ? 1 : 0); break; } case XR_USB_SERIAL_WAIT_GPIO1: rv = xr_usb_serial_wait_gpio1(xr_usb_serial, (struct xr_pog422_gpio1_event __user *)arg); break; case XR_USB_SERIAL_GET_GPIO1_EVENT: if (!xr_usb_serial->customized_int_enabled) return -EOPNOTSUPP; rv = xr_usb_serial_copy_gpio1_event(xr_usb_serial, (struct xr_pog422_gpio1_event __user *)arg); break; case XR_USB_SERIAL_GET_RX_STATS: { struct xr_pog422_rx_stats stats; stats.chars_inserted = atomic64_read(&xr_usb_serial->rx_chars_inserted); stats.urbs_completed = atomic64_read(&xr_usb_serial->rx_urbs_completed); stats.have_extra_byte = xr_usb_serial->have_extra_byte ? 1u : 0u; stats.preciseflags = xr_usb_serial->preciseflags ? 1u : 0u; if (copy_to_user((void __user *)arg, &stats, sizeof(stats))) return -EFAULT; rv = 0; break; } case XR_USB_SERIAL_RESET_RX_STATS: atomic64_set(&xr_usb_serial->rx_chars_inserted, 0); atomic64_set(&xr_usb_serial->rx_urbs_completed, 0); rv = 0; break; case XR_USB_SERIAL_SET_TIMING_TRACE: { int enable; unsigned long flags; if (get_user(enable, (int __user *)arg)) return -EFAULT; spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); xr_usb_serial->timing_enabled = enable ? 1 : 0; xr_usb_serial->timing_head = 0; xr_usb_serial->timing_count = 0; xr_usb_serial->timing_dropped = 0; if (enable) xr_usb_serial->timing_total_pushed = 0; spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); rv = 0; break; } case XR_USB_SERIAL_GET_TIMING_STATUS: { struct xr_pog422_timing_status st; unsigned long flags; memset(&st, 0, sizeof(st)); spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); st.enabled = xr_usb_serial->timing_enabled; st.count = xr_usb_serial->timing_count; st.dropped = xr_usb_serial->timing_dropped; st.total_pushed = xr_usb_serial->timing_total_pushed; spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); if (copy_to_user((void __user *)arg, &st, sizeof(st))) return -EFAULT; rv = 0; break; } case XR_USB_SERIAL_RESET_TIMING_TRACE: { unsigned long flags; spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); xr_usb_serial->timing_head = 0; xr_usb_serial->timing_count = 0; xr_usb_serial->timing_dropped = 0; xr_usb_serial->timing_total_pushed = 0; spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); rv = 0; break; } case XR_USB_SERIAL_DRAIN_TIMING_TRACE: { struct xr_pog422_timing_drain req; struct xr_pog422_timing_event *kbuf = NULL; unsigned long flags; u32 n, start, i, dropped; if (copy_from_user(&req, (void __user *)arg, sizeof(req))) return -EFAULT; if (req.max_out == 0 || !req.user_buf) return -EINVAL; if (req.max_out > POG422_TIMING_RING_MAX) req.max_out = POG422_TIMING_RING_MAX; kbuf = kmalloc_array(req.max_out, sizeof(*kbuf), GFP_KERNEL); if (!kbuf) return -ENOMEM; spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); n = xr_usb_serial->timing_count; if (n > req.max_out) n = req.max_out; start = (xr_usb_serial->timing_head + POG422_TIMING_RING_MAX - xr_usb_serial->timing_count) % POG422_TIMING_RING_MAX; for (i = 0; i < n; i++) { u32 idx = (start + i) % POG422_TIMING_RING_MAX; kbuf[i].timestamp_ns = xr_usb_serial->timing_ts[idx]; kbuf[i].type = xr_usb_serial->timing_type[idx]; kbuf[i].gpio_state = xr_usb_serial->timing_gpio_state[idx]; kbuf[i].gpio_int = xr_usb_serial->timing_gpio_int[idx]; kbuf[i].flags = xr_usb_serial->timing_flags[idx]; kbuf[i]._pad = 0; kbuf[i].nbytes = xr_usb_serial->timing_nbytes[idx]; kbuf[i].rx_chars = xr_usb_serial->timing_rx_chars[idx]; } dropped = xr_usb_serial->timing_dropped; if (n > 0) { xr_usb_serial->timing_count -= n; xr_usb_serial->timing_dropped = 0; } req.enabled = xr_usb_serial->timing_enabled; spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); if (n && copy_to_user((void __user *)(uintptr_t)req.user_buf, kbuf, n * sizeof(*kbuf))) { kfree(kbuf); return -EFAULT; } kfree(kbuf); req.out_n = n; req.dropped = dropped; if (copy_to_user((void __user *)arg, &req, sizeof(req))) return -EFAULT; rv = 0; break; } case XR_USB_SERIAL_SET_DTTTY_RX: { int enable = (int)arg; unsigned long flags; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); xr_usb_serial->dttty_rx = (enable != 0); if (xr_usb_serial->dttty_rx) pog422_dttty_flush_locked(xr_usb_serial); spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); rv = 0; break; } case XR_USB_SERIAL_GET_DTTTY_RX_AVAIL: { struct xr_pog422_dttty_avail info; unsigned long flags; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); info.avail = pog422_dttty_avail_locked(xr_usb_serial); info.total_written = xr_usb_serial->dttty_total_written; info.overruns = xr_usb_serial->dttty_overruns; info.dttty_rx = xr_usb_serial->dttty_rx ? 1u : 0u; spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); if (copy_to_user((void __user *)arg, &info, sizeof(info))) return -EFAULT; rv = 0; break; } case XR_USB_SERIAL_READ_DTTTY_RX: { struct xr_pog422_dttty_read req; unsigned long flags; u8 *kbuf = NULL; u32 out_len = 0; if (copy_from_user(&req, (void __user *)arg, sizeof(req))) return -EFAULT; if (req.max_len == 0 || req.user_buf == 0) return -EINVAL; if (req.max_len > POG422_DTTTY_RX_SIZE) req.max_len = POG422_DTTTY_RX_SIZE; kbuf = kmalloc(req.max_len, GFP_KERNEL); if (!kbuf) return -ENOMEM; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); while (out_len < req.max_len && xr_usb_serial->dttty_tail != xr_usb_serial->dttty_head) { kbuf[out_len++] = xr_usb_serial->dttty_buf[xr_usb_serial->dttty_tail]; xr_usb_serial->dttty_tail = (xr_usb_serial->dttty_tail + 1) & (POG422_DTTTY_RX_SIZE - 1); } req.out_len = out_len; req.avail = pog422_dttty_avail_locked(xr_usb_serial); req.total_written = xr_usb_serial->dttty_total_written; req.overruns = xr_usb_serial->dttty_overruns; spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); if (out_len > 0 && copy_to_user((void __user *)(uintptr_t)req.user_buf, kbuf, out_len)) { kfree(kbuf); return -EFAULT; } kfree(kbuf); if (copy_to_user((void __user *)arg, &req, sizeof(req))) { return -EFAULT; } rv = 0; break; } case XR_USB_SERIAL_FLUSH_DTTTY_RX: { unsigned long flags; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); pog422_dttty_flush_locked(xr_usb_serial); spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); rv = 0; break; } case XR_USB_SERIAL_SET_WIDE_RX: { int enable = (int)arg; unsigned long flags; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); xr_usb_serial->wide_rx = (enable != 0); if (xr_usb_serial->wide_rx) pog422_wide_rx_flush_locked(xr_usb_serial); spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); rv = 0; break; } case XR_USB_SERIAL_GET_WIDE_RX_AVAIL: { struct xr_pog422_wide_rx_avail info; unsigned long flags; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); info.avail = pog422_wide_rx_avail_bytes_locked(xr_usb_serial); info.total_written = xr_usb_serial->wide_rx_total_written; info.overruns = xr_usb_serial->wide_rx_overruns; info.wide_rx = xr_usb_serial->wide_rx ? 1u : 0u; spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); if (copy_to_user((void __user *)arg, &info, sizeof(info))) return -EFAULT; rv = 0; break; } case XR_USB_SERIAL_READ_WIDE_RX: { struct xr_pog422_wide_rx_read req; unsigned long flags; u8 *kbuf = NULL; u32 out_len = 0; u32 max_pairs; if (copy_from_user(&req, (void __user *)arg, sizeof(req))) return -EFAULT; if (req.max_len == 0 || req.user_buf == 0) return -EINVAL; if (req.max_len > (POG422_WIDE_RX_SIZE * 2u)) req.max_len = POG422_WIDE_RX_SIZE * 2u; req.max_len &= ~1u; /* even only */ if (req.max_len == 0) return -EINVAL; kbuf = kmalloc(req.max_len, GFP_KERNEL); if (!kbuf) return -ENOMEM; max_pairs = req.max_len / 2u; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); while (out_len / 2u < max_pairs && xr_usb_serial->wide_rx_tail != xr_usb_serial->wide_rx_head) { unsigned int idx = xr_usb_serial->wide_rx_tail; kbuf[out_len++] = xr_usb_serial->wide_rx_buf[idx * 2u]; kbuf[out_len++] = xr_usb_serial->wide_rx_buf[idx * 2u + 1u]; xr_usb_serial->wide_rx_tail = (xr_usb_serial->wide_rx_tail + 1) & (POG422_WIDE_RX_SIZE - 1); } req.out_len = out_len; req.avail = pog422_wide_rx_avail_bytes_locked(xr_usb_serial); req.total_written = xr_usb_serial->wide_rx_total_written; req.overruns = xr_usb_serial->wide_rx_overruns; spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); if (out_len > 0 && copy_to_user((void __user *)(uintptr_t)req.user_buf, kbuf, out_len)) { kfree(kbuf); return -EFAULT; } kfree(kbuf); if (copy_to_user((void __user *)arg, &req, sizeof(req))) return -EFAULT; rv = 0; break; } case XR_USB_SERIAL_FLUSH_WIDE_RX: { unsigned long flags; spin_lock_irqsave(&xr_usb_serial->read_lock, flags); pog422_wide_rx_flush_locked(xr_usb_serial); spin_unlock_irqrestore(&xr_usb_serial->read_lock, flags); rv = 0; break; } case XR_USB_SERIAL_INJECT_TX: rv = xr_usb_serial_pog422_inject_tx(xr_usb_serial, (struct xr_pog422_inject_tx __user *)arg); break; case XR_USB_SERIAL_WAIT_TX_EMPTY: rv = xr_usb_serial_pog422_wait_tx_empty(xr_usb_serial, (struct xr_pog422_tx_empty __user *)arg); break; case XR_USB_SERIAL_DRAIN_HWDBG: { struct xr_pog422_hwdbg_drain req; #if defined(POG422_HW_DEBUG) && (POG422_HW_DEBUG) struct xr_pog422_hwdbg_edge *kbuf = NULL; unsigned long flags; u32 n, start, i, dropped; #endif if (copy_from_user(&req, (void __user *)arg, sizeof(req))) return -EFAULT; #if !(defined(POG422_HW_DEBUG) && (POG422_HW_DEBUG)) req.out_n = 0; req.dropped = 0; req.hw_debug = 0; if (copy_to_user((void __user *)arg, &req, sizeof(req))) return -EFAULT; return -EOPNOTSUPP; #else if (req.max_out == 0 || !req.user_buf) return -EINVAL; if (req.max_out > POG422_HWDBG_EDGE_MAX) req.max_out = POG422_HWDBG_EDGE_MAX; kbuf = kmalloc_array(req.max_out, sizeof(*kbuf), GFP_KERNEL); if (!kbuf) return -ENOMEM; spin_lock_irqsave(&xr_usb_serial->gpio1_lock, flags); n = xr_usb_serial->hwdbg_count; if (n > req.max_out) n = req.max_out; start = (xr_usb_serial->hwdbg_head + POG422_HWDBG_EDGE_MAX - xr_usb_serial->hwdbg_count) % POG422_HWDBG_EDGE_MAX; for (i = 0; i < n; i++) { u32 idx = (start + i) % POG422_HWDBG_EDGE_MAX; kbuf[i].timestamp_ns = xr_usb_serial->hwdbg_ts[idx]; kbuf[i].gpio_state = xr_usb_serial->hwdbg_gpio_state[idx]; kbuf[i].gpio_int = xr_usb_serial->hwdbg_gpio_int[idx]; kbuf[i].flags = xr_usb_serial->hwdbg_flags[idx]; memset(kbuf[i]._pad, 0, sizeof(kbuf[i]._pad)); } dropped = xr_usb_serial->hwdbg_dropped; xr_usb_serial->hwdbg_head = 0; xr_usb_serial->hwdbg_count = 0; xr_usb_serial->hwdbg_dropped = 0; spin_unlock_irqrestore(&xr_usb_serial->gpio1_lock, flags); if (copy_to_user((void __user *)(uintptr_t)req.user_buf, kbuf, n * sizeof(*kbuf))) { kfree(kbuf); return -EFAULT; } kfree(kbuf); req.out_n = n; req.dropped = dropped; req.hw_debug = 1; if (copy_to_user((void __user *)arg, &req, sizeof(req))) return -EFAULT; rv = 0; break; #endif } } return rv; } /* * MARK/SPACE inject flips parity between address and data bytes. A full * set_termios (disable UART + set_flow_mode RS485 re-init) drops GPIOB7 * RS485_EN_ACT mid-burst. When only CDC parity changes, send SET_LINE_CODING * with the UART left enabled — matches mainline xr_serial B142x path. */ static int xr_usb_serial_line_parity_only(const struct usb_cdc_line_coding *old_lc, const struct usb_cdc_line_coding *new_lc) { return old_lc->dwDTERate == new_lc->dwDTERate && old_lc->bCharFormat == new_lc->bCharFormat && old_lc->bDataBits == new_lc->bDataBits && old_lc->bParityType != new_lc->bParityType; } static void xr_usb_serial_tty_set_termios(struct tty_struct *tty, const struct ktermios *termios_old) { struct xr_usb_serial *xr_usb_serial = tty->driver_data; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) struct ktermios *termios = tty->termios; #else struct ktermios *termios = &tty->termios; #endif unsigned int cflag = termios->c_cflag; struct usb_cdc_line_coding newline; int newctrl = xr_usb_serial->ctrlout; int next_trans9; newline.dwDTERate = cpu_to_le32(tty_get_baud_rate(tty)); newline.bCharFormat = termios->c_cflag & CSTOPB ? 1 : 0; newline.bParityType = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; next_trans9 = 0; switch (termios->c_cflag & CSIZE) { case CS5:/*using CS5 replace of the 9 bit data mode*/ newline.bDataBits = 9; next_trans9 = 1; break; case CS6: newline.bDataBits = 6; break; case CS7: newline.bDataBits = 7; break; case CS8: default: newline.bDataBits = 8; break; } /* FIXME: Needs to clear unsupported bits in the termios */ xr_usb_serial->clocal = ((termios->c_cflag & CLOCAL) != 0); if (!memcmp(&xr_usb_serial->line, &newline, sizeof(newline)) && xr_usb_serial->trans9 == next_trans9) return; if (xr_usb_serial_is_b142x(xr_usb_serial) && xr_usb_serial_line_parity_only(&xr_usb_serial->line, &newline) && xr_usb_serial->trans9 == next_trans9) { memcpy(&xr_usb_serial->line, &newline, sizeof(newline)); xr_usb_serial_set_line(xr_usb_serial, &xr_usb_serial->line); return; } xr_usb_serial_disable(xr_usb_serial); xr_usb_serial->trans9 = next_trans9; if (!newline.dwDTERate) { newline.dwDTERate = xr_usb_serial->line.dwDTERate; newctrl &= ~XR_USB_SERIAL_CTRL_DTR; } else newctrl |= XR_USB_SERIAL_CTRL_DTR; if (newctrl != xr_usb_serial->ctrlout) xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout = newctrl); xr_usb_serial_set_flow_mode(xr_usb_serial,tty,cflag);/*set the serial flow mode*/ if (xr_usb_serial->trans9) { /* Turn on wide mode if we're 9-bit transparent. */ xr_usb_serial_set_wide_mode(xr_usb_serial,1); } else if (!xr_usb_serial->preciseflags) { xr_usb_serial_set_wide_mode(xr_usb_serial,0); } if (memcmp(&xr_usb_serial->line, &newline, sizeof newline)) { memcpy(&xr_usb_serial->line, &newline, sizeof newline); dev_dbg(&xr_usb_serial->control->dev, "%s - set line: %d %d %d %d\n", __func__, le32_to_cpu(newline.dwDTERate), newline.bCharFormat, newline.bParityType, newline.bDataBits); xr_usb_serial_set_line(xr_usb_serial, &xr_usb_serial->line); } xr_usb_serial_enable(xr_usb_serial); } static const struct tty_port_operations xr_usb_serial_port_ops = { .shutdown = xr_usb_serial_port_shutdown, .activate = xr_usb_serial_port_activate, .destruct = xr_usb_serial_port_destruct, }; /* * USB probe and disconnect routines. */ /* Little helpers: write/read buffers free */ static void xr_usb_serial_write_buffers_free(struct xr_usb_serial *xr_usb_serial) { int i; struct xr_usb_serial_wb *wb; struct usb_device *usb_dev = interface_to_usbdev(xr_usb_serial->control); for (wb = &xr_usb_serial->wb[0], i = 0; i < XR_USB_SERIAL_NW; i++, wb++) usb_free_coherent(usb_dev, xr_usb_serial->writesize, wb->buf, wb->dmah); } static void xr_usb_serial_read_buffers_free(struct xr_usb_serial *xr_usb_serial) { struct usb_device *usb_dev = interface_to_usbdev(xr_usb_serial->control); int i; for (i = 0; i < xr_usb_serial->rx_buflimit; i++) usb_free_coherent(usb_dev, xr_usb_serial->readsize, xr_usb_serial->read_buffers[i].base, xr_usb_serial->read_buffers[i].dma); } /* Little helper: write buffers allocate */ static int xr_usb_serial_write_buffers_alloc(struct xr_usb_serial *xr_usb_serial) { int i; struct xr_usb_serial_wb *wb; for (wb = &xr_usb_serial->wb[0], i = 0; i < XR_USB_SERIAL_NW; i++, wb++) { wb->buf = usb_alloc_coherent(xr_usb_serial->dev, xr_usb_serial->writesize, GFP_KERNEL, &wb->dmah); if (!wb->buf) { while (i != 0) { --i; --wb; usb_free_coherent(xr_usb_serial->dev, xr_usb_serial->writesize, wb->buf, wb->dmah); } return -ENOMEM; } } return 0; } static int xr_usb_serial_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = NULL; struct usb_cdc_country_functional_desc *cfd = NULL; unsigned char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; struct usb_interface *data_interface; struct usb_endpoint_descriptor *epctrl = NULL; struct usb_endpoint_descriptor *epread = NULL; struct usb_endpoint_descriptor *epwrite = NULL; struct usb_device *usb_dev = interface_to_usbdev(intf); struct xr_usb_serial *xr_usb_serial; int minor; int ctrlsize, readsize; u8 *buf; u8 ac_management_function = 0; u8 call_management_function = 0; int call_interface_num = -1; int data_interface_num = -1; unsigned long quirks; int num_rx_buf; int i; int combined_interfaces = 0; struct device *tty_dev; int rv = -ENOMEM; /* normal quirks */ quirks = (unsigned long)id->driver_info; if (quirks == IGNORE_DEVICE) return -ENODEV; num_rx_buf = (quirks == SINGLE_RX_URB) ? 1 : XR_USB_SERIAL_NR; dev_dbg(&intf->dev, "USB_device_id idVendor:%04x, idProduct %04x\n",id->idVendor,id->idProduct); /* handle quirks deadly to normal probing*/ if (quirks == NO_UNION_NORMAL) { data_interface = usb_ifnum_to_if(usb_dev, 1); control_interface = usb_ifnum_to_if(usb_dev, 0); goto skip_normal_probe; } /* normal probing*/ if (!buffer) { dev_err(&intf->dev, "Weird descriptor references\n"); return -EINVAL; } if (!buflen) { if (intf->cur_altsetting->endpoint && intf->cur_altsetting->endpoint->extralen && intf->cur_altsetting->endpoint->extra) { dev_dbg(&intf->dev, "Seeking extra descriptors on endpoint\n"); buflen = intf->cur_altsetting->endpoint->extralen; buffer = intf->cur_altsetting->endpoint->extra; } else { dev_err(&intf->dev, "Zero length descriptor references\n"); return -EINVAL; } } while (buflen > 0) { if (buffer[1] != USB_DT_CS_INTERFACE) { dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; } switch (buffer[2]) { case USB_CDC_UNION_TYPE: /* we've found it */ if (union_header) { dev_err(&intf->dev, "More than one " "union descriptor, skipping ...\n"); goto next_desc; } union_header = (struct usb_cdc_union_desc *)buffer; break; case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ cfd = (struct usb_cdc_country_functional_desc *)buffer; break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: ac_management_function = buffer[3]; break; case USB_CDC_CALL_MANAGEMENT_TYPE: call_management_function = buffer[3]; call_interface_num = buffer[4]; if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); break; default: /* there are LOTS more CDC descriptors that * could legitimately be found here. */ dev_dbg(&intf->dev, "Ignoring descriptor: " "type %02x, length %d\n", buffer[2], buffer[0]); break; } next_desc: buflen -= buffer[0]; buffer += buffer[0]; } if (!union_header) { if (call_interface_num > 0) { dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); /* quirks for Droids MuIn LCD */ if (quirks & NO_DATA_INTERFACE) data_interface = usb_ifnum_to_if(usb_dev, 0); else data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); control_interface = intf; } else { if (intf->cur_altsetting->desc.bNumEndpoints != 3) { dev_dbg(&intf->dev,"No union descriptor, giving up\n"); return -ENODEV; } else { dev_warn(&intf->dev,"No union descriptor, testing for castrated device\n"); combined_interfaces = 1; control_interface = data_interface = intf; goto look_for_collapsed_interface; } } } else { control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); if (!control_interface || !data_interface) { dev_dbg(&intf->dev, "no interfaces\n"); return -ENODEV; } } if (data_interface_num != call_interface_num) dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n"); if (control_interface == data_interface) { /* some broken devices designed for windows work this way */ dev_warn(&intf->dev,"Control and data interfaces are not separated!\n"); combined_interfaces = 1; /* a popular other OS doesn't use it */ quirks |= NO_CAP_LINE; if (data_interface->cur_altsetting->desc.bNumEndpoints != 3) { dev_err(&intf->dev, "This needs exactly 3 endpoints\n"); return -EINVAL; } look_for_collapsed_interface: for (i = 0; i < 3; i++) { struct usb_endpoint_descriptor *ep; ep = &data_interface->cur_altsetting->endpoint[i].desc; if (usb_endpoint_is_int_in(ep)) epctrl = ep; else if (usb_endpoint_is_bulk_out(ep)) epwrite = ep; else if (usb_endpoint_is_bulk_in(ep)) epread = ep; else return -EINVAL; } if (!epctrl || !epread || !epwrite) return -ENODEV; else goto made_compressed_probe; } skip_normal_probe: /*workaround for switched interfaces */ if (data_interface->cur_altsetting->desc.bInterfaceClass != CDC_DATA_INTERFACE_TYPE) { if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { struct usb_interface *t; dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); t = control_interface; control_interface = data_interface; data_interface = t; } else { return -EINVAL; } } /* Accept probe requests only for the control interface */ if (!combined_interfaces && intf != control_interface) return -ENODEV; if (!combined_interfaces && usb_interface_claimed(data_interface)) { /* valid in this context */ dev_dbg(&intf->dev, "The data interface isn't available\n"); return -EBUSY; } if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 || control_interface->cur_altsetting->desc.bNumEndpoints == 0) return -EINVAL; epctrl = &control_interface->cur_altsetting->endpoint[0].desc; epread = &data_interface->cur_altsetting->endpoint[0].desc; epwrite = &data_interface->cur_altsetting->endpoint[1].desc; /* workaround for switched endpoints */ if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ struct usb_endpoint_descriptor *t; dev_dbg(&intf->dev, "The data interface has switched endpoints\n"); t = epread; epread = epwrite; epwrite = t; } made_compressed_probe: dev_dbg(&intf->dev, "interfaces are valid\n"); xr_usb_serial = kzalloc(sizeof(struct xr_usb_serial), GFP_KERNEL); if (xr_usb_serial == NULL) { dev_err(&intf->dev, "out of memory (xr_usb_serial kzalloc)\n"); goto alloc_fail; } minor = xr_usb_serial_alloc_minor(xr_usb_serial); if (minor == XR_USB_SERIAL_TTY_MINORS) { dev_err(&intf->dev, "no more free xr_usb_serial devices\n"); kfree(xr_usb_serial); return -ENODEV; } ctrlsize = usb_endpoint_maxp(epctrl); readsize = usb_endpoint_maxp(epread) * (quirks == SINGLE_RX_URB ? 1 : 2); xr_usb_serial->combined_interfaces = combined_interfaces; xr_usb_serial->writesize = usb_endpoint_maxp(epwrite) * 20; xr_usb_serial->control = control_interface; xr_usb_serial->data = data_interface; xr_usb_serial->minor = minor; xr_usb_serial->dev = usb_dev; xr_usb_serial->ctrl_caps = ac_management_function; if (quirks & NO_CAP_LINE) xr_usb_serial->ctrl_caps &= ~USB_CDC_CAP_LINE; xr_usb_serial->ctrlsize = ctrlsize; xr_usb_serial->readsize = readsize; xr_usb_serial->rx_buflimit = num_rx_buf; INIT_WORK(&xr_usb_serial->work, xr_usb_serial_softint); INIT_DELAYED_WORK(&xr_usb_serial->de_cap_work, pog422_de_cap_work_fn); atomic_set(&xr_usb_serial->de_manual_held, 0); spin_lock_init(&xr_usb_serial->write_lock); spin_lock_init(&xr_usb_serial->read_lock); atomic64_set(&xr_usb_serial->rx_chars_inserted, 0); atomic64_set(&xr_usb_serial->rx_urbs_completed, 0); mutex_init(&xr_usb_serial->mutex); init_waitqueue_head(&xr_usb_serial->gpio1_wait); init_waitqueue_head(&xr_usb_serial->tx_empty_wait); spin_lock_init(&xr_usb_serial->gpio1_lock); xr_usb_serial->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); xr_usb_serial->is_int_ep = usb_endpoint_xfer_int(epread); if (xr_usb_serial->is_int_ep) xr_usb_serial->bInterval = epread->bInterval; tty_port_init(&xr_usb_serial->port); xr_usb_serial->port.ops = &xr_usb_serial_port_ops; xr_usb_serial->DeviceVendor = id->idVendor; xr_usb_serial->DeviceProduct = id->idProduct; #if 0 if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1410) {//map the serial port A B C D to blocknum 0 1 2 3 for the xr21v141x device xr_usb_serial->channel = epwrite->bEndpointAddress - 1; } else if((xr_usb_serial->DeviceProduct&0xfff0) == 0x1420) {//map the serial port A B C D to blocknum 0 2 4 6 for the xr21B142x device xr_usb_serial->channel = (epwrite->bEndpointAddress - 4)*2; } else { xr_usb_serial->channel = epwrite->bEndpointAddress; } #else xr_usb_serial->channel = epwrite->bEndpointAddress; dev_dbg(&intf->dev, "epwrite->bEndpointAddress =%d\n",epwrite->bEndpointAddress); #endif buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &xr_usb_serial->ctrl_dma); if (!buf) { dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n"); goto alloc_fail2; } xr_usb_serial->ctrl_buffer = buf; if (xr_usb_serial_write_buffers_alloc(xr_usb_serial) < 0) { dev_err(&intf->dev, "out of memory (write buffer alloc)\n"); goto alloc_fail4; } xr_usb_serial->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); if (!xr_usb_serial->ctrlurb) { dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); goto alloc_fail5; } for (i = 0; i < num_rx_buf; i++) { struct xr_usb_serial_rb *rb = &(xr_usb_serial->read_buffers[i]); struct urb *urb; rb->base = usb_alloc_coherent(xr_usb_serial->dev, readsize, GFP_KERNEL, &rb->dma); if (!rb->base) { dev_err(&intf->dev, "out of memory " "(read bufs usb_alloc_coherent)\n"); goto alloc_fail6; } rb->index = i; rb->instance = xr_usb_serial; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { dev_err(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); goto alloc_fail6; } urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; if (xr_usb_serial->is_int_ep) { usb_fill_int_urb(urb, xr_usb_serial->dev, xr_usb_serial->rx_endpoint, rb->base, xr_usb_serial->readsize, xr_usb_serial_read_bulk_callback, rb, xr_usb_serial->bInterval); } else { usb_fill_bulk_urb(urb, xr_usb_serial->dev, xr_usb_serial->rx_endpoint, rb->base, xr_usb_serial->readsize, xr_usb_serial_read_bulk_callback, rb); } xr_usb_serial->read_urbs[i] = urb; __set_bit(i, &xr_usb_serial->read_urbs_free); } for (i = 0; i < XR_USB_SERIAL_NW; i++) { struct xr_usb_serial_wb *snd = &(xr_usb_serial->wb[i]); snd->urb = usb_alloc_urb(0, GFP_KERNEL); if (snd->urb == NULL) { dev_err(&intf->dev, "out of memory (write urbs usb_alloc_urb)\n"); goto alloc_fail7; } if (usb_endpoint_xfer_int(epwrite)) usb_fill_int_urb(snd->urb, usb_dev, #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), #else usb_sndintpipe(usb_dev, epwrite->bEndpointAddress), #endif NULL, xr_usb_serial->writesize, xr_usb_serial_write_bulk, snd, epwrite->bInterval); else usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), NULL, xr_usb_serial->writesize, xr_usb_serial_write_bulk, snd); snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; snd->instance = xr_usb_serial; } usb_set_intfdata(intf, xr_usb_serial); xr_usb_serial->rs485_422_en = false; //default enable rs232 i = device_create_file(&intf->dev, &dev_attr_bRS485_422_en); if (i < 0) goto alloc_fail7; i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); if (i < 0) goto alloc_fail8; if (cfd) { /* export the country data */ xr_usb_serial->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); if (!xr_usb_serial->country_codes) goto skip_countries; xr_usb_serial->country_code_size = cfd->bLength - 4; memcpy(xr_usb_serial->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); xr_usb_serial->country_rel_date = cfd->iCountryCodeRelDate; i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); if (i < 0) { kfree(xr_usb_serial->country_codes); xr_usb_serial->country_codes = NULL; xr_usb_serial->country_code_size = 0; goto skip_countries; } i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); if (i < 0) { device_remove_file(&intf->dev, &dev_attr_wCountryCodes); kfree(xr_usb_serial->country_codes); xr_usb_serial->country_codes = NULL; xr_usb_serial->country_code_size = 0; goto skip_countries; } } skip_countries: usb_fill_int_urb(xr_usb_serial->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), xr_usb_serial->ctrl_buffer, ctrlsize, xr_usb_serial_ctrl_irq, xr_usb_serial, /* works around buggy devices */ epctrl->bInterval ? epctrl->bInterval : 0xff); xr_usb_serial->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; xr_usb_serial->ctrlurb->transfer_dma = xr_usb_serial->ctrl_dma; dev_info(&intf->dev, "ttyXR_USB_SERIAL%d: USB XR_USB_SERIAL device\n", minor); xr_usb_serial_pre_setup(xr_usb_serial); xr_usb_serial_set_control(xr_usb_serial, xr_usb_serial->ctrlout); xr_usb_serial->line.dwDTERate = cpu_to_le32(9600); xr_usb_serial->line.bDataBits = 8; xr_usb_serial_set_line(xr_usb_serial, &xr_usb_serial->line); /* Last step in probe: RS485 auto on every B142x block (MST ch0 + SLV ch2). * Runs before userspace — earliest bus unlock after hat USB enumerate. */ xr_usb_serial_pog422_rs485_probe_finish(xr_usb_serial); usb_driver_claim_interface(&xr_usb_serial_driver, data_interface, xr_usb_serial); usb_set_intfdata(data_interface, xr_usb_serial); usb_get_intf(control_interface); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) tty_register_device(xr_usb_serial_tty_driver, minor, &control_interface->dev); #else tty_dev = tty_port_register_device(&xr_usb_serial->port, xr_usb_serial_tty_driver, minor, &control_interface->dev); if (IS_ERR(tty_dev)) { rv = PTR_ERR(tty_dev); goto alloc_fail9; } #endif return 0; alloc_fail9: if (xr_usb_serial->country_codes) { device_remove_file(&xr_usb_serial->control->dev, &dev_attr_wCountryCodes); device_remove_file(&xr_usb_serial->control->dev, &dev_attr_iCountryCodeRelDate); } device_remove_file(&xr_usb_serial->control->dev, &dev_attr_bmCapabilities); alloc_fail8: device_remove_file(&xr_usb_serial->control->dev, &dev_attr_bRS485_422_en); alloc_fail7: usb_set_intfdata(intf, NULL); for (i = 0; i < XR_USB_SERIAL_NW; i++) usb_free_urb(xr_usb_serial->wb[i].urb); alloc_fail6: for (i = 0; i < num_rx_buf; i++) usb_free_urb(xr_usb_serial->read_urbs[i]); xr_usb_serial_read_buffers_free(xr_usb_serial); usb_free_urb(xr_usb_serial->ctrlurb); alloc_fail5: xr_usb_serial_write_buffers_free(xr_usb_serial); alloc_fail4: usb_free_coherent(usb_dev, ctrlsize, xr_usb_serial->ctrl_buffer, xr_usb_serial->ctrl_dma); alloc_fail2: xr_usb_serial_release_minor(xr_usb_serial); kfree(xr_usb_serial); alloc_fail: return rv; } static void stop_data_traffic(struct xr_usb_serial *xr_usb_serial) { int i; dev_dbg(&xr_usb_serial->control->dev, "%s\n", __func__); usb_kill_urb(xr_usb_serial->ctrlurb); for (i = 0; i < XR_USB_SERIAL_NW; i++) usb_kill_urb(xr_usb_serial->wb[i].urb); for (i = 0; i < xr_usb_serial->rx_buflimit; i++) usb_kill_urb(xr_usb_serial->read_urbs[i]); cancel_work_sync(&xr_usb_serial->work); pog422_de_cap_disarm(xr_usb_serial); cancel_delayed_work_sync(&xr_usb_serial->de_cap_work); } static void xr_usb_serial_disconnect(struct usb_interface *intf) { struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); struct tty_struct *tty; int i; dev_dbg(&intf->dev, "%s\n", __func__); /* sibling interface is already cleaning up */ if (!xr_usb_serial) return; mutex_lock(&xr_usb_serial->mutex); xr_usb_serial->disconnected = true; if (xr_usb_serial->country_codes) { device_remove_file(&xr_usb_serial->control->dev, &dev_attr_wCountryCodes); device_remove_file(&xr_usb_serial->control->dev, &dev_attr_iCountryCodeRelDate); } device_remove_file(&xr_usb_serial->control->dev, &dev_attr_bmCapabilities); device_remove_file(&xr_usb_serial->control->dev, &dev_attr_bRS485_422_en); usb_set_intfdata(xr_usb_serial->control, NULL); usb_set_intfdata(xr_usb_serial->data, NULL); mutex_unlock(&xr_usb_serial->mutex); tty = tty_port_tty_get(&xr_usb_serial->port); if (tty) { tty_vhangup(tty); tty_kref_put(tty); } stop_data_traffic(xr_usb_serial); #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) tty_unregister_device(xr_usb_serial_tty_driver, xr_usb_serial->minor); #endif usb_free_urb(xr_usb_serial->ctrlurb); for (i = 0; i < XR_USB_SERIAL_NW; i++) usb_free_urb(xr_usb_serial->wb[i].urb); for (i = 0; i < xr_usb_serial->rx_buflimit; i++) usb_free_urb(xr_usb_serial->read_urbs[i]); xr_usb_serial_write_buffers_free(xr_usb_serial); usb_free_coherent(usb_dev, xr_usb_serial->ctrlsize, xr_usb_serial->ctrl_buffer, xr_usb_serial->ctrl_dma); xr_usb_serial_read_buffers_free(xr_usb_serial); if (!xr_usb_serial->combined_interfaces) usb_driver_release_interface(&xr_usb_serial_driver, intf == xr_usb_serial->control ? xr_usb_serial->data : xr_usb_serial->control); tty_port_put(&xr_usb_serial->port); } #ifdef CONFIG_PM static int xr_usb_serial_suspend(struct usb_interface *intf, pm_message_t message) { struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); int cnt; if (PMSG_IS_AUTO(message)) { int b; spin_lock_irq(&xr_usb_serial->write_lock); b = xr_usb_serial->transmitting; spin_unlock_irq(&xr_usb_serial->write_lock); if (b) return -EBUSY; } spin_lock_irq(&xr_usb_serial->read_lock); spin_lock(&xr_usb_serial->write_lock); cnt = xr_usb_serial->susp_count++; spin_unlock(&xr_usb_serial->write_lock); spin_unlock_irq(&xr_usb_serial->read_lock); if (cnt) return 0; if (tty_port_initialized(&xr_usb_serial->port)) stop_data_traffic(xr_usb_serial); return 0; } static int xr_usb_serial_resume(struct usb_interface *intf) { struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); struct xr_usb_serial_wb *wb; int rv = 0; int cnt; spin_lock_irq(&xr_usb_serial->read_lock); xr_usb_serial->susp_count -= 1; cnt = xr_usb_serial->susp_count; spin_unlock_irq(&xr_usb_serial->read_lock); if (cnt) return 0; if (tty_port_initialized(&xr_usb_serial->port)) { rv = usb_submit_urb(xr_usb_serial->ctrlurb, GFP_NOIO); spin_lock_irq(&xr_usb_serial->write_lock); if (xr_usb_serial->delayed_wb) { wb = xr_usb_serial->delayed_wb; xr_usb_serial->delayed_wb = NULL; spin_unlock_irq(&xr_usb_serial->write_lock); xr_usb_serial_start_wb(xr_usb_serial, wb); } else { spin_unlock_irq(&xr_usb_serial->write_lock); } /* * delayed error checking because we must * do the write path at all cost */ if (rv < 0) goto err_out; rv = xr_usb_serial_submit_read_urbs(xr_usb_serial, GFP_NOIO); } err_out: return rv; } static int xr_usb_serial_reset_resume(struct usb_interface *intf) { struct xr_usb_serial *xr_usb_serial = usb_get_intfdata(intf); #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) #else struct tty_struct *tty; #endif if (tty_port_initialized(&xr_usb_serial->port)) { #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 9, 0) tty_port_tty_hangup(&xr_usb_serial->port, false); #else tty = tty_port_tty_get(&xr_usb_serial->port); if (tty) { tty_hangup(tty); tty_kref_put(tty); } #endif } return xr_usb_serial_resume(intf); } #endif /* CONFIG_PM */ /* * USB driver structure. */ static const struct usb_device_id xr_usb_serial_ids[] = { { USB_DEVICE(0x04e2, 0x1410)}, { USB_DEVICE(0x04e2, 0x1411)}, { USB_DEVICE(0x04e2, 0x1412)}, { USB_DEVICE(0x04e2, 0x1414)}, { USB_DEVICE(0x04e2, 0x1420)}, { USB_DEVICE(0x04e2, 0x1421)}, { USB_DEVICE(0x04e2, 0x1422)}, { USB_DEVICE(0x04e2, 0x1424)}, { USB_DEVICE(0x04e2, 0x1400)}, { USB_DEVICE(0x04e2, 0x1401)}, { USB_DEVICE(0x04e2, 0x1402)}, { USB_DEVICE(0x04e2, 0x1403)}, { } }; MODULE_DEVICE_TABLE(usb, xr_usb_serial_ids); static struct usb_driver xr_usb_serial_driver = { .name = "cdc_xr_usb_serial", .probe = xr_usb_serial_probe, .disconnect = xr_usb_serial_disconnect, #ifdef CONFIG_PM .suspend = xr_usb_serial_suspend, .resume = xr_usb_serial_resume, .reset_resume = xr_usb_serial_reset_resume, #endif .id_table = xr_usb_serial_ids, #ifdef CONFIG_PM .supports_autosuspend = 1, #endif .disable_hub_initiated_lpm = 1, }; /* * TTY driver structures. */ static const struct tty_operations xr_usb_serial_ops = { .install = xr_usb_serial_tty_install, .open = xr_usb_serial_tty_open, .close = xr_usb_serial_tty_close, .cleanup = xr_usb_serial_tty_cleanup, .hangup = xr_usb_serial_tty_hangup, .write = xr_usb_serial_tty_write, .write_room = xr_usb_serial_tty_write_room, .ioctl = xr_usb_serial_tty_ioctl, .throttle = xr_usb_serial_tty_throttle, .unthrottle = xr_usb_serial_tty_unthrottle, .chars_in_buffer = xr_usb_serial_tty_chars_in_buffer, .break_ctl = xr_usb_serial_tty_break_ctl, .set_termios = xr_usb_serial_tty_set_termios, .tiocmget = xr_usb_serial_tty_tiocmget, .tiocmset = xr_usb_serial_tty_tiocmset, }; /* * Init / exit. */ static int __init xr_usb_serial_init(void) { int retval; xr_usb_serial_tty_driver = tty_alloc_driver(XR_USB_SERIAL_TTY_MINORS, 0); if (IS_ERR(xr_usb_serial_tty_driver)) return -ENOMEM; xr_usb_serial_tty_driver->driver_name = "xr_usb_serial", xr_usb_serial_tty_driver->name = "ttyXRUSB", xr_usb_serial_tty_driver->major = XR_USB_SERIAL_TTY_MAJOR, xr_usb_serial_tty_driver->minor_start = 0, xr_usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, xr_usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL, xr_usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; xr_usb_serial_tty_driver->init_termios = tty_std_termios; xr_usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(xr_usb_serial_tty_driver, &xr_usb_serial_ops); retval = tty_register_driver(xr_usb_serial_tty_driver); if (retval) { tty_driver_kref_put(xr_usb_serial_tty_driver); return retval; } retval = usb_register(&xr_usb_serial_driver); if (retval) { tty_unregister_driver(xr_usb_serial_tty_driver); tty_driver_kref_put(xr_usb_serial_tty_driver); return retval; } printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); return 0; } static void __exit xr_usb_serial_exit(void) { usb_deregister(&xr_usb_serial_driver); tty_unregister_driver(xr_usb_serial_tty_driver); tty_driver_kref_put(xr_usb_serial_tty_driver); } module_init(xr_usb_serial_init); module_exit(xr_usb_serial_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC " (POG422 fork 1D: GPIO interrupt)"); MODULE_VERSION("2.2.2.12"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(XR_USB_SERIAL_TTY_MAJOR);