/* * USB serial driver for USB to UART(s) chip ch342/ch343/ch344/ch347/ch9101/ch9102/ch9103/ch9104, etc. * * Copyright (C) 2022 Nanjing Qinheng Microelectronics Co., Ltd. * Web: http://wch.cn * Author: WCH * * 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. * * System required: * Kernel version beyond 3.4.x * Update Log: * V1.0 - initial version * V1.1 - added support of chip ch344, ch9101 and ch9103 * V1.2 - added gpio support of chip ch344 * V1.3 - added support of chip ch347 * V1.4 - added support of chip ch9104 * V1.5 - added gpio character device * - added supports for kernel version beyond 5.14.x * - removed the gpio ioctl commands */ #define DEBUG #define VERBOSE_DEBUG #undef DEBUG #undef VERBOSE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) #include #endif #include "ch343.h" #define DRIVER_AUTHOR "WCH" #define DRIVER_DESC "USB serial driver for ch342/ch343/ch344/ch347/ch9101/ch9102/ch9103/ch9104, etc." #define VERSION_DESC "V1.5 On 2022.12" #define IOCTL_MAGIC 'W' #define IOCTL_CMD_GETCHIPTYPE _IOR(IOCTL_MAGIC, 0x84, u16) #define IOCTL_CMD_CTRLIN _IOWR(IOCTL_MAGIC, 0x90, u16) #define IOCTL_CMD_CTRLOUT _IOW(IOCTL_MAGIC, 0x91, u16) #define IOCTL_CMD_GICOUNT _IOR(IOCTL_MAGIC, 0x92, u16) static struct usb_driver ch343_driver; static struct tty_driver *ch343_tty_driver; static struct usb_interface *g_intf; static DEFINE_IDR(ch343_minors); static DEFINE_MUTEX(ch343_minors_lock); static void ch343_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old); /* * Look up an ch343 structure by minor. If found and not disconnected, increment * its refcount and return it with its mutex held. */ static struct ch343 *ch343_get_by_minor(unsigned int minor) { struct ch343 *ch343; mutex_lock(&ch343_minors_lock); ch343 = idr_find(&ch343_minors, minor); if (ch343) { mutex_lock(&ch343->mutex); if (ch343->disconnected) { mutex_unlock(&ch343->mutex); ch343 = NULL; } else { tty_port_get(&ch343->port); mutex_unlock(&ch343->mutex); } } mutex_unlock(&ch343_minors_lock); return ch343; } /* * Try to find an available minor number and if found, associate it with 'ch343'. */ static int ch343_alloc_minor(struct ch343 *ch343) { int minor; mutex_lock(&ch343_minors_lock); minor = idr_alloc(&ch343_minors, ch343, 0, CH343_TTY_MINORS, GFP_KERNEL); mutex_unlock(&ch343_minors_lock); return minor; } /* Release the minor number associated with 'ch343'. */ static void ch343_release_minor(struct ch343 *ch343) { mutex_lock(&ch343_minors_lock); idr_remove(&ch343_minors, ch343->minor); mutex_unlock(&ch343_minors_lock); } /* * Functions for CH343 control messages. */ static int ch343_control_out(struct ch343 *ch343, u8 request, u16 value, u16 index) { int retval; retval = usb_autopm_get_interface(ch343->control); if (retval) return retval; retval = usb_control_msg(ch343->dev, usb_sndctrlpipe(ch343->dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, value, index, NULL, 0, DEFAULT_TIMEOUT); usb_autopm_put_interface(ch343->control); return retval; } static int ch343_control_in(struct ch343 *ch343, u8 request, u16 value, u16 index, char *buf, unsigned bufsize) { int retval; retval = usb_autopm_get_interface(ch343->control); if (retval) return retval; retval = usb_control_msg(ch343->dev, usb_rcvctrlpipe(ch343->dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, value, index, buf, bufsize, DEFAULT_TIMEOUT); usb_autopm_put_interface(ch343->control); return retval; } static int ch343_control_msg_out(struct ch343 *ch343, u8 request, u8 requesttype, u16 value, u16 index, void *buf, unsigned bufsize) { int retval; char *buffer; buffer = kmalloc(bufsize, GFP_KERNEL); if (!buffer) return -ENOMEM; retval = copy_from_user(buffer, (char __user *)buf, bufsize); if (retval) goto out; retval = usb_autopm_get_interface(ch343->control); if (retval) goto out; retval = usb_control_msg(ch343->dev, usb_sndctrlpipe(ch343->dev, 0), request, requesttype, value, index, buf, bufsize, DEFAULT_TIMEOUT); usb_autopm_put_interface(ch343->control); out: kfree(buffer); return retval; } static int ch343_control_msg_in(struct ch343 *ch343, u8 request, u8 requesttype, u16 value, u16 index, void *buf, unsigned bufsize) { int retval; char *buffer; buffer = kmalloc(bufsize, GFP_KERNEL); if (!buffer) return -ENOMEM; retval = usb_autopm_get_interface(ch343->control); if (retval) goto out; retval = usb_control_msg(ch343->dev, usb_rcvctrlpipe(ch343->dev, 0), request, requesttype, value, index, buffer, bufsize, DEFAULT_TIMEOUT); if (retval > 0) { if (copy_to_user((char __user *)buf, buffer, retval)) { retval = -EFAULT; } } usb_autopm_put_interface(ch343->control); out: kfree(buffer); return retval; } static inline int ch343_set_control(struct ch343 *ch343, int control) { if (ch343->iface <= 1) return ch343_control_out(ch343, CMD_C2 + ch343->iface, ~control, 0x0000); else if (ch343->iface <= 3) return ch343_control_out(ch343, CMD_C2 + 0x10 + (ch343->iface - 2), ~control, 0x0000); else return -1; } static inline int ch343_set_line(struct ch343 *ch343, struct usb_cdc_line_coding *line) { return 0; } static int ch343_get_status(struct ch343 *ch343) { char *buffer; int retval; const unsigned size = 2; unsigned long flags; buffer = kmalloc(size, GFP_KERNEL); if (!buffer) return -ENOMEM; retval = ch343_control_in(ch343, CMD_R, CMD_C3 + ch343->iface, 0, buffer, size); if (retval != size) goto out; /* setup the private status if available */ spin_lock_irqsave(&ch343->read_lock, flags); ch343->ctrlin = (~(*buffer)) & CH343_CTI_ST; spin_unlock_irqrestore(&ch343->read_lock, flags); out: kfree(buffer); return retval; } /* -------------------------------------------------------------------------- */ static int ch343_configure(struct ch343 *ch343) { char *buffer; int r; const unsigned size = 2; u8 chiptype; buffer = kmalloc(size, GFP_KERNEL); if (!buffer) return -ENOMEM; r = ch343_control_in(ch343, CMD_C6, 0, 0, buffer, size); if (r != size) goto out; chiptype = buffer[1]; switch (ch343->idProduct) { case 0x55D2: if (chiptype == 0x48) ch343->chiptype = CHIP_CH342F; else if (chiptype == 0x41) ch343->chiptype = CHIP_CH342K; break; case 0x55D3: if (chiptype == 0x08) ch343->chiptype = CHIP_CH343GP; else if (chiptype == 0x02) ch343->chiptype = CHIP_CH343J; else if (chiptype == 0x01) ch343->chiptype = CHIP_CH343K; else if (chiptype == 0x18) ch343->chiptype = CHIP_CH343G_AUTOBAUD; break; case 0x55D4: if (chiptype == 0x08) ch343->chiptype = CHIP_CH9102F; else if (chiptype == 0x09) ch343->chiptype = CHIP_CH9102X; break; case 0x55D5: if (chiptype == 0xC0) { if ((buffer[0] & 0xF0) == 0x40) ch343->chiptype = CHIP_CH344L; else ch343->chiptype = CHIP_CH344L_V2; } else ch343->chiptype = CHIP_CH344Q; break; case 0x55D7: if (chiptype == 0x4B) ch343->chiptype = CHIP_CH9103M; break; case 0x55D8: if (chiptype == 0x08) ch343->chiptype = CHIP_CH9101UH; else if (chiptype == 0x0A) ch343->chiptype = CHIP_CH9101RY; break; case 0x55DA: case 0x55DB: case 0x55DD: ch343->chiptype = CHIP_CH347T; break; case 0x55DF: ch343->chiptype = CHIP_CH9104L; break; default: break; } if (ch343->chiptype != CHIP_CH344L && ch343->chiptype != CHIP_CH344L_V2 && ch343->chiptype != CHIP_CH9104L) { r = ch343_get_status(ch343); if (r < 0) goto out; } dev_dbg(&ch343->data->dev, "%s - chip hver : 0x%2x, sver : 0x%2x, chip : %d\n", __func__, buffer[0], buffer[1], ch343->chiptype); out: kfree(buffer); return r < 0 ? r : 0; } /* * Write buffer management. * All of these assume proper locks taken by the caller. */ static int ch343_wb_alloc(struct ch343 *ch343) { int i, wbn; struct ch343_wb *wb; wbn = 0; i = 0; for (;;) { wb = &ch343->wb[wbn]; if (!wb->use) { wb->use = 1; return wbn; } wbn = (wbn + 1) % CH343_NW; if (++i >= CH343_NW) return -1; } } static int ch343_wb_is_avail(struct ch343 *ch343) { int i, n; unsigned long flags; n = CH343_NW; spin_lock_irqsave(&ch343->write_lock, flags); for (i = 0; i < CH343_NW; i++) n -= ch343->wb[i].use; spin_unlock_irqrestore(&ch343->write_lock, flags); return n; } /* * Finish write. Caller must hold ch343->write_lock */ static void ch343_write_done(struct ch343 *ch343, struct ch343_wb *wb) { wb->use = 0; ch343->transmitting--; usb_autopm_put_interface_async(ch343->control); } /* * Poke write. * * the caller is responsible for locking */ static int ch343_start_wb(struct ch343 *ch343, struct ch343_wb *wb) { int rc; ch343->transmitting++; wb->urb->transfer_buffer = wb->buf; wb->urb->transfer_dma = wb->dmah; wb->urb->transfer_buffer_length = wb->len; wb->urb->dev = ch343->dev; rc = usb_submit_urb(wb->urb, GFP_ATOMIC); if (rc < 0) { dev_err(&ch343->data->dev, "%s - usb_submit_urb(write bulk) failed: %d\n", __func__, rc); ch343_write_done(ch343, wb); } return rc; } static void ch343_update_status(struct ch343 *ch343, unsigned char *data, size_t len) { unsigned long flags; u8 status; u8 difference; u8 type = data[0]; u8 handled = 0; if (len < 4) return; if (ch343->chiptype == CHIP_CH344L) { if (data[0] != 0x00) return; type = data[1]; } else if (ch343->chiptype == CHIP_CH344Q || ch343->chiptype == CHIP_CH344L_V2 || ch343->chiptype == CHIP_CH9104L) { type = data[1]; } if (type & CH343_CTT_M) { status = ~data[len - 1] & CH343_CTI_ST; if (ch343->chiptype == CHIP_CH344L || ch343->chiptype == CHIP_CH344L_V2) status &= CH343_CTI_C; if (!ch343->clocal && (ch343->ctrlin & status & CH343_CTI_DC)) { tty_port_tty_hangup(&ch343->port, false); } spin_lock_irqsave(&ch343->read_lock, flags); difference = status ^ ch343->ctrlin; ch343->ctrlin = status; ch343->oldcount = ch343->iocount; if (difference) { if (difference & CH343_CTI_C) { ch343->iocount.cts++; } if (difference & CH343_CTI_DS) { ch343->iocount.dsr++; } if (difference & CH343_CTI_R) { ch343->iocount.rng++; } if (difference & CH343_CTI_DC) { ch343->iocount.dcd++; } spin_unlock_irqrestore(&ch343->read_lock, flags); wake_up_interruptible(&ch343->wioctl); } else spin_unlock_irqrestore(&ch343->read_lock, flags); handled = 1; } if (type & CH343_CTT_O) { spin_lock_irqsave(&ch343->read_lock, flags); ch343->oldcount = ch343->iocount; ch343->iocount.overrun++; spin_unlock_irqrestore(&ch343->read_lock, flags); handled = 1; } if ((type & CH343_CTT_F) == CH343_CTT_F) { spin_lock_irqsave(&ch343->read_lock, flags); ch343->oldcount = ch343->iocount; ch343->iocount.frame++; spin_unlock_irqrestore(&ch343->read_lock, flags); handled = 1; } else if (type & CH343_CTT_P) { spin_lock_irqsave(&ch343->read_lock, flags); ch343->oldcount = ch343->iocount; ch343->iocount.parity++; spin_unlock_irqrestore(&ch343->read_lock, flags); handled = 1; } if (!handled) dev_err(&ch343->control->dev, "%s - unknown status received:" "len:%d, data0:0x%x, data1:0x%x\n", __func__, (int)len, data[0], data[1]); } /* Reports status changes with "interrupt" transfers */ static void ch343_ctrl_irq(struct urb *urb) { struct ch343 *ch343 = urb->context; unsigned char *data = urb->transfer_buffer; unsigned int len = urb->actual_length; int status = urb->status; int retval; switch (status) { case 0: /* success */ break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ dev_dbg(&ch343->control->dev, "%s - urb shutting down with status: %d\n", __func__, status); return; default: dev_dbg(&ch343->control->dev, "%s - nonzero urb status received: %d\n", __func__, status); goto exit; } usb_mark_last_busy(ch343->dev); ch343_update_status(ch343, data, len); exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval && retval != -EPERM) dev_err(&ch343->control->dev, "%s - usb_submit_urb failed: %d\n", __func__, retval); } static int ch343_submit_read_urb(struct ch343 *ch343, int index, gfp_t mem_flags) { int res; if (!test_and_clear_bit(index, &ch343->read_urbs_free)) return 0; dev_vdbg(&ch343->data->dev, "%s - urb %d\n", __func__, index); res = usb_submit_urb(ch343->read_urbs[index], mem_flags); if (res) { if (res != -EPERM) { dev_err(&ch343->data->dev, "%s - usb_submit_urb failed: %d\n", __func__, res); } set_bit(index, &ch343->read_urbs_free); return res; } return 0; } static int ch343_submit_read_urbs(struct ch343 *ch343, gfp_t mem_flags) { int res; int i; for (i = 0; i < ch343->rx_buflimit; ++i) { res = ch343_submit_read_urb(ch343, i, mem_flags); if (res) return res; } return 0; } static void ch343_process_read_urb(struct ch343 *ch343, struct urb *urb) { if (!urb->actual_length) return; tty_insert_flip_string(&ch343->port, urb->transfer_buffer, urb->actual_length); tty_flip_buffer_push(&ch343->port); } static void ch343_read_bulk_callback(struct urb *urb) { struct ch343_rb *rb = urb->context; struct ch343 *ch343 = rb->instance; int status = urb->status; dev_vdbg(&ch343->data->dev, "%s - urb %d, len %d\n", __func__, rb->index, urb->actual_length); if (!ch343->dev) { set_bit(rb->index, &ch343->read_urbs_free); dev_dbg(&ch343->data->dev, "%s - disconnected\n", __func__); return; } if (status) { set_bit(rb->index, &ch343->read_urbs_free); dev_dbg(&ch343->data->dev, "%s - non-zero urb status: %d\n", __func__, status); return; } usb_mark_last_busy(ch343->dev); ch343_process_read_urb(ch343, urb); set_bit(rb->index, &ch343->read_urbs_free); ch343_submit_read_urb(ch343, rb->index, GFP_ATOMIC); } /* data interface wrote those outgoing bytes */ static void ch343_write_bulk(struct urb *urb) { struct ch343_wb *wb = urb->context; struct ch343 *ch343 = wb->instance; unsigned long flags; int status = urb->status; dev_vdbg(&ch343->data->dev, "%s, len %d\n", __func__, urb->actual_length); if (status || (urb->actual_length != urb->transfer_buffer_length)) dev_vdbg(&ch343->data->dev, "%s - len %d/%d, status %d\n", __func__, urb->actual_length, urb->transfer_buffer_length, status); spin_lock_irqsave(&ch343->write_lock, flags); ch343_write_done(ch343, wb); spin_unlock_irqrestore(&ch343->write_lock, flags); schedule_work(&ch343->work); } static void ch343_softint(struct work_struct *work) { struct ch343 *ch343 = container_of(work, struct ch343, work); dev_dbg(&ch343->data->dev, "%s\n", __func__); tty_port_tty_wakeup(&ch343->port); } /* * TTY handlers */ static int ch343_tty_install(struct tty_driver *driver, struct tty_struct *tty) { struct ch343 *ch343; int retval; dev_dbg(tty->dev, "%s\n", __func__); ch343 = ch343_get_by_minor(tty->index); if (!ch343) return -ENODEV; retval = tty_standard_install(driver, tty); if (retval) goto error_init_termios; tty->driver_data = ch343; return 0; error_init_termios: tty_port_put(&ch343->port); return retval; } static int ch343_tty_open(struct tty_struct *tty, struct file *filp) { struct ch343 *ch343 = tty->driver_data; dev_dbg(tty->dev, "%s\n", __func__); return tty_port_open(&ch343->port, tty, filp); } static void ch343_port_dtr_rts(struct tty_port *port, int raise) { struct ch343 *ch343 = container_of(port, struct ch343, port); int res; dev_dbg(&ch343->data->dev, "%s, raise:%d\n", __func__, raise); if (raise) ch343->ctrlout |= CH343_CTO_D | CH343_CTO_R; else ch343->ctrlout &= ~(CH343_CTO_D | CH343_CTO_R); res = ch343_set_control(ch343, ch343->ctrlout); if (res) dev_err(&ch343->control->dev, "failed to set dtr/rts\n"); } static int ch343_port_activate(struct tty_port *port, struct tty_struct *tty) { struct ch343 *ch343 = container_of(port, struct ch343, port); int retval = -ENODEV; int i; dev_dbg(&ch343->control->dev, "%s\n", __func__); mutex_lock(&ch343->mutex); if (ch343->disconnected) goto disconnected; retval = usb_autopm_get_interface(ch343->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); ch343->control->needs_remote_wakeup = 1; // retval = ch343_configure(ch343); // if (retval) // goto error_configure; ch343_tty_set_termios(tty, NULL); retval = usb_submit_urb(ch343->ctrlurb, GFP_KERNEL); if (retval) { dev_err(&ch343->control->dev, "%s - usb_submit_urb(ctrl cmd) failed\n", __func__); goto error_submit_urb; } retval = ch343_submit_read_urbs(ch343, GFP_KERNEL); if (retval) goto error_submit_read_urbs; usb_autopm_put_interface(ch343->control); mutex_unlock(&ch343->mutex); return 0; error_submit_read_urbs: for (i = 0; i < ch343->rx_buflimit; i++) usb_kill_urb(ch343->read_urbs[i]); error_submit_urb: usb_kill_urb(ch343->ctrlurb); // error_configure: usb_autopm_put_interface(ch343->control); error_get_interface: disconnected: mutex_unlock(&ch343->mutex); return usb_translate_errors(retval); } static void ch343_port_destruct(struct tty_port *port) { struct ch343 *ch343 = container_of(port, struct ch343, port); dev_dbg(&ch343->control->dev, "%s\n", __func__); ch343_release_minor(ch343); usb_put_intf(ch343->control); kfree(ch343); } static void ch343_port_shutdown(struct tty_port *port) { struct ch343 *ch343 = container_of(port, struct ch343, port); struct urb *urb; struct ch343_wb *wb; int i; dev_dbg(&ch343->control->dev, "%s\n", __func__); usb_autopm_get_interface_no_resume(ch343->control); ch343->control->needs_remote_wakeup = 0; usb_autopm_put_interface(ch343->control); for (;;) { urb = usb_get_from_anchor(&ch343->delayed); if (!urb) break; wb = urb->context; wb->use = 0; usb_autopm_put_interface_async(ch343->control); } usb_kill_urb(ch343->ctrlurb); for (i = 0; i < CH343_NW; i++) usb_kill_urb(ch343->wb[i].urb); for (i = 0; i < ch343->rx_buflimit; i++) usb_kill_urb(ch343->read_urbs[i]); } static void ch343_tty_cleanup(struct tty_struct *tty) { struct ch343 *ch343 = tty->driver_data; dev_dbg(&ch343->control->dev, "%s\n", __func__); tty_port_put(&ch343->port); } static void ch343_tty_hangup(struct tty_struct *tty) { struct ch343 *ch343 = tty->driver_data; dev_dbg(&ch343->control->dev, "%s\n", __func__); tty_port_hangup(&ch343->port); } static void ch343_tty_close(struct tty_struct *tty, struct file *filp) { struct ch343 *ch343 = tty->driver_data; dev_dbg(&ch343->control->dev, "%s\n", __func__); tty_port_close(&ch343->port, tty, filp); } static int ch343_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct ch343 *ch343 = tty->driver_data; int stat; unsigned long flags; int wbn; struct ch343_wb *wb; if (!count) return 0; dev_vdbg(&ch343->data->dev, "%s - count %d\n", __func__, count); spin_lock_irqsave(&ch343->write_lock, flags); wbn = ch343_wb_alloc(ch343); if (wbn < 0) { spin_unlock_irqrestore(&ch343->write_lock, flags); return 0; } wb = &ch343->wb[wbn]; if (!ch343->dev) { wb->use = 0; spin_unlock_irqrestore(&ch343->write_lock, flags); return -ENODEV; } count = (count > ch343->writesize) ? ch343->writesize : count; memcpy(wb->buf, buf, count); wb->len = count; stat = usb_autopm_get_interface_async(ch343->control); if (stat) { wb->use = 0; spin_unlock_irqrestore(&ch343->write_lock, flags); return stat; } if (ch343->susp_count) { usb_anchor_urb(wb->urb, &ch343->delayed); spin_unlock_irqrestore(&ch343->write_lock, flags); return count; } stat = ch343_start_wb(ch343, wb); spin_unlock_irqrestore(&ch343->write_lock, flags); if (stat < 0) return stat; return count; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) static unsigned int ch343_tty_write_room(struct tty_struct *tty) #else static int ch343_tty_write_room(struct tty_struct *tty) #endif { struct ch343 *ch343 = tty->driver_data; /* * Do not let the line discipline to know that we have a reserve, * or it might get too enthusiastic. */ return ch343_wb_is_avail(ch343) ? ch343->writesize : 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) static unsigned int ch343_tty_chars_in_buffer(struct tty_struct *tty) #else static int ch343_tty_chars_in_buffer(struct tty_struct *tty) #endif { struct ch343 *ch343 = tty->driver_data; /* * if the device was unplugged then any remaining characters fell out * of the connector ;) */ if (ch343->disconnected) return 0; /* * This is inaccurate (overcounts), but it works. */ return (CH343_NW - ch343_wb_is_avail(ch343)) * ch343->writesize; } static int ch343_tty_break_ctl(struct tty_struct *tty, int state) { struct ch343 *ch343 = tty->driver_data; int retval; uint16_t reg_contents; uint8_t *regbuf; dev_dbg(&ch343->control->dev, "%s\n", __func__); regbuf = kmalloc(2, GFP_KERNEL); if (!regbuf) return -1; if (state != 0) { if ((ch343->chiptype == CHIP_CH344L) || (ch343->chiptype == CHIP_CH344Q) || (ch343->chiptype == CHIP_CH344L_V2) || (ch343->chiptype == CHIP_CH9104L)) { regbuf[0] = ch343->iface; regbuf[1] = 0x01; } else { regbuf[0] = CH343_N_B; regbuf[1] = 0x00; } } else { if ((ch343->chiptype == CHIP_CH344L) || (ch343->chiptype == CHIP_CH344Q) || (ch343->chiptype == CHIP_CH344L_V2) || (ch343->chiptype == CHIP_CH9104L)) { regbuf[0] = ch343->iface; regbuf[1] = 0x00; } else { regbuf[0] = CH343_N_B | CH343_N_AB; regbuf[1] = 0x00; } } reg_contents = get_unaligned_le16(regbuf); if ((ch343->chiptype == CHIP_CH344L) || (ch343->chiptype == CHIP_CH344Q) || (ch343->chiptype == CHIP_CH344L_V2) || (ch343->chiptype == CHIP_CH9104L)) { retval = ch343_control_out(ch343, CMD_C4, reg_contents, 0x00); } else { if (ch343->iface) retval = ch343_control_out(ch343, CMD_C4, 0x00, reg_contents); else retval = ch343_control_out(ch343, CMD_C4, reg_contents, 0x00); } if (retval < 0) dev_err(&ch343->control->dev, "%s - USB control write error (%d)\n", __func__, retval); kfree(regbuf); return retval; } static int ch343_tty_tiocmget(struct tty_struct *tty) { struct ch343 *ch343 = tty->driver_data; unsigned long flags; unsigned int result; dev_dbg(&ch343->control->dev, "%s\n", __func__); spin_lock_irqsave(&ch343->read_lock, flags); result = (ch343->ctrlout & CH343_CTO_D ? TIOCM_DTR : 0) | (ch343->ctrlout & CH343_CTO_R ? TIOCM_RTS : 0) | (ch343->ctrlin & CH343_CTI_C ? TIOCM_CTS : 0) | (ch343->ctrlin & CH343_CTI_DS ? TIOCM_DSR : 0) | (ch343->ctrlin & CH343_CTI_R ? TIOCM_RI : 0) | (ch343->ctrlin & CH343_CTI_DC ? TIOCM_CD : 0); spin_unlock_irqrestore(&ch343->read_lock, flags); return result; } static int ch343_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct ch343 *ch343 = tty->driver_data; unsigned int newctrl; dev_dbg(&ch343->control->dev, "%s\n", __func__); newctrl = ch343->ctrlout; set = (set & TIOCM_DTR ? CH343_CTO_D : 0) | (set & TIOCM_RTS ? CH343_CTO_R : 0); clear = (clear & TIOCM_DTR ? CH343_CTO_D : 0) | (clear & TIOCM_RTS ? CH343_CTO_R : 0); newctrl = (newctrl & ~clear) | set; if (ch343->ctrlout == newctrl) { return 0; } return ch343_set_control(ch343, ch343->ctrlout = newctrl); } static int ch343_get_serial_info(struct ch343 *ch343, 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 = ch343->writesize; tmp.baud_base = le32_to_cpu(ch343->line.dwDTERate); tmp.close_delay = ch343->port.close_delay / 10; tmp.closing_wait = ch343->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : ch343->port.closing_wait / 10; if (copy_to_user(info, &tmp, sizeof(tmp))) return -EFAULT; else return 0; } static int ch343_set_serial_info(struct ch343 *ch343, 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(&ch343->port.mutex); if (!capable(CAP_SYS_ADMIN)) { if ((close_delay != ch343->port.close_delay) || (closing_wait != ch343->port.closing_wait)) retval = -EPERM; else retval = -EOPNOTSUPP; } else { ch343->port.close_delay = close_delay; ch343->port.closing_wait = closing_wait; } mutex_unlock(&ch343->port.mutex); return retval; } static int ch343_wait_serial_change(struct ch343 *ch343, unsigned long arg) { int rv = 0; DECLARE_WAITQUEUE(wait, current); struct async_icount old, new; do { spin_lock_irq(&ch343->read_lock); old = ch343->oldcount; new = ch343->iocount; ch343->oldcount = new; spin_unlock_irq(&ch343->read_lock); if ((arg & TIOCM_CTS) && old.cts != new.cts) break; if ((arg & TIOCM_DSR) && old.dsr != new.dsr) break; if ((arg & TIOCM_RI) && old.rng != new.rng) break; if ((arg & TIOCM_CD) && old.dcd != new.dcd) break; add_wait_queue(&ch343->wioctl, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule(); remove_wait_queue(&ch343->wioctl, &wait); if (ch343->disconnected) { if (arg & TIOCM_CD) break; else rv = -ENODEV; } else { if (signal_pending(current)) rv = -ERESTARTSYS; } } while (!rv); return rv; } static int ch343_get_serial_usage(struct ch343 *ch343, struct serial_icounter_struct __user *count) { struct serial_icounter_struct icount; int rv = 0; memset(&icount, 0, sizeof(icount)); icount.cts = ch343->iocount.cts; icount.dsr = ch343->iocount.dsr; icount.rng = ch343->iocount.rng; icount.dcd = ch343->iocount.dcd; icount.frame = ch343->iocount.frame; icount.overrun = ch343->iocount.overrun; icount.parity = ch343->iocount.parity; icount.brk = ch343->iocount.brk; if (copy_to_user(count, &icount, sizeof(icount)) > 0) rv = -EFAULT; return rv; } static int ch343_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct ch343 *ch343 = tty->driver_data; int rv = 0; unsigned long arg1, arg2, arg3, arg4, arg5, arg6; u32 __user *argval = (u32 __user *)arg; u8 *buffer; dev_dbg(&ch343->control->dev, "%s\n", __func__); buffer = kmalloc(512, GFP_KERNEL); if (!buffer) return -ENOMEM; switch (cmd) { case TIOCGSERIAL: /* gets serial port data */ rv = ch343_get_serial_info(ch343, (struct serial_struct __user *)arg); break; case TIOCSSERIAL: rv = ch343_set_serial_info(ch343, (struct serial_struct __user *)arg); break; case TIOCMIWAIT: rv = usb_autopm_get_interface(ch343->control); if (rv < 0) { rv = -EIO; break; } rv = ch343_wait_serial_change(ch343, arg); usb_autopm_put_interface(ch343->control); break; case IOCTL_CMD_GICOUNT: case TIOCGICOUNT: rv = ch343_get_serial_usage(ch343, (struct serial_icounter_struct __user *)arg); break; case IOCTL_CMD_GETCHIPTYPE: if (put_user(ch343->chiptype, argval)) { rv = -EFAULT; goto out; } break; case IOCTL_CMD_CTRLIN: get_user(arg1, (u8 __user *)arg); get_user(arg2, ((u8 __user *)arg + 1)); get_user(arg3, (u16 __user *)((u8 *)arg + 2)); get_user(arg4, (u16 __user *)((u8 *)arg + 4)); get_user(arg5, (u16 __user *)((u8 *)arg + 6)); arg6 = (unsigned long)((u8 __user *)arg + 8); rv = ch343_control_msg_in(ch343, (u8)arg1, (u8)arg2, (u16)arg3, (u16)arg4, (u8 __user *)arg6, (u16)arg5); break; case IOCTL_CMD_CTRLOUT: get_user(arg1, (u8 __user *)arg); get_user(arg2, ((u8 __user *)arg + 1)); get_user(arg3, (u16 __user *)((u8 *)arg + 2)); get_user(arg4, (u16 __user *)((u8 *)arg + 4)); get_user(arg5, (u16 __user *)((u8 *)arg + 6)); arg6 = (unsigned long)((u8 __user *)arg + 8); rv = ch343_control_msg_out(ch343, (u8)arg1, (u8)arg2, (u16)arg3, (u16)arg4, (u8 __user *)arg6, (u16)arg5); if (rv != (u16)arg5) { rv = -EINVAL; goto out; } break; default: rv = -ENOIOCTLCMD; break; } out: kfree(buffer); return rv < 0 ? rv : 0; } static int ch343_get(CHIPTYPE chiptype, unsigned int bval, unsigned char *fct, unsigned char *dvs) { unsigned char a; unsigned char b; unsigned long c; if (((chiptype == CHIP_CH347T) || (chiptype == CHIP_CH344Q) || (chiptype == CHIP_CH9104L)) && bval >= 2000000) { *fct = (unsigned char)(bval / 200); *dvs = (unsigned char)((bval / 200) >> 8); } switch (bval) { case 6000000: case 4000000: case 2400000: case 921600: case 307200: case 256000: b = 7; c = 12000000; break; default: if (bval > 6000000 / 255) { b = 3; c = 6000000; } else if (bval > 750000 / 255) { b = 2; c = 750000; } else if (bval > 93750 / 255) { b = 1; c = 93750; } else { b = 0; c = 11719; } break; } a = (unsigned char)(c / bval); if (a == 0 || a == 0xFF) return -EINVAL; if ((c / a - bval) > (bval - c / (a + 1))) a++; a = 256 - a; *fct = a; *dvs = b; return 0; } static void ch343_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) { struct ch343 *ch343 = tty->driver_data; struct ktermios *termios = &tty->termios; struct usb_ch343_line_coding newline; int newctrl = ch343->ctrlout; unsigned char dvs = 0; unsigned char reg_count = 0; unsigned char fct = 0; unsigned char reg_value = 0; unsigned short value = 0; unsigned short index = 0; dev_dbg(tty->dev, "%s\n", __func__); if (termios_old && !tty_termios_hw_change(&tty->termios, termios_old)) { return; } newline.dwDTERate = tty_get_baud_rate(tty); if (newline.dwDTERate == 0) newline.dwDTERate = 9600; ch343_get(ch343->chiptype, newline.dwDTERate, &fct, &dvs); newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 1; if (newline.bCharFormat == 2) reg_value |= CH343_L_SB; newline.bParityType = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; switch (newline.bParityType) { case 0x01: reg_value |= CH343_L_P_O; break; case 0x02: reg_value |= CH343_L_P_E; break; case 0x03: reg_value |= CH343_L_P_M; break; case 0x04: reg_value |= CH343_L_P_S; break; default: break; } switch (termios->c_cflag & CSIZE) { case CS5: newline.bDataBits = 5; reg_value |= CH343_L_C5; break; case CS6: newline.bDataBits = 6; reg_value |= CH343_L_C6; break; case CS7: newline.bDataBits = 7; reg_value |= CH343_L_C7; break; case CS8: default: newline.bDataBits = 8; reg_value |= CH343_L_C8; break; } /* FIXME: Needs to clear unsupported bits in the termios */ ch343->clocal = ((termios->c_cflag & CLOCAL) != 0); if (C_BAUD(tty) == B0) { newline.dwDTERate = ch343->line.dwDTERate; newctrl &= ~CH343_CTO_D; } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { newctrl |= CH343_CTO_D; } reg_value |= CH343_L_E_R | CH343_L_E_T; reg_count |= CH343_L_R_CT | CH343_L_R_CL | CH343_L_R_T; value |= reg_count; value |= (unsigned short)reg_value << 8; index |= 0x00 | dvs; index |= (unsigned short)fct << 8; if (ch343->iface <= 1) ch343_control_out(ch343, CMD_C1 + ch343->iface, value, index); else if (ch343->iface <= 3) ch343_control_out(ch343, CMD_C1 + 0x10 + (ch343->iface - 2), value, index); if (memcmp(&ch343->line, &newline, sizeof newline)) { memcpy(&ch343->line, &newline, sizeof newline); dev_dbg(&ch343->control->dev, "%s - set line: %d %d %d %d\n", __func__, newline.dwDTERate, newline.bCharFormat, newline.bParityType, newline.bDataBits); } if (C_CRTSCTS(tty)) { newctrl |= CH343_CTO_A | CH343_CTO_R; } else newctrl &= ~CH343_CTO_A; if (newctrl != ch343->ctrlout) ch343_set_control(ch343, ch343->ctrlout = newctrl); } static const struct tty_port_operations ch343_port_ops = { .dtr_rts = ch343_port_dtr_rts, .shutdown = ch343_port_shutdown, .activate = ch343_port_activate, .destruct = ch343_port_destruct, }; /* Little helpers: write/read buffers free */ static void ch343_write_buffers_free(struct ch343 *ch343) { int i; struct ch343_wb *wb; struct usb_device *usb_dev = interface_to_usbdev(ch343->control); for (wb = &ch343->wb[0], i = 0; i < CH343_NW; i++, wb++) usb_free_coherent(usb_dev, ch343->writesize, wb->buf, wb->dmah); } static void ch343_read_buffers_free(struct ch343 *ch343) { struct usb_device *usb_dev = interface_to_usbdev(ch343->control); int i; for (i = 0; i < ch343->rx_buflimit; i++) usb_free_coherent(usb_dev, ch343->readsize, ch343->read_buffers[i].base, ch343->read_buffers[i].dma); } /* Little helper: write buffers allocate */ static int ch343_write_buffers_alloc(struct ch343 *ch343) { int i; struct ch343_wb *wb; for (wb = &ch343->wb[0], i = 0; i < CH343_NW; i++, wb++) { wb->buf = usb_alloc_coherent(ch343->dev, ch343->writesize, GFP_KERNEL, &wb->dmah); if (!wb->buf) { while (i != 0) { --i; --wb; usb_free_coherent(ch343->dev, ch343->writesize, wb->buf, wb->dmah); } return -ENOMEM; } } return 0; } static int ch343_open(struct inode *inode, struct file *file) { struct ch343 *ch343; struct usb_interface *interface; int subminor; int retval = 0; subminor = iminor(inode); interface = usb_find_interface(&ch343_driver, subminor); if (!interface) { pr_err("%s - error, can't find device for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; } ch343 = usb_get_intfdata(interface); if (!ch343) { retval = -ENODEV; goto exit; } /* save our object in the file's private structure */ file->private_data = ch343; exit: return retval; } static int ch343_release(struct inode *inode, struct file *file) { struct ch343 *ch343; ch343 = file->private_data; if (ch343 == NULL) return -ENODEV; return 0; } static long ch343_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ch343 *ch343; int rv = 0; u8 *buffer; unsigned long arg1, arg2, arg3, arg4, arg5, arg6; u32 __user *argval = (u32 __user *)arg; ch343 = file->private_data; if (ch343 == NULL) return -ENODEV; buffer = kmalloc(512, GFP_KERNEL); if (!buffer) return -ENOMEM; switch (cmd) { case IOCTL_CMD_GETCHIPTYPE: if (put_user(ch343->chiptype, argval)) { rv = -EFAULT; goto out; } break; case IOCTL_CMD_CTRLIN: get_user(arg1, (u8 __user *)arg); get_user(arg2, ((u8 __user *)arg + 1)); get_user(arg3, (u16 __user *)((u8 *)arg + 2)); get_user(arg4, (u16 __user *)((u8 *)arg + 4)); get_user(arg5, (u16 __user *)((u8 *)arg + 6)); arg6 = (unsigned long)((u8 __user *)arg + 8); rv = ch343_control_msg_in(ch343, (u8)arg1, (u8)arg2, (u16)arg3, (u16)arg4, (u8 __user *)arg6, (u16)arg5); break; case IOCTL_CMD_CTRLOUT: get_user(arg1, (u8 __user *)arg); get_user(arg2, ((u8 __user *)arg + 1)); get_user(arg3, (u16 __user *)((u8 *)arg + 2)); get_user(arg4, (u16 __user *)((u8 *)arg + 4)); get_user(arg5, (u16 __user *)((u8 *)arg + 6)); arg6 = (unsigned long)((u8 __user *)arg + 8); rv = ch343_control_msg_out(ch343, (u8)arg1, (u8)arg2, (u16)arg3, (u16)arg4, (u8 __user *)arg6, (u16)arg5); if (rv != (u16)arg5) { rv = -EINVAL; goto out; } break; default: rv = -ENOIOCTLCMD; break; } out: kfree(buffer); return rv; } static const struct file_operations ch343_fops = { .owner = THIS_MODULE, .open = ch343_open, .unlocked_ioctl = ch343_ioctl, .release = ch343_release, }; /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core */ static struct usb_class_driver ch343_class = { .name = "ch343_iodev%d", .fops = &ch343_fops, .minor_base = USB_MINOR_BASE, }; /* * USB probe and disconnect routines. */ static int ch343_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = 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 ch343 *ch343; int minor; int ctrlsize, readsize; u8 *buf; unsigned long quirks; int num_rx_buf = CH343_NR; int i; unsigned int elength = 0; struct device *tty_dev; int rv = -ENOMEM; /* normal quirks */ quirks = (unsigned long)id->driver_info; if (!buffer) { dev_err(&intf->dev, "Weird descriptor references\n"); return -EINVAL; } while (buflen > 0) { elength = buffer[0]; if (!elength) { dev_err(&intf->dev, "skipping garbage byte\n"); elength = 1; goto next_desc; } 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 (elength < sizeof(struct usb_cdc_union_desc)) goto next_desc; 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; default: /* * there are LOTS more CDC descriptors that * could legitimately be found here. */ break; } next_desc: buflen -= elength; buffer += elength; } control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); data_interface = usb_ifnum_to_if(usb_dev, union_header->bSlaveInterface0); if (intf != control_interface) return -ENODEV; if (usb_interface_claimed(data_interface)) { 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; epwrite = &data_interface->cur_altsetting->endpoint[0].desc; epread = &data_interface->cur_altsetting->endpoint[1].desc; /* workaround for switched endpoints */ if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ dev_dbg(&intf->dev, "The data interface has switched endpoints\n"); swap(epread, epwrite); } ch343 = kzalloc(sizeof(struct ch343), GFP_KERNEL); if (ch343 == NULL) goto alloc_fail; ch343->idVendor = id->idVendor; ch343->idProduct = id->idProduct; ch343->iface = control_interface->cur_altsetting->desc.bInterfaceNumber / 2; minor = ch343_alloc_minor(ch343); if (minor < 0) { dev_err(&intf->dev, "no more free ch343 devices\n"); kfree(ch343); return -ENODEV; } ctrlsize = usb_endpoint_maxp(epctrl); readsize = usb_endpoint_maxp(epread); ch343->writesize = usb_endpoint_maxp(epwrite) * 20; ch343->control = control_interface; ch343->data = data_interface; ch343->minor = minor; ch343->dev = usb_dev; ch343->ctrlsize = ctrlsize; ch343->readsize = readsize; ch343->rx_buflimit = num_rx_buf; dev_dbg(&intf->dev, "ep%d ctrl: %d, ep%d read: %d, ep%d write: %d\n", usb_endpoint_num(epctrl), usb_endpoint_maxp(epctrl), usb_endpoint_num(epread), usb_endpoint_maxp(epread), usb_endpoint_num(epwrite), usb_endpoint_maxp(epwrite)); INIT_WORK(&ch343->work, ch343_softint); init_waitqueue_head(&ch343->wioctl); spin_lock_init(&ch343->write_lock); spin_lock_init(&ch343->read_lock); mutex_init(&ch343->mutex); ch343->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); tty_port_init(&ch343->port); ch343->port.ops = &ch343_port_ops; init_usb_anchor(&ch343->delayed); ch343->quirks = quirks; buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &ch343->ctrl_dma); if (!buf) goto alloc_fail2; ch343->ctrl_buffer = buf; if (ch343_write_buffers_alloc(ch343) < 0) goto alloc_fail4; ch343->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); if (!ch343->ctrlurb) goto alloc_fail5; for (i = 0; i < num_rx_buf; i++) { struct ch343_rb *rb = &(ch343->read_buffers[i]); struct urb *urb; rb->base = usb_alloc_coherent(ch343->dev, readsize, GFP_KERNEL, &rb->dma); if (!rb->base) goto alloc_fail6; rb->index = i; rb->instance = ch343; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) goto alloc_fail6; urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; usb_fill_bulk_urb(urb, ch343->dev, ch343->rx_endpoint, rb->base, ch343->readsize, ch343_read_bulk_callback, rb); ch343->read_urbs[i] = urb; __set_bit(i, &ch343->read_urbs_free); } for (i = 0; i < CH343_NW; i++) { struct ch343_wb *snd = &(ch343->wb[i]); snd->urb = usb_alloc_urb(0, GFP_KERNEL); if (snd->urb == NULL) goto alloc_fail7; usb_fill_bulk_urb(snd->urb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), NULL, ch343->writesize, ch343_write_bulk, snd); snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; snd->instance = ch343; } usb_set_intfdata(intf, ch343); usb_fill_int_urb(ch343->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), ch343->ctrl_buffer, ctrlsize, ch343_ctrl_irq, ch343, epctrl->bInterval ? epctrl->bInterval : 16); ch343->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ch343->ctrlurb->transfer_dma = ch343->ctrl_dma; dev_info(&intf->dev, "ttyXRUSB%d: usb to uart device\n", minor); usb_driver_claim_interface(&ch343_driver, data_interface, ch343); usb_set_intfdata(data_interface, ch343); usb_get_intf(control_interface); rv = ch343_configure(ch343); if (rv) goto alloc_fail7; if (ch343->iface == 0) { /* register the device now, as it is ready */ rv = usb_register_dev(intf, &ch343_class); if (rv) { /* error when registering this driver */ dev_err(&intf->dev, "Not able to get a minor for this device.\n"); } else { g_intf = intf; } } tty_dev = tty_port_register_device(&ch343->port, ch343_tty_driver, minor, &control_interface->dev); if (IS_ERR(tty_dev)) { rv = PTR_ERR(tty_dev); goto alloc_fail7; } return 0; alloc_fail7: usb_set_intfdata(intf, NULL); for (i = 0; i < CH343_NW; i++) usb_free_urb(ch343->wb[i].urb); alloc_fail6: for (i = 0; i < num_rx_buf; i++) usb_free_urb(ch343->read_urbs[i]); ch343_read_buffers_free(ch343); usb_free_urb(ch343->ctrlurb); alloc_fail5: ch343_write_buffers_free(ch343); alloc_fail4: usb_free_coherent(usb_dev, ctrlsize, ch343->ctrl_buffer, ch343->ctrl_dma); alloc_fail2: ch343_release_minor(ch343); kfree(ch343); alloc_fail: return rv; } static void stop_data_traffic(struct ch343 *ch343) { int i; usb_kill_urb(ch343->ctrlurb); for (i = 0; i < CH343_NW; i++) usb_kill_urb(ch343->wb[i].urb); for (i = 0; i < ch343->rx_buflimit; i++) usb_kill_urb(ch343->read_urbs[i]); cancel_work_sync(&ch343->work); } static void ch343_disconnect(struct usb_interface *intf) { struct ch343 *ch343 = 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 (!ch343) return; /* give back minor */ if ((ch343->iface == 0) && (g_intf != NULL)) { usb_deregister_dev(g_intf, &ch343_class); } mutex_lock(&ch343->mutex); ch343->disconnected = true; wake_up_all(&ch343->wioctl); usb_set_intfdata(ch343->control, NULL); usb_set_intfdata(ch343->data, NULL); mutex_unlock(&ch343->mutex); tty = tty_port_tty_get(&ch343->port); if (tty) { tty_vhangup(tty); tty_kref_put(tty); } stop_data_traffic(ch343); tty_unregister_device(ch343_tty_driver, ch343->minor); usb_free_urb(ch343->ctrlurb); for (i = 0; i < CH343_NW; i++) usb_free_urb(ch343->wb[i].urb); for (i = 0; i < ch343->rx_buflimit; i++) usb_free_urb(ch343->read_urbs[i]); ch343_write_buffers_free(ch343); usb_free_coherent(usb_dev, ch343->ctrlsize, ch343->ctrl_buffer, ch343->ctrl_dma); ch343_read_buffers_free(ch343); usb_driver_release_interface(&ch343_driver, intf == ch343->control ? ch343->data : ch343->control); tty_port_put(&ch343->port); dev_info(&intf->dev, "%s\n", "ch343 usb device disconnect."); } #ifdef CONFIG_PM static int ch343_suspend(struct usb_interface *intf, pm_message_t message) { struct ch343 *ch343 = usb_get_intfdata(intf); int cnt; dev_dbg(&intf->dev, "%s\n", __func__); spin_lock_irq(&ch343->write_lock); if (PMSG_IS_AUTO(message)) { if (ch343->transmitting) { spin_unlock_irq(&ch343->write_lock); return -EBUSY; } } cnt = ch343->susp_count++; spin_unlock_irq(&ch343->write_lock); if (cnt) return 0; stop_data_traffic(ch343); return 0; } static int ch343_resume(struct usb_interface *intf) { struct ch343 *ch343 = usb_get_intfdata(intf); struct urb *urb; int rv = 0; dev_dbg(&intf->dev, "%s\n", __func__); spin_lock_irq(&ch343->write_lock); if (--ch343->susp_count) goto out; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) if (tty_port_initialized(&ch343->port)) { #else if (test_bit(ASYNCB_INITIALIZED, &ch343->port.flags)) { #endif rv = usb_submit_urb(ch343->ctrlurb, GFP_ATOMIC); for (;;) { urb = usb_get_from_anchor(&ch343->delayed); if (!urb) break; ch343_start_wb(ch343, urb->context); } /* * delayed error checking because we must * do the write path at all cost */ if (rv < 0) goto out; rv = ch343_submit_read_urbs(ch343, GFP_ATOMIC); } out: spin_unlock_irq(&ch343->write_lock); return rv; } static int ch343_reset_resume(struct usb_interface *intf) { struct ch343 *ch343 = usb_get_intfdata(intf); dev_dbg(&intf->dev, "%s\n", __func__); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) if (tty_port_initialized(&ch343->port)) #else if (test_bit(ASYNCB_INITIALIZED, &ch343->port.flags)) #endif tty_port_tty_hangup(&ch343->port, false); return ch343_resume(intf); } #endif /* CONFIG_PM */ /* * USB driver structure. */ static const struct usb_device_id ch343_ids[] = {{USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55D2, /* ch342 chip */ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55D3, /* ch343 chip */ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55D5, /* ch344 chip */ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55DA, /* ch347 chip mode0*/ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55DB, /* ch347 chip mode1*/ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55DD, /* ch347 chip mode3*/ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55D8, /* ch9101 chip */ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55D4, /* ch9102 chip */ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55D7, /* ch9103 chip */ USB_CDC_ACM_PROTO_AT_V25TER)}, {USB_DEVICE_INTERFACE_PROTOCOL(0x1a86, 0x55DF, /* ch9104 chip */ USB_CDC_ACM_PROTO_AT_V25TER)}, {}}; MODULE_DEVICE_TABLE(usb, ch343_ids); static struct usb_driver ch343_driver = { .name = "usb_ch343", .probe = ch343_probe, .disconnect = ch343_disconnect, #ifdef CONFIG_PM .suspend = ch343_suspend, .resume = ch343_resume, .reset_resume = ch343_reset_resume, #endif .id_table = ch343_ids, #ifdef CONFIG_PM .supports_autosuspend = 1, #endif .disable_hub_initiated_lpm = 1, }; /* * TTY driver structures. */ static const struct tty_operations ch343_ops = { .install = ch343_tty_install, .open = ch343_tty_open, .close = ch343_tty_close, .cleanup = ch343_tty_cleanup, .hangup = ch343_tty_hangup, .write = ch343_tty_write, .write_room = ch343_tty_write_room, .ioctl = ch343_tty_ioctl, .chars_in_buffer = ch343_tty_chars_in_buffer, .break_ctl = ch343_tty_break_ctl, .set_termios = ch343_tty_set_termios, .tiocmget = ch343_tty_tiocmget, .tiocmset = ch343_tty_tiocmset, }; /* * Init / exit. */ static int __init ch343_init(void) { int retval; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) ch343_tty_driver = tty_alloc_driver(CH343_TTY_MINORS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); if (IS_ERR(ch343_tty_driver)) return PTR_ERR(ch343_tty_driver); #else ch343_tty_driver = alloc_tty_driver(CH343_TTY_MINORS); if (!ch343_tty_driver) return -ENOMEM; #endif ch343_tty_driver->driver_name = "usbch343", ch343_tty_driver->name = "ttyXRUSB", ch343_tty_driver->major = CH343_TTY_MAJOR, ch343_tty_driver->minor_start = 0, ch343_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, ch343_tty_driver->subtype = SERIAL_TYPE_NORMAL, #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)) ch343_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; #endif ch343_tty_driver->init_termios = tty_std_termios; ch343_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(ch343_tty_driver, &ch343_ops); retval = tty_register_driver(ch343_tty_driver); if (retval) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) tty_driver_kref_put(ch343_tty_driver); #else put_tty_driver(ch343_tty_driver); #endif return retval; } retval = usb_register(&ch343_driver); if (retval) { tty_unregister_driver(ch343_tty_driver); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) tty_driver_kref_put(ch343_tty_driver); #else put_tty_driver(ch343_tty_driver); #endif return retval; } printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n"); printk(KERN_INFO KBUILD_MODNAME ": " VERSION_DESC "\n"); return 0; } static void __exit ch343_exit(void) { usb_deregister(&ch343_driver); tty_unregister_driver(ch343_tty_driver); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)) tty_driver_kref_put(ch343_tty_driver); #else put_tty_driver(ch343_tty_driver); #endif idr_destroy(&ch343_minors); printk(KERN_INFO KBUILD_MODNAME ": " "ch343 driver exit.\n"); } module_init(ch343_init); module_exit(ch343_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(VERSION_DESC); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(CH343_TTY_MAJOR);