blob: 896d50124a9adfba608ad3c92a08acdbdf00dd45 [file] [log] [blame]
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +08001/*
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +08002 * UART driver for the Greybus "generic" UART module.
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +08003 *
4 * Copyright 2014 Google Inc.
5 *
6 * Released under the GPLv2 only.
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +08007 *
8 * Heavily based on drivers/usb/class/cdc-acm.c and
9 * drivers/usb/serial/usb-serial.c.
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080010 */
11
12#include <linux/kernel.h>
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +080013#include <linux/errno.h>
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080014#include <linux/module.h>
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +080015#include <linux/sched.h>
16#include <linux/wait.h>
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080017#include <linux/slab.h>
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +080018#include <linux/uaccess.h>
19#include <linux/mutex.h>
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080020#include <linux/tty.h>
21#include <linux/serial.h>
22#include <linux/tty_driver.h>
23#include <linux/tty_flip.h>
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +080024#include <linux/serial.h>
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080025#include <linux/idr.h>
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080026#include "greybus.h"
27
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +080028#define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */
29#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080030
31struct gb_tty {
32 struct tty_port port;
33 struct greybus_device *gdev;
34 int cport;
35 unsigned int minor;
36 unsigned char clocal;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +080037 unsigned int throttled:1;
38 unsigned int throttle_req:1;
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +080039 bool disconnected;
40 int writesize; // FIXME - set this somehow.
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +080041 spinlock_t read_lock;
42 spinlock_t write_lock;
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +080043 struct async_icount iocount;
44 struct async_icount oldcount;
45 wait_queue_head_t wioctl;
46 struct mutex mutex;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080047};
48
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070049static const struct greybus_module_id id_table[] = {
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080050 { GREYBUS_DEVICE(0x45, 0x45) }, /* make shit up */
51 { }, /* terminating NULL entry */
52};
53
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080054static struct tty_driver *gb_tty_driver;
55static DEFINE_IDR(tty_minors);
56static DEFINE_MUTEX(table_lock);
57
58static struct gb_tty *get_gb_by_minor(unsigned minor)
59{
60 struct gb_tty *gb_tty;
61
62 mutex_lock(&table_lock);
63 gb_tty = idr_find(&tty_minors, minor);
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +080064 if (gb_tty) {
65 mutex_lock(&gb_tty->mutex);
66 if (gb_tty->disconnected) {
67 mutex_unlock(&gb_tty->mutex);
68 gb_tty = NULL;
69 } else {
70 tty_port_get(&gb_tty->port);
71 mutex_unlock(&gb_tty->mutex);
72 }
73 }
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080074 mutex_unlock(&table_lock);
75 return gb_tty;
76}
77
78static int alloc_minor(struct gb_tty *gb_tty)
79{
80 int minor;
81
82 mutex_lock(&table_lock);
Alex Elderff5f0b32014-08-18 18:25:11 -050083 minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL);
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080084 mutex_unlock(&table_lock);
Alex Elderff5f0b32014-08-18 18:25:11 -050085 if (minor >= 0)
86 gb_tty->minor = minor;
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080087 return minor;
88}
89
90static void release_minor(struct gb_tty *gb_tty)
91{
Alex Elderff5f0b32014-08-18 18:25:11 -050092 int minor = gb_tty->minor;
93
94 gb_tty->minor = 0; /* Maybe should use an invalid value instead */
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080095 mutex_lock(&table_lock);
Alex Elderff5f0b32014-08-18 18:25:11 -050096 idr_remove(&tty_minors, minor);
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080097 mutex_unlock(&table_lock);
98}
99
100static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty)
101{
102 struct gb_tty *gb_tty;
103 int retval;
104
105 gb_tty = get_gb_by_minor(tty->index);
106 if (!gb_tty)
107 return -ENODEV;
108
109 retval = tty_standard_install(driver, tty);
110 if (retval)
111 goto error;
112
113 tty->driver_data = gb_tty;
114 return 0;
115error:
116 tty_port_put(&gb_tty->port);
117 return retval;
118}
119
120static int gb_tty_open(struct tty_struct *tty, struct file *file)
121{
122 struct gb_tty *gb_tty = tty->driver_data;
123
124 return tty_port_open(&gb_tty->port, tty, file);
125}
126
127static void gb_tty_close(struct tty_struct *tty, struct file *file)
128{
129 struct gb_tty *gb_tty = tty->driver_data;
130
131 tty_port_close(&gb_tty->port, tty, file);
132}
133
134static void gb_tty_cleanup(struct tty_struct *tty)
135{
136 struct gb_tty *gb_tty = tty->driver_data;
137
138 tty_port_put(&gb_tty->port);
139}
140
141static void gb_tty_hangup(struct tty_struct *tty)
142{
143 struct gb_tty *gb_tty = tty->driver_data;
144
145 tty_port_hangup(&gb_tty->port);
146}
147
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800148static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf,
149 int count)
150{
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800151// struct gb_tty *gb_tty = tty->driver_data;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800152
153 // FIXME - actually implement...
154
155 return 0;
156}
157
158static int gb_tty_write_room(struct tty_struct *tty)
159{
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800160// struct gb_tty *gb_tty = tty->driver_data;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800161
162 // FIXME - how much do we want to say we have room for?
163 return 0;
164}
165
166static int gb_tty_chars_in_buffer(struct tty_struct *tty)
167{
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800168// struct gb_tty *gb_tty = tty->driver_data;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800169
170 // FIXME - how many left to send?
171 return 0;
172}
173
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800174static int gb_tty_break_ctl(struct tty_struct *tty, int state)
175{
176// struct gb_tty *gb_tty = tty->driver_data;
177
178 // FIXME - send a break, if asked to...
179 return 0;
180}
181
182static void gb_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
183{
184 // FIXME - is this it???
185 tty_termios_copy_hw(&tty->termios, old);
186}
187
188static int gb_tty_tiocmget(struct tty_struct *tty)
189{
190// struct gb_tty *gb_tty = tty->driver_data;
191
192 // FIXME - get some tiocms!
193 return 0;
194}
195
196static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set,
197 unsigned int clear)
198{
199// struct gb_tty *gb_tty = tty->driver_data;
200
201 // FIXME - set some tiocms!
202 return 0;
203}
204
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800205static void gb_tty_throttle(struct tty_struct *tty)
206{
207 struct gb_tty *gb_tty = tty->driver_data;
208
209 spin_lock_irq(&gb_tty->read_lock);
210 gb_tty->throttle_req = 1;
211 spin_unlock_irq(&gb_tty->read_lock);
212}
213
214static void gb_tty_unthrottle(struct tty_struct *tty)
215{
216 struct gb_tty *gb_tty = tty->driver_data;
217 unsigned int was_throttled;
218
219 spin_lock_irq(&gb_tty->read_lock);
220 was_throttled = gb_tty->throttled;
221 gb_tty->throttle_req = 0;
222 gb_tty->throttled = 0;
223 spin_unlock_irq(&gb_tty->read_lock);
224
225 if (was_throttled) {
226 // FIXME - send more data
227 }
228}
229
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800230static int get_serial_info(struct gb_tty *gb_tty,
231 struct serial_struct __user *info)
232{
233 struct serial_struct tmp;
234
235 if (!info)
236 return -EINVAL;
237
238 memset(&tmp, 0, sizeof(tmp));
239 tmp.flags = ASYNC_LOW_LATENCY;
240 tmp.xmit_fifo_size = gb_tty->writesize;
241 tmp.baud_base = 0; // FIXME
242 tmp.close_delay = gb_tty->port.close_delay / 10;
243 tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
244 ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10;
245
246 if (copy_to_user(info, &tmp, sizeof(tmp)))
247 return -EFAULT;
Greg Kroah-Hartman3be03d42014-09-01 19:10:06 -0700248 return 0;
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800249}
250
251static int set_serial_info(struct gb_tty *gb_tty,
252 struct serial_struct __user *newinfo)
253{
254 struct serial_struct new_serial;
255 unsigned int closing_wait;
256 unsigned int close_delay;
257 int retval;
258
259 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
260 return -EFAULT;
261
262 close_delay = new_serial.close_delay * 10;
263 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
264 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
265
266 mutex_lock(&gb_tty->port.mutex);
267 if (!capable(CAP_SYS_ADMIN)) {
268 if ((close_delay != gb_tty->port.close_delay) ||
269 (closing_wait != gb_tty->port.closing_wait))
270 retval = -EPERM;
271 else
272 retval = -EOPNOTSUPP;
273 } else {
274 gb_tty->port.close_delay = close_delay;
275 gb_tty->port.closing_wait = closing_wait;
276 }
277 mutex_unlock(&gb_tty->port.mutex);
278 return retval;
279}
280
281static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg)
282{
283 int retval = 0;
284 DECLARE_WAITQUEUE(wait, current);
285 struct async_icount old;
286 struct async_icount new;
287
Alex Eldercaaa8a82014-08-18 18:25:12 -0500288 if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD)))
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800289 return -EINVAL;
290
291 do {
292 spin_lock_irq(&gb_tty->read_lock);
293 old = gb_tty->oldcount;
294 new = gb_tty->iocount;
295 gb_tty->oldcount = new;
Alex Eldercaaa8a82014-08-18 18:25:12 -0500296 spin_unlock_irq(&gb_tty->read_lock);
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800297
298 if ((arg & TIOCM_DSR) && (old.dsr != new.dsr))
299 break;
300 if ((arg & TIOCM_CD) && (old.dcd != new.dcd))
301 break;
302 if ((arg & TIOCM_RI) && (old.rng != new.rng))
303 break;
304
305 add_wait_queue(&gb_tty->wioctl, &wait);
306 set_current_state(TASK_INTERRUPTIBLE);
307 schedule();
308 remove_wait_queue(&gb_tty->wioctl, &wait);
309 if (gb_tty->disconnected) {
310 if (arg & TIOCM_CD)
311 break;
Alex Eldercaaa8a82014-08-18 18:25:12 -0500312 retval = -ENODEV;
313 } else if (signal_pending(current)) {
314 retval = -ERESTARTSYS;
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800315 }
316 } while (!retval);
317
318 return retval;
319}
320
321static int get_serial_usage(struct gb_tty *gb_tty,
322 struct serial_icounter_struct __user *count)
323{
324 struct serial_icounter_struct icount;
325 int retval = 0;
326
327 memset(&icount, 0, sizeof(icount));
328 icount.dsr = gb_tty->iocount.dsr;
329 icount.rng = gb_tty->iocount.rng;
330 icount.dcd = gb_tty->iocount.dcd;
331 icount.frame = gb_tty->iocount.frame;
332 icount.overrun = gb_tty->iocount.overrun;
333 icount.parity = gb_tty->iocount.parity;
334 icount.brk = gb_tty->iocount.brk;
335
336 if (copy_to_user(count, &icount, sizeof(icount)) > 0)
337 retval = -EFAULT;
338
339 return retval;
340}
341
342static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
343 unsigned long arg)
344{
345 struct gb_tty *gb_tty = tty->driver_data;
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800346
347 switch (cmd) {
348 case TIOCGSERIAL:
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700349 return get_serial_info(gb_tty,
350 (struct serial_struct __user *)arg);
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800351 case TIOCSSERIAL:
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700352 return set_serial_info(gb_tty,
353 (struct serial_struct __user *)arg);
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800354 case TIOCMIWAIT:
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700355 return wait_serial_change(gb_tty, arg);
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800356 case TIOCGICOUNT:
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700357 return get_serial_usage(gb_tty,
358 (struct serial_icounter_struct __user *)arg);
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800359 }
360
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700361 return -ENOIOCTLCMD;
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800362}
363
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800364
365static const struct tty_operations gb_ops = {
366 .install = gb_tty_install,
367 .open = gb_tty_open,
368 .close = gb_tty_close,
369 .cleanup = gb_tty_cleanup,
370 .hangup = gb_tty_hangup,
371 .write = gb_tty_write,
372 .write_room = gb_tty_write_room,
373 .ioctl = gb_tty_ioctl,
374 .throttle = gb_tty_throttle,
375 .unthrottle = gb_tty_unthrottle,
376 .chars_in_buffer = gb_tty_chars_in_buffer,
377 .break_ctl = gb_tty_break_ctl,
378 .set_termios = gb_tty_set_termios,
379 .tiocmget = gb_tty_tiocmget,
380 .tiocmset = gb_tty_tiocmset,
381};
382
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800383
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700384int gb_tty_probe(struct greybus_device *gdev,
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -0700385 const struct greybus_module_id *id)
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800386{
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800387 struct gb_tty *gb_tty;
388 struct device *tty_dev;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800389 int retval;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800390 int minor;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800391
Greg Kroah-Hartman8bf23e82014-08-30 17:18:04 -0700392 gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL);
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800393 if (!gb_tty)
394 return -ENOMEM;
395
396 minor = alloc_minor(gb_tty);
Alex Elderff5f0b32014-08-18 18:25:11 -0500397 if (minor < 0) {
398 if (minor == -ENOSPC) {
399 dev_err(&gdev->dev, "no more free minor numbers\n");
400 return -ENODEV;
401 }
402 return minor;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800403 }
404
405 gb_tty->minor = minor;
406 gb_tty->gdev = gdev;
407 spin_lock_init(&gb_tty->write_lock);
408 spin_lock_init(&gb_tty->read_lock);
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800409 init_waitqueue_head(&gb_tty->wioctl);
410 mutex_init(&gb_tty->mutex);
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800411
412 /* FIXME - allocate gb buffers */
413
Greg Kroah-Hartmaneca17c52014-08-30 16:54:05 -0700414 gdev->gb_tty = gb_tty;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800415
416 tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor,
417 &gdev->dev);
418 if (IS_ERR(tty_dev)) {
419 retval = PTR_ERR(tty_dev);
420 goto error;
421 }
422
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800423 return 0;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800424error:
Greg Kroah-Hartmaneca17c52014-08-30 16:54:05 -0700425 gdev->gb_tty = NULL;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800426 release_minor(gb_tty);
427 return retval;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800428}
429
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700430void gb_tty_disconnect(struct greybus_device *gdev)
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800431{
Greg Kroah-Hartmaneca17c52014-08-30 16:54:05 -0700432 struct gb_tty *gb_tty = gdev->gb_tty;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800433 struct tty_struct *tty;
434
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800435 if (!gb_tty)
436 return;
437
438 mutex_lock(&gb_tty->mutex);
439 gb_tty->disconnected = true;
440
441 wake_up_all(&gb_tty->wioctl);
Greg Kroah-Hartmaneca17c52014-08-30 16:54:05 -0700442 gdev->gb_tty = NULL;
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800443 mutex_unlock(&gb_tty->mutex);
444
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800445 tty = tty_port_tty_get(&gb_tty->port);
446 if (tty) {
447 tty_vhangup(tty);
448 tty_kref_put(tty);
449 }
450 /* FIXME - stop all traffic */
451
452 tty_unregister_device(gb_tty_driver, gb_tty->minor);
453
Greg Kroah-Hartmane68453e2014-08-15 19:44:32 +0800454 /* FIXME - free transmit / recieve buffers */
455
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800456 tty_port_put(&gb_tty->port);
Greg Kroah-Hartmane5f167f2014-08-30 17:11:41 -0700457
458 kfree(gb_tty);
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800459}
460
461static struct greybus_driver tty_gb_driver = {
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700462 .probe = gb_tty_probe,
463 .disconnect = gb_tty_disconnect,
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800464 .id_table = id_table,
465};
466
467
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700468int __init gb_tty_init(void)
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800469{
470 int retval;
471
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800472 gb_tty_driver = alloc_tty_driver(GB_NUM_MINORS);
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800473 if (!gb_tty_driver)
474 return -ENOMEM;
475
476 gb_tty_driver->driver_name = "gb";
477 gb_tty_driver->name = "ttyGB";
478 gb_tty_driver->major = GB_TTY_MAJOR;
479 gb_tty_driver->minor_start = 0;
480 gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
481 gb_tty_driver->subtype = SERIAL_TYPE_NORMAL;
482 gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
483 gb_tty_driver->init_termios = tty_std_termios;
484 gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
485 tty_set_operations(gb_tty_driver, &gb_ops);
486
487 retval = tty_register_driver(gb_tty_driver);
488 if (retval) {
489 put_tty_driver(gb_tty_driver);
490 return retval;
491 }
492
493 retval = greybus_register(&tty_gb_driver);
494 if (retval) {
495 tty_unregister_driver(gb_tty_driver);
496 put_tty_driver(gb_tty_driver);
497 }
498 return retval;
499}
500
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700501void __exit gb_tty_exit(void)
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800502{
503 greybus_deregister(&tty_gb_driver);
504 tty_unregister_driver(gb_tty_driver);
505 put_tty_driver(gb_tty_driver);
506}
507
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700508#if 0
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800509module_init(gb_tty_init);
510module_exit(gb_tty_exit);
511MODULE_LICENSE("GPL");
512MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700513#endif