linux/drivers/staging/gdm724x/gdm_tty.c
<<
>>
Prefs
   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 = alloc_tty_driver(GDM_TTY_MINOR);
 285                if (!tty_driver)
 286                        return -ENOMEM;
 287
 288                tty_driver->owner = THIS_MODULE;
 289                tty_driver->driver_name = DRIVER_STRING[i];
 290                tty_driver->name = DEVICE_STRING[i];
 291                tty_driver->major = GDM_TTY_MAJOR;
 292                tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 293                tty_driver->subtype = SERIAL_TYPE_NORMAL;
 294                tty_driver->flags = TTY_DRIVER_REAL_RAW |
 295                                        TTY_DRIVER_DYNAMIC_DEV;
 296                tty_driver->init_termios = tty_std_termios;
 297                tty_driver->init_termios.c_cflag = B9600 | CS8 | HUPCL | CLOCAL;
 298                tty_driver->init_termios.c_lflag = ISIG | ICANON | IEXTEN;
 299                tty_set_operations(tty_driver, &gdm_tty_ops);
 300
 301                ret = tty_register_driver(tty_driver);
 302                if (ret) {
 303                        put_tty_driver(tty_driver);
 304                        return ret;
 305                }
 306
 307                gdm_driver[i] = tty_driver;
 308        }
 309
 310        return ret;
 311}
 312
 313void unregister_lte_tty_driver(void)
 314{
 315        struct tty_driver *tty_driver;
 316        int i;
 317
 318        for (i = 0; i < TTY_MAX_COUNT; i++) {
 319                tty_driver = gdm_driver[i];
 320                if (tty_driver) {
 321                        tty_unregister_driver(tty_driver);
 322                        put_tty_driver(tty_driver);
 323                }
 324        }
 325}
 326
 327