gdm_tty.c 7.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 */

J
Joe Perches 已提交
14 15
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb/cdc.h>
#include <linux/serial.h>
#include "gdm_tty.h"

#define GDM_TTY_MAJOR 0
#define GDM_TTY_MINOR 32

#define ACM_CTRL_DTR 0x01
#define ACM_CTRL_RTS 0x02
#define ACM_CTRL_DSR 0x02
#define ACM_CTRL_RI  0x08
#define ACM_CTRL_DCD 0x01

#define WRITE_SIZE 2048

#define MUX_TX_MAX_SIZE 2048

#define gdm_tty_send(n, d, l, i, c, b) (\
	n->tty_dev->send_func(n->tty_dev->priv_dev, d, l, i, c, b))
#define gdm_tty_recv(n, c) (\
	n->tty_dev->recv_func(n->tty_dev->priv_dev, c))
#define gdm_tty_send_control(n, r, v, d, l) (\
	n->tty_dev->send_control(n->tty_dev->priv_dev, r, v, d, l))

47
#define GDM_TTY_READY(gdm) (gdm && gdm->tty_dev && gdm->port.count)
48

49 50
static struct tty_driver *gdm_driver[TTY_MAX_COUNT];
static struct gdm *gdm_table[TTY_MAX_COUNT][GDM_TTY_MINOR];
51
static DEFINE_MUTEX(gdm_table_lock);
52 53 54 55

static char *DRIVER_STRING[TTY_MAX_COUNT] = {"GCTATC", "GCTDM"};
static char *DEVICE_STRING[TTY_MAX_COUNT] = {"GCT-ATC", "GCT-DM"};

56 57 58 59 60 61 62 63 64 65
static void gdm_port_destruct(struct tty_port *port)
{
	struct gdm *gdm = container_of(port, struct gdm, port);

	mutex_lock(&gdm_table_lock);
	gdm_table[gdm->index][gdm->minor] = NULL;
	mutex_unlock(&gdm_table_lock);

	kfree(gdm);
}
66

67
static const struct tty_port_operations gdm_port_ops = {
68
	.destruct = gdm_port_destruct,
69 70
};

71
static int gdm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
72
{
73 74
	struct gdm *gdm = NULL;
	int ret;
75
	int i;
76
	int j;
77

78
	j = GDM_TTY_MINOR;
79 80
	for (i = 0; i < TTY_MAX_COUNT; i++) {
		if (!strcmp(tty->driver->driver_name, DRIVER_STRING[i])) {
81
			j = tty->index;
82 83 84 85
			break;
		}
	}

86
	if (j == GDM_TTY_MINOR)
87 88
		return -ENODEV;

89 90
	mutex_lock(&gdm_table_lock);
	gdm = gdm_table[i][j];
91
	if (!gdm) {
92
		mutex_unlock(&gdm_table_lock);
93
		return -ENODEV;
94
	}
95

96
	tty_port_get(&gdm->port);
97

98 99 100
	ret = tty_standard_install(driver, tty);
	if (ret) {
		tty_port_put(&gdm->port);
101
		mutex_unlock(&gdm_table_lock);
102 103
		return ret;
	}
104

105
	tty->driver_data = gdm;
106
	mutex_unlock(&gdm_table_lock);
107 108

	return 0;
109 110
}

111
static int gdm_tty_open(struct tty_struct *tty, struct file *filp)
112
{
113
	struct gdm *gdm = tty->driver_data;
114

115 116
	return tty_port_open(&gdm->port, tty, filp);
}
117

118 119 120
static void gdm_tty_cleanup(struct tty_struct *tty)
{
	struct gdm *gdm = tty->driver_data;
121

122 123
	tty_port_put(&gdm->port);
}
124

125 126 127
static void gdm_tty_hangup(struct tty_struct *tty)
{
	struct gdm *gdm = tty->driver_data;
128

129 130
	tty_port_hangup(&gdm->port);
}
131

132 133 134
static void gdm_tty_close(struct tty_struct *tty, struct file *filp)
{
	struct gdm *gdm = tty->driver_data;
135

136
	tty_port_close(&gdm->port, tty, filp);
137 138
}

139 140 141 142 143
static int gdm_tty_recv_complete(void *data,
				 int len,
				 int index,
				 struct tty_dev *tty_dev,
				 int complete)
144
{
145
	struct gdm *gdm = tty_dev->gdm[index];
146

147
	if (!GDM_TTY_READY(gdm)) {
148
		if (complete == RECV_PACKET_PROCESS_COMPLETE)
149
			gdm_tty_recv(gdm, gdm_tty_recv_complete);
150 151 152
		return TO_HOST_PORT_CLOSE;
	}

153 154 155 156 157 158 159
	if (data && len) {
		if (tty_buffer_request_room(&gdm->port, len) == len) {
			tty_insert_flip_string(&gdm->port, data, len);
			tty_flip_buffer_push(&gdm->port);
		} else {
			return TO_HOST_BUFFER_REQUEST_FAIL;
		}
160 161 162
	}

	if (complete == RECV_PACKET_PROCESS_COMPLETE)
163
		gdm_tty_recv(gdm, gdm_tty_recv_complete);
164

165
	return 0;
166 167 168 169
}

static void gdm_tty_send_complete(void *arg)
{
170
	struct gdm *gdm = arg;
171

172
	if (!GDM_TTY_READY(gdm))
173 174
		return;

175
	tty_port_tty_wakeup(&gdm->port);
176 177
}

178 179
static int gdm_tty_write(struct tty_struct *tty, const unsigned char *buf,
			 int len)
180
{
181
	struct gdm *gdm = tty->driver_data;
182 183 184 185
	int remain = len;
	int sent_len = 0;
	int sending_len = 0;

186
	if (!GDM_TTY_READY(gdm))
187 188 189 190 191 192
		return -ENODEV;

	if (!len)
		return 0;

	while (1) {
193 194
		sending_len = remain > MUX_TX_MAX_SIZE ? MUX_TX_MAX_SIZE :
							 remain;
195
		gdm_tty_send(gdm,
196
			     (void *)(buf + sent_len),
197
			     sending_len,
198
			     gdm->index,
199
			     gdm_tty_send_complete,
200
			     gdm
201 202 203 204 205 206 207 208 209 210 211 212
			    );
		sent_len += sending_len;
		remain -= sending_len;
		if (remain <= 0)
			break;
	}

	return len;
}

static int gdm_tty_write_room(struct tty_struct *tty)
{
213
	struct gdm *gdm = tty->driver_data;
214

215
	if (!GDM_TTY_READY(gdm))
216 217 218 219 220
		return -ENODEV;

	return WRITE_SIZE;
}

221
int register_lte_tty_device(struct tty_dev *tty_dev, struct device *device)
222
{
223 224 225
	struct gdm *gdm;
	int i;
	int j;
226

227
	for (i = 0; i < TTY_MAX_COUNT; i++) {
228

229
		gdm = kmalloc(sizeof(*gdm), GFP_KERNEL);
230 231
		if (!gdm)
			return -ENOMEM;
232

233
		mutex_lock(&gdm_table_lock);
234
		for (j = 0; j < GDM_TTY_MINOR; j++) {
235
			if (!gdm_table[i][j])
236 237 238 239
				break;
		}

		if (j == GDM_TTY_MINOR) {
240
			kfree(gdm);
241 242
			mutex_unlock(&gdm_table_lock);
			return -EINVAL;
243 244
		}

245 246
		gdm_table[i][j] = gdm;
		mutex_unlock(&gdm_table_lock);
247

248
		tty_dev->gdm[i] = gdm;
249
		tty_port_init(&gdm->port);
250

251 252 253 254
		gdm->port.ops = &gdm_port_ops;
		gdm->index = i;
		gdm->minor = j;
		gdm->tty_dev = tty_dev;
255

256 257
		tty_port_register_device(&gdm->port, gdm_driver[i],
					 gdm->minor, device);
258
	}
259 260

	for (i = 0; i < MAX_ISSUE_NUM; i++)
261
		gdm_tty_recv(gdm, gdm_tty_recv_complete);
262 263 264 265 266 267

	return 0;
}

void unregister_lte_tty_device(struct tty_dev *tty_dev)
{
268 269
	struct gdm *gdm;
	struct tty_struct *tty;
270 271 272
	int i;

	for (i = 0; i < TTY_MAX_COUNT; i++) {
273 274
		gdm = tty_dev->gdm[i];
		if (!gdm)
275 276
			continue;

277
		mutex_lock(&gdm_table_lock);
278
		gdm_table[gdm->index][gdm->minor] = NULL;
279
		mutex_unlock(&gdm_table_lock);
280

281 282 283 284
		tty = tty_port_tty_get(&gdm->port);
		if (tty) {
			tty_vhangup(tty);
			tty_kref_put(tty);
285 286
		}

287
		tty_unregister_device(gdm_driver[i], gdm->minor);
288 289
		tty_port_put(&gdm->port);
	}
290 291 292
}

static const struct tty_operations gdm_tty_ops = {
293 294 295 296 297 298 299
	.install =	gdm_tty_install,
	.open =		gdm_tty_open,
	.close =	gdm_tty_close,
	.cleanup =	gdm_tty_cleanup,
	.hangup =	gdm_tty_hangup,
	.write =	gdm_tty_write,
	.write_room =	gdm_tty_write_room,
300 301 302 303
};

int register_lte_tty_driver(void)
{
304
	struct tty_driver *tty_driver;
305 306 307 308 309
	int i;
	int ret;

	for (i = 0; i < TTY_MAX_COUNT; i++) {
		tty_driver = alloc_tty_driver(GDM_TTY_MINOR);
310
		if (!tty_driver)
311 312 313 314 315 316 317 318
			return -ENOMEM;

		tty_driver->owner = THIS_MODULE;
		tty_driver->driver_name = DRIVER_STRING[i];
		tty_driver->name = DEVICE_STRING[i];
		tty_driver->major = GDM_TTY_MAJOR;
		tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
		tty_driver->subtype = SERIAL_TYPE_NORMAL;
319 320
		tty_driver->flags = TTY_DRIVER_REAL_RAW |
					TTY_DRIVER_DYNAMIC_DEV;
321 322 323 324 325 326
		tty_driver->init_termios = tty_std_termios;
		tty_driver->init_termios.c_cflag = B9600 | CS8 | HUPCL | CLOCAL;
		tty_driver->init_termios.c_lflag = ISIG | ICANON | IEXTEN;
		tty_set_operations(tty_driver, &gdm_tty_ops);

		ret = tty_register_driver(tty_driver);
327 328 329 330
		if (ret) {
			put_tty_driver(tty_driver);
			return ret;
		}
331

332
		gdm_driver[i] = tty_driver;
333 334 335 336 337 338 339 340 341 342 343
	}

	return ret;
}

void unregister_lte_tty_driver(void)
{
	struct tty_driver *tty_driver;
	int i;

	for (i = 0; i < TTY_MAX_COUNT; i++) {
344
		tty_driver = gdm_driver[i];
345 346 347 348 349 350
		if (tty_driver) {
			tty_unregister_driver(tty_driver);
			put_tty_driver(tty_driver);
		}
	}
}
351