blob: 792536c1e77e6b3209216dfcdb67389684380099 [file] [log] [blame]
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +08001/*
2 * I2C bridge driver for the Greybus "generic" I2C module.
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * Released under the GPLv2 only.
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/slab.h>
12#include <linux/tty.h>
13#include <linux/serial.h>
14#include <linux/tty_driver.h>
15#include <linux/tty_flip.h>
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080016#include <linux/idr.h>
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080017#include "greybus.h"
18
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +080019#define GB_TTY_MAJOR 180 /* FIXME use a real number!!! */
20#define GB_NUM_MINORS 255 /* 255 is enough for anyone... */
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080021
22struct gb_tty {
23 struct tty_port port;
24 struct greybus_device *gdev;
25 int cport;
26 unsigned int minor;
27 unsigned char clocal;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +080028 unsigned int throttled:1;
29 unsigned int throttle_req:1;
30 spinlock_t read_lock;
31 spinlock_t write_lock;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +080032 // FIXME locking!!!
33};
34
35static const struct greybus_device_id id_table[] = {
36 { GREYBUS_DEVICE(0x45, 0x45) }, /* make shit up */
37 { }, /* terminating NULL entry */
38};
39
Greg Kroah-Hartmanff45c262014-08-15 18:33:33 +080040static struct tty_driver *gb_tty_driver;
41static DEFINE_IDR(tty_minors);
42static DEFINE_MUTEX(table_lock);
43
44static struct gb_tty *get_gb_by_minor(unsigned minor)
45{
46 struct gb_tty *gb_tty;
47
48 mutex_lock(&table_lock);
49 gb_tty = idr_find(&tty_minors, minor);
50 mutex_unlock(&table_lock);
51 return gb_tty;
52}
53
54static int alloc_minor(struct gb_tty *gb_tty)
55{
56 int minor;
57
58 mutex_lock(&table_lock);
59 minor = idr_alloc(&tty_minors, gb_tty, 0, 0, GFP_KERNEL);
60 if (minor < 0)
61 goto error;
62 gb_tty->minor = minor;
63error:
64 mutex_unlock(&table_lock);
65 return minor;
66}
67
68static void release_minor(struct gb_tty *gb_tty)
69{
70 mutex_lock(&table_lock);
71 idr_remove(&tty_minors, gb_tty->minor);
72 mutex_unlock(&table_lock);
73}
74
75static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty)
76{
77 struct gb_tty *gb_tty;
78 int retval;
79
80 gb_tty = get_gb_by_minor(tty->index);
81 if (!gb_tty)
82 return -ENODEV;
83
84 retval = tty_standard_install(driver, tty);
85 if (retval)
86 goto error;
87
88 tty->driver_data = gb_tty;
89 return 0;
90error:
91 tty_port_put(&gb_tty->port);
92 return retval;
93}
94
95static int gb_tty_open(struct tty_struct *tty, struct file *file)
96{
97 struct gb_tty *gb_tty = tty->driver_data;
98
99 return tty_port_open(&gb_tty->port, tty, file);
100}
101
102static void gb_tty_close(struct tty_struct *tty, struct file *file)
103{
104 struct gb_tty *gb_tty = tty->driver_data;
105
106 tty_port_close(&gb_tty->port, tty, file);
107}
108
109static void gb_tty_cleanup(struct tty_struct *tty)
110{
111 struct gb_tty *gb_tty = tty->driver_data;
112
113 tty_port_put(&gb_tty->port);
114}
115
116static void gb_tty_hangup(struct tty_struct *tty)
117{
118 struct gb_tty *gb_tty = tty->driver_data;
119
120 tty_port_hangup(&gb_tty->port);
121}
122
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800123static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf,
124 int count)
125{
126 struct gb_tty *gb_tty = tty->driver_data;
127
128 // FIXME - actually implement...
129
130 return 0;
131}
132
133static int gb_tty_write_room(struct tty_struct *tty)
134{
135 struct gb_tty *gb_tty = tty->driver_data;
136
137 // FIXME - how much do we want to say we have room for?
138 return 0;
139}
140
141static int gb_tty_chars_in_buffer(struct tty_struct *tty)
142{
143 struct gb_tty *gb_tty = tty->driver_data;
144
145 // FIXME - how many left to send?
146 return 0;
147}
148
149static void gb_tty_throttle(struct tty_struct *tty)
150{
151 struct gb_tty *gb_tty = tty->driver_data;
152
153 spin_lock_irq(&gb_tty->read_lock);
154 gb_tty->throttle_req = 1;
155 spin_unlock_irq(&gb_tty->read_lock);
156}
157
158static void gb_tty_unthrottle(struct tty_struct *tty)
159{
160 struct gb_tty *gb_tty = tty->driver_data;
161 unsigned int was_throttled;
162
163 spin_lock_irq(&gb_tty->read_lock);
164 was_throttled = gb_tty->throttled;
165 gb_tty->throttle_req = 0;
166 gb_tty->throttled = 0;
167 spin_unlock_irq(&gb_tty->read_lock);
168
169 if (was_throttled) {
170 // FIXME - send more data
171 }
172}
173
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800174
175static const struct tty_operations gb_ops = {
176 .install = gb_tty_install,
177 .open = gb_tty_open,
178 .close = gb_tty_close,
179 .cleanup = gb_tty_cleanup,
180 .hangup = gb_tty_hangup,
181 .write = gb_tty_write,
182 .write_room = gb_tty_write_room,
183 .ioctl = gb_tty_ioctl,
184 .throttle = gb_tty_throttle,
185 .unthrottle = gb_tty_unthrottle,
186 .chars_in_buffer = gb_tty_chars_in_buffer,
187 .break_ctl = gb_tty_break_ctl,
188 .set_termios = gb_tty_set_termios,
189 .tiocmget = gb_tty_tiocmget,
190 .tiocmset = gb_tty_tiocmset,
191};
192
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800193
194static int tty_gb_probe(struct greybus_device *gdev, const struct greybus_device_id *id)
195{
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800196 struct gb_tty *gb_tty;
197 struct device *tty_dev;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800198 int retval;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800199 int minor;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800200
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800201 gb_tty = devm_kzalloc(&gdev->dev, sizeof(*gb_tty), GFP_KERNEL);
202 if (!gb_tty)
203 return -ENOMEM;
204
205 minor = alloc_minor(gb_tty);
206 if (minor == GB_NUM_MINORS) {
207 dev_err(&gdev->dev, "no more free minor numbers\n");
208 return -ENODEV;
209 }
210
211 gb_tty->minor = minor;
212 gb_tty->gdev = gdev;
213 spin_lock_init(&gb_tty->write_lock);
214 spin_lock_init(&gb_tty->read_lock);
215
216 /* FIXME - allocate gb buffers */
217
218 greybus_set_drvdata(gdev, gb_tty);
219
220 tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor,
221 &gdev->dev);
222 if (IS_ERR(tty_dev)) {
223 retval = PTR_ERR(tty_dev);
224 goto error;
225 }
226
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800227 return 0;
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800228error:
229 release_minor(gb_tty);
230 return retval;
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800231}
232
233static void tty_gb_disconnect(struct greybus_device *gdev)
234{
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800235 struct gb_tty *gb_tty = greybus_get_drvdata(gdev);
236 struct tty_struct *tty;
237
238 tty = tty_port_tty_get(&gb_tty->port);
239 if (tty) {
240 tty_vhangup(tty);
241 tty_kref_put(tty);
242 }
243 /* FIXME - stop all traffic */
244
245 tty_unregister_device(gb_tty_driver, gb_tty->minor);
246
247 tty_port_put(&gb_tty->port);
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800248}
249
250static struct greybus_driver tty_gb_driver = {
251 .probe = tty_gb_probe,
252 .disconnect = tty_gb_disconnect,
253 .id_table = id_table,
254};
255
256
257static int __init gb_tty_init(void)
258{
259 int retval;
260
Greg Kroah-Hartmana18e1512014-08-15 18:54:11 +0800261 gb_tty_driver = alloc_tty_driver(GB_NUM_MINORS);
Greg Kroah-Hartman79c822b2014-08-15 16:01:23 +0800262 if (!gb_tty_driver)
263 return -ENOMEM;
264
265 gb_tty_driver->driver_name = "gb";
266 gb_tty_driver->name = "ttyGB";
267 gb_tty_driver->major = GB_TTY_MAJOR;
268 gb_tty_driver->minor_start = 0;
269 gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
270 gb_tty_driver->subtype = SERIAL_TYPE_NORMAL;
271 gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
272 gb_tty_driver->init_termios = tty_std_termios;
273 gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
274 tty_set_operations(gb_tty_driver, &gb_ops);
275
276 retval = tty_register_driver(gb_tty_driver);
277 if (retval) {
278 put_tty_driver(gb_tty_driver);
279 return retval;
280 }
281
282 retval = greybus_register(&tty_gb_driver);
283 if (retval) {
284 tty_unregister_driver(gb_tty_driver);
285 put_tty_driver(gb_tty_driver);
286 }
287 return retval;
288}
289
290static void __exit gb_tty_exit(void)
291{
292 greybus_deregister(&tty_gb_driver);
293 tty_unregister_driver(gb_tty_driver);
294 put_tty_driver(gb_tty_driver);
295}
296
297module_init(gb_tty_init);
298module_exit(gb_tty_exit);
299MODULE_LICENSE("GPL");
300MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");