gdm_tty.c (6860B)
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */ 3 4#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 5 6#include <linux/kernel.h> 7#include <linux/errno.h> 8#include <linux/tty.h> 9#include <linux/tty_driver.h> 10#include <linux/tty_flip.h> 11#include <linux/module.h> 12#include <linux/slab.h> 13#include <linux/usb/cdc.h> 14#include <linux/serial.h> 15#include "gdm_tty.h" 16 17#define GDM_TTY_MAJOR 0 18#define GDM_TTY_MINOR 32 19 20#define ACM_CTRL_DTR 0x01 21#define ACM_CTRL_RTS 0x02 22#define ACM_CTRL_DSR 0x02 23#define ACM_CTRL_RI 0x08 24#define ACM_CTRL_DCD 0x01 25 26#define WRITE_SIZE 2048 27 28#define MUX_TX_MAX_SIZE 2048 29 30#define GDM_TTY_READY(gdm) (gdm && gdm->tty_dev && gdm->port.count) 31 32static struct tty_driver *gdm_driver[TTY_MAX_COUNT]; 33static struct gdm *gdm_table[TTY_MAX_COUNT][GDM_TTY_MINOR]; 34static DEFINE_MUTEX(gdm_table_lock); 35 36static const char *DRIVER_STRING[TTY_MAX_COUNT] = {"GCTATC", "GCTDM"}; 37static char *DEVICE_STRING[TTY_MAX_COUNT] = {"GCT-ATC", "GCT-DM"}; 38 39static void gdm_port_destruct(struct tty_port *port) 40{ 41 struct gdm *gdm = container_of(port, struct gdm, port); 42 43 mutex_lock(&gdm_table_lock); 44 gdm_table[gdm->index][gdm->minor] = NULL; 45 mutex_unlock(&gdm_table_lock); 46 47 kfree(gdm); 48} 49 50static const struct tty_port_operations gdm_port_ops = { 51 .destruct = gdm_port_destruct, 52}; 53 54static int gdm_tty_install(struct tty_driver *driver, struct tty_struct *tty) 55{ 56 struct gdm *gdm = NULL; 57 int ret; 58 59 ret = match_string(DRIVER_STRING, TTY_MAX_COUNT, 60 tty->driver->driver_name); 61 if (ret < 0) 62 return -ENODEV; 63 64 mutex_lock(&gdm_table_lock); 65 gdm = gdm_table[ret][tty->index]; 66 if (!gdm) { 67 mutex_unlock(&gdm_table_lock); 68 return -ENODEV; 69 } 70 71 tty_port_get(&gdm->port); 72 73 ret = tty_standard_install(driver, tty); 74 if (ret) { 75 tty_port_put(&gdm->port); 76 mutex_unlock(&gdm_table_lock); 77 return ret; 78 } 79 80 tty->driver_data = gdm; 81 mutex_unlock(&gdm_table_lock); 82 83 return 0; 84} 85 86static int gdm_tty_open(struct tty_struct *tty, struct file *filp) 87{ 88 struct gdm *gdm = tty->driver_data; 89 90 return tty_port_open(&gdm->port, tty, filp); 91} 92 93static void gdm_tty_cleanup(struct tty_struct *tty) 94{ 95 struct gdm *gdm = tty->driver_data; 96 97 tty_port_put(&gdm->port); 98} 99 100static void gdm_tty_hangup(struct tty_struct *tty) 101{ 102 struct gdm *gdm = tty->driver_data; 103 104 tty_port_hangup(&gdm->port); 105} 106 107static void gdm_tty_close(struct tty_struct *tty, struct file *filp) 108{ 109 struct gdm *gdm = tty->driver_data; 110 111 tty_port_close(&gdm->port, tty, filp); 112} 113 114static int gdm_tty_recv_complete(void *data, 115 int len, 116 int index, 117 struct tty_dev *tty_dev, 118 int complete) 119{ 120 struct gdm *gdm = tty_dev->gdm[index]; 121 122 if (!GDM_TTY_READY(gdm)) { 123 if (complete == RECV_PACKET_PROCESS_COMPLETE) 124 gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev, 125 gdm_tty_recv_complete); 126 return TO_HOST_PORT_CLOSE; 127 } 128 129 if (data && len) { 130 if (tty_buffer_request_room(&gdm->port, len) == len) { 131 tty_insert_flip_string(&gdm->port, data, len); 132 tty_flip_buffer_push(&gdm->port); 133 } else { 134 return TO_HOST_BUFFER_REQUEST_FAIL; 135 } 136 } 137 138 if (complete == RECV_PACKET_PROCESS_COMPLETE) 139 gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev, 140 gdm_tty_recv_complete); 141 142 return 0; 143} 144 145static void gdm_tty_send_complete(void *arg) 146{ 147 struct gdm *gdm = arg; 148 149 if (!GDM_TTY_READY(gdm)) 150 return; 151 152 tty_port_tty_wakeup(&gdm->port); 153} 154 155static int gdm_tty_write(struct tty_struct *tty, const unsigned char *buf, 156 int len) 157{ 158 struct gdm *gdm = tty->driver_data; 159 int remain = len; 160 int sent_len = 0; 161 int sending_len = 0; 162 163 if (!GDM_TTY_READY(gdm)) 164 return -ENODEV; 165 166 if (!len) 167 return 0; 168 169 while (1) { 170 sending_len = min(MUX_TX_MAX_SIZE, remain); 171 gdm->tty_dev->send_func(gdm->tty_dev->priv_dev, 172 (void *)(buf + sent_len), 173 sending_len, 174 gdm->index, 175 gdm_tty_send_complete, 176 gdm); 177 sent_len += sending_len; 178 remain -= sending_len; 179 if (remain <= 0) 180 break; 181 } 182 183 return len; 184} 185 186static unsigned int gdm_tty_write_room(struct tty_struct *tty) 187{ 188 struct gdm *gdm = tty->driver_data; 189 190 if (!GDM_TTY_READY(gdm)) 191 return 0; 192 193 return WRITE_SIZE; 194} 195 196int register_lte_tty_device(struct tty_dev *tty_dev, struct device *device) 197{ 198 struct gdm *gdm; 199 int i; 200 int j; 201 202 for (i = 0; i < TTY_MAX_COUNT; i++) { 203 gdm = kmalloc(sizeof(*gdm), GFP_KERNEL); 204 if (!gdm) 205 return -ENOMEM; 206 207 mutex_lock(&gdm_table_lock); 208 for (j = 0; j < GDM_TTY_MINOR; j++) { 209 if (!gdm_table[i][j]) 210 break; 211 } 212 213 if (j == GDM_TTY_MINOR) { 214 kfree(gdm); 215 mutex_unlock(&gdm_table_lock); 216 return -EINVAL; 217 } 218 219 gdm_table[i][j] = gdm; 220 mutex_unlock(&gdm_table_lock); 221 222 tty_dev->gdm[i] = gdm; 223 tty_port_init(&gdm->port); 224 225 gdm->port.ops = &gdm_port_ops; 226 gdm->index = i; 227 gdm->minor = j; 228 gdm->tty_dev = tty_dev; 229 230 tty_port_register_device(&gdm->port, gdm_driver[i], 231 gdm->minor, device); 232 } 233 234 for (i = 0; i < MAX_ISSUE_NUM; i++) 235 gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev, 236 gdm_tty_recv_complete); 237 238 return 0; 239} 240 241void unregister_lte_tty_device(struct tty_dev *tty_dev) 242{ 243 struct gdm *gdm; 244 struct tty_struct *tty; 245 int i; 246 247 for (i = 0; i < TTY_MAX_COUNT; i++) { 248 gdm = tty_dev->gdm[i]; 249 if (!gdm) 250 continue; 251 252 mutex_lock(&gdm_table_lock); 253 gdm_table[gdm->index][gdm->minor] = NULL; 254 mutex_unlock(&gdm_table_lock); 255 256 tty = tty_port_tty_get(&gdm->port); 257 if (tty) { 258 tty_vhangup(tty); 259 tty_kref_put(tty); 260 } 261 262 tty_unregister_device(gdm_driver[i], gdm->minor); 263 tty_port_put(&gdm->port); 264 } 265} 266 267static const struct tty_operations gdm_tty_ops = { 268 .install = gdm_tty_install, 269 .open = gdm_tty_open, 270 .close = gdm_tty_close, 271 .cleanup = gdm_tty_cleanup, 272 .hangup = gdm_tty_hangup, 273 .write = gdm_tty_write, 274 .write_room = gdm_tty_write_room, 275}; 276 277int register_lte_tty_driver(void) 278{ 279 struct tty_driver *tty_driver; 280 int i; 281 int ret; 282 283 for (i = 0; i < TTY_MAX_COUNT; i++) { 284 tty_driver = tty_alloc_driver(GDM_TTY_MINOR, 285 TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); 286 if (IS_ERR(tty_driver)) 287 return PTR_ERR(tty_driver); 288 289 tty_driver->owner = THIS_MODULE; 290 tty_driver->driver_name = DRIVER_STRING[i]; 291 tty_driver->name = DEVICE_STRING[i]; 292 tty_driver->major = GDM_TTY_MAJOR; 293 tty_driver->type = TTY_DRIVER_TYPE_SERIAL; 294 tty_driver->subtype = SERIAL_TYPE_NORMAL; 295 tty_driver->init_termios = tty_std_termios; 296 tty_driver->init_termios.c_cflag = B9600 | CS8 | HUPCL | CLOCAL; 297 tty_driver->init_termios.c_lflag = ISIG | ICANON | IEXTEN; 298 tty_set_operations(tty_driver, &gdm_tty_ops); 299 300 ret = tty_register_driver(tty_driver); 301 if (ret) { 302 tty_driver_kref_put(tty_driver); 303 return ret; 304 } 305 306 gdm_driver[i] = tty_driver; 307 } 308 309 return ret; 310} 311 312void unregister_lte_tty_driver(void) 313{ 314 struct tty_driver *tty_driver; 315 int i; 316 317 for (i = 0; i < TTY_MAX_COUNT; i++) { 318 tty_driver = gdm_driver[i]; 319 if (tty_driver) { 320 tty_unregister_driver(tty_driver); 321 tty_driver_kref_put(tty_driver); 322 } 323 } 324} 325