]>
Commit | Line | Data |
---|---|---|
79c822be | 1 | /* |
e68453ed | 2 | * UART driver for the Greybus "generic" UART module. |
79c822be GKH |
3 | * |
4 | * Copyright 2014 Google Inc. | |
a46e9671 | 5 | * Copyright 2014 Linaro Ltd. |
79c822be GKH |
6 | * |
7 | * Released under the GPLv2 only. | |
e68453ed GKH |
8 | * |
9 | * Heavily based on drivers/usb/class/cdc-acm.c and | |
10 | * drivers/usb/serial/usb-serial.c. | |
79c822be | 11 | */ |
168db1cd | 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
79c822be GKH |
13 | |
14 | #include <linux/kernel.h> | |
e68453ed | 15 | #include <linux/errno.h> |
79c822be | 16 | #include <linux/module.h> |
174cd4b1 | 17 | #include <linux/sched/signal.h> |
e68453ed | 18 | #include <linux/wait.h> |
79c822be | 19 | #include <linux/slab.h> |
e68453ed GKH |
20 | #include <linux/uaccess.h> |
21 | #include <linux/mutex.h> | |
79c822be GKH |
22 | #include <linux/tty.h> |
23 | #include <linux/serial.h> | |
24 | #include <linux/tty_driver.h> | |
25 | #include <linux/tty_flip.h> | |
e68453ed | 26 | #include <linux/serial.h> |
ff45c265 | 27 | #include <linux/idr.h> |
7fabc884 MB |
28 | #include <linux/fs.h> |
29 | #include <linux/kdev_t.h> | |
8d6fbe9b AH |
30 | #include <linux/kfifo.h> |
31 | #include <linux/workqueue.h> | |
5dad5c31 | 32 | #include <linux/completion.h> |
e1e9dbdd | 33 | |
79c822be | 34 | #include "greybus.h" |
e54b106d | 35 | #include "gbphy.h" |
79c822be | 36 | |
f5537d46 | 37 | #define GB_NUM_MINORS 16 /* 16 is is more than enough */ |
7fabc884 | 38 | #define GB_NAME "ttyGB" |
79c822be | 39 | |
8d6fbe9b AH |
40 | #define GB_UART_WRITE_FIFO_SIZE PAGE_SIZE |
41 | #define GB_UART_WRITE_ROOM_MARGIN 1 /* leave some space in fifo */ | |
a8bc00fb | 42 | #define GB_UART_FIRMWARE_CREDITS 4096 |
5dad5c31 | 43 | #define GB_UART_CREDIT_WAIT_TIMEOUT_MSEC 10000 |
8d6fbe9b | 44 | |
11fca140 BD |
45 | struct gb_tty_line_coding { |
46 | __le32 rate; | |
47 | __u8 format; | |
48 | __u8 parity; | |
49 | __u8 data_bits; | |
e55c2520 | 50 | __u8 flow_control; |
11fca140 BD |
51 | }; |
52 | ||
79c822be | 53 | struct gb_tty { |
e54b106d | 54 | struct gbphy_device *gbphy_dev; |
79c822be | 55 | struct tty_port port; |
dd1c64ed | 56 | void *buffer; |
7f29aded | 57 | size_t buffer_payload_max; |
aed0bc6e | 58 | struct gb_connection *connection; |
1cfc667d | 59 | u16 cport_id; |
79c822be GKH |
60 | unsigned int minor; |
61 | unsigned char clocal; | |
e68453ed | 62 | bool disconnected; |
a18e1517 GKH |
63 | spinlock_t read_lock; |
64 | spinlock_t write_lock; | |
e68453ed GKH |
65 | struct async_icount iocount; |
66 | struct async_icount oldcount; | |
67 | wait_queue_head_t wioctl; | |
68 | struct mutex mutex; | |
ba4b099c BD |
69 | u8 ctrlin; /* input control lines */ |
70 | u8 ctrlout; /* output control lines */ | |
11fca140 | 71 | struct gb_tty_line_coding line_coding; |
8d6fbe9b AH |
72 | struct work_struct tx_work; |
73 | struct kfifo write_fifo; | |
74 | bool close_pending; | |
a8bc00fb | 75 | unsigned int credits; |
5dad5c31 | 76 | struct completion credits_complete; |
79c822be GKH |
77 | }; |
78 | ||
ff45c265 GKH |
79 | static struct tty_driver *gb_tty_driver; |
80 | static DEFINE_IDR(tty_minors); | |
81 | static DEFINE_MUTEX(table_lock); | |
4d980989 | 82 | |
c5f338c4 | 83 | static int gb_uart_receive_data_handler(struct gb_operation *op) |
4c025cf4 | 84 | { |
c5f338c4 | 85 | struct gb_connection *connection = op->connection; |
0ec30632 | 86 | struct gb_tty *gb_tty = gb_connection_get_data(connection); |
4c025cf4 | 87 | struct tty_port *port = &gb_tty->port; |
c5f338c4 JH |
88 | struct gb_message *request = op->request; |
89 | struct gb_uart_recv_data_request *receive_data; | |
4c025cf4 BD |
90 | u16 recv_data_size; |
91 | int count; | |
92 | unsigned long tty_flags = TTY_NORMAL; | |
93 | ||
c5f338c4 | 94 | if (request->payload_size < sizeof(*receive_data)) { |
e54b106d | 95 | dev_err(&gb_tty->gbphy_dev->dev, |
319b78c3 CL |
96 | "short receive-data request received (%zu < %zu)\n", |
97 | request->payload_size, sizeof(*receive_data)); | |
c5f338c4 JH |
98 | return -EINVAL; |
99 | } | |
100 | ||
101 | receive_data = op->request->payload; | |
4c025cf4 | 102 | recv_data_size = le16_to_cpu(receive_data->size); |
c5f338c4 JH |
103 | |
104 | if (recv_data_size != request->payload_size - sizeof(*receive_data)) { | |
e54b106d | 105 | dev_err(&gb_tty->gbphy_dev->dev, |
319b78c3 CL |
106 | "malformed receive-data request received (%u != %zu)\n", |
107 | recv_data_size, | |
108 | request->payload_size - sizeof(*receive_data)); | |
c5f338c4 JH |
109 | return -EINVAL; |
110 | } | |
111 | ||
112 | if (!recv_data_size) | |
4c025cf4 BD |
113 | return -EINVAL; |
114 | ||
115 | if (receive_data->flags) { | |
116 | if (receive_data->flags & GB_UART_RECV_FLAG_BREAK) | |
117 | tty_flags = TTY_BREAK; | |
118 | else if (receive_data->flags & GB_UART_RECV_FLAG_PARITY) | |
119 | tty_flags = TTY_PARITY; | |
120 | else if (receive_data->flags & GB_UART_RECV_FLAG_FRAMING) | |
121 | tty_flags = TTY_FRAME; | |
122 | ||
123 | /* overrun is special, not associated with a char */ | |
124 | if (receive_data->flags & GB_UART_RECV_FLAG_OVERRUN) | |
125 | tty_insert_flip_char(port, 0, TTY_OVERRUN); | |
126 | } | |
127 | count = tty_insert_flip_string_fixed_flag(port, receive_data->data, | |
128 | tty_flags, recv_data_size); | |
129 | if (count != recv_data_size) { | |
e54b106d | 130 | dev_err(&gb_tty->gbphy_dev->dev, |
4c025cf4 BD |
131 | "UART: RX 0x%08x bytes only wrote 0x%08x\n", |
132 | recv_data_size, count); | |
133 | } | |
134 | if (count) | |
135 | tty_flip_buffer_push(port); | |
136 | return 0; | |
137 | } | |
138 | ||
cb7f00ba | 139 | static int gb_uart_serial_state_handler(struct gb_operation *op) |
1c087015 BD |
140 | { |
141 | struct gb_connection *connection = op->connection; | |
0ec30632 | 142 | struct gb_tty *gb_tty = gb_connection_get_data(connection); |
1c087015 | 143 | struct gb_message *request = op->request; |
1c087015 | 144 | struct gb_uart_serial_state_request *serial_state; |
cb7f00ba JH |
145 | |
146 | if (request->payload_size < sizeof(*serial_state)) { | |
e54b106d | 147 | dev_err(&gb_tty->gbphy_dev->dev, |
319b78c3 CL |
148 | "short serial-state event received (%zu < %zu)\n", |
149 | request->payload_size, sizeof(*serial_state)); | |
cb7f00ba JH |
150 | return -EINVAL; |
151 | } | |
152 | ||
153 | serial_state = request->payload; | |
154 | gb_tty->ctrlin = serial_state->control; | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
a8bc00fb AH |
159 | static int gb_uart_receive_credits_handler(struct gb_operation *op) |
160 | { | |
161 | struct gb_connection *connection = op->connection; | |
162 | struct gb_tty *gb_tty = gb_connection_get_data(connection); | |
163 | struct gb_message *request = op->request; | |
164 | struct gb_uart_receive_credits_request *credit_request; | |
165 | unsigned long flags; | |
166 | unsigned int incoming_credits; | |
167 | int ret = 0; | |
168 | ||
169 | if (request->payload_size < sizeof(*credit_request)) { | |
170 | dev_err(&gb_tty->gbphy_dev->dev, | |
319b78c3 CL |
171 | "short receive_credits event received (%zu < %zu)\n", |
172 | request->payload_size, | |
173 | sizeof(*credit_request)); | |
a8bc00fb AH |
174 | return -EINVAL; |
175 | } | |
176 | ||
177 | credit_request = request->payload; | |
178 | incoming_credits = le16_to_cpu(credit_request->count); | |
179 | ||
180 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
181 | gb_tty->credits += incoming_credits; | |
182 | if (gb_tty->credits > GB_UART_FIRMWARE_CREDITS) { | |
183 | gb_tty->credits -= incoming_credits; | |
184 | ret = -EINVAL; | |
185 | } | |
186 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
187 | ||
188 | if (ret) { | |
189 | dev_err(&gb_tty->gbphy_dev->dev, | |
190 | "invalid number of incoming credits: %d\n", | |
191 | incoming_credits); | |
192 | return ret; | |
193 | } | |
194 | ||
195 | if (!gb_tty->close_pending) | |
196 | schedule_work(&gb_tty->tx_work); | |
197 | ||
198 | /* | |
199 | * the port the tty layer may be waiting for credits | |
200 | */ | |
201 | tty_port_tty_wakeup(&gb_tty->port); | |
202 | ||
5dad5c31 AH |
203 | if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) |
204 | complete(&gb_tty->credits_complete); | |
205 | ||
a8bc00fb AH |
206 | return ret; |
207 | } | |
208 | ||
7dbe1f49 | 209 | static int gb_uart_request_handler(struct gb_operation *op) |
cb7f00ba JH |
210 | { |
211 | struct gb_connection *connection = op->connection; | |
7dbe1f49 GKH |
212 | struct gb_tty *gb_tty = gb_connection_get_data(connection); |
213 | int type = op->type; | |
cb7f00ba | 214 | int ret; |
1c087015 BD |
215 | |
216 | switch (type) { | |
217 | case GB_UART_TYPE_RECEIVE_DATA: | |
c5f338c4 | 218 | ret = gb_uart_receive_data_handler(op); |
1c087015 BD |
219 | break; |
220 | case GB_UART_TYPE_SERIAL_STATE: | |
cb7f00ba | 221 | ret = gb_uart_serial_state_handler(op); |
1c087015 | 222 | break; |
a8bc00fb AH |
223 | case GB_UART_TYPE_RECEIVE_CREDITS: |
224 | ret = gb_uart_receive_credits_handler(op); | |
225 | break; | |
1c087015 | 226 | default: |
e54b106d | 227 | dev_err(&gb_tty->gbphy_dev->dev, |
b933fa4a | 228 | "unsupported unsolicited request: 0x%02x\n", type); |
1c087015 BD |
229 | ret = -EINVAL; |
230 | } | |
231 | ||
232 | return ret; | |
233 | } | |
234 | ||
8d6fbe9b | 235 | static void gb_uart_tx_write_work(struct work_struct *work) |
b4be4043 | 236 | { |
b4be4043 | 237 | struct gb_uart_send_data_request *request; |
8d6fbe9b AH |
238 | struct gb_tty *gb_tty; |
239 | unsigned long flags; | |
240 | unsigned int send_size; | |
563bd79b | 241 | int ret; |
b4be4043 | 242 | |
8d6fbe9b AH |
243 | gb_tty = container_of(work, struct gb_tty, tx_work); |
244 | request = gb_tty->buffer; | |
b4be4043 | 245 | |
8d6fbe9b AH |
246 | while (1) { |
247 | if (gb_tty->close_pending) | |
248 | break; | |
249 | ||
250 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
a8bc00fb AH |
251 | send_size = gb_tty->buffer_payload_max; |
252 | if (send_size > gb_tty->credits) | |
253 | send_size = gb_tty->credits; | |
254 | ||
8d6fbe9b | 255 | send_size = kfifo_out_peek(&gb_tty->write_fifo, |
319b78c3 CL |
256 | &request->data[0], |
257 | send_size); | |
8d6fbe9b AH |
258 | if (!send_size) { |
259 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
260 | break; | |
261 | } | |
262 | ||
a8bc00fb | 263 | gb_tty->credits -= send_size; |
8d6fbe9b AH |
264 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); |
265 | ||
266 | request->size = cpu_to_le16(send_size); | |
267 | ret = gb_operation_sync(gb_tty->connection, | |
268 | GB_UART_TYPE_SEND_DATA, | |
269 | request, sizeof(*request) + send_size, | |
270 | NULL, 0); | |
271 | if (ret) { | |
272 | dev_err(&gb_tty->gbphy_dev->dev, | |
273 | "send data error: %d\n", ret); | |
a8bc00fb AH |
274 | spin_lock_irqsave(&gb_tty->write_lock, flags); |
275 | gb_tty->credits += send_size; | |
276 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
8d6fbe9b AH |
277 | if (!gb_tty->close_pending) |
278 | schedule_work(work); | |
279 | return; | |
280 | } | |
281 | ||
282 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
283 | ret = kfifo_out(&gb_tty->write_fifo, &request->data[0], | |
284 | send_size); | |
285 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
286 | ||
287 | tty_port_tty_wakeup(&gb_tty->port); | |
288 | } | |
b4be4043 GKH |
289 | } |
290 | ||
9f240f20 | 291 | static int send_line_coding(struct gb_tty *tty) |
b4be4043 | 292 | { |
e51f1d1a | 293 | struct gb_uart_set_line_coding_request request; |
b4be4043 | 294 | |
11fca140 | 295 | memcpy(&request, &tty->line_coding, |
9f240f20 | 296 | sizeof(tty->line_coding)); |
530430b7 | 297 | return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_LINE_CODING, |
e51f1d1a | 298 | &request, sizeof(request), NULL, 0); |
b4be4043 GKH |
299 | } |
300 | ||
ba4b099c | 301 | static int send_control(struct gb_tty *gb_tty, u8 control) |
b4be4043 | 302 | { |
e51f1d1a | 303 | struct gb_uart_set_control_line_state_request request; |
b4be4043 | 304 | |
ba4b099c | 305 | request.control = control; |
62229a1b | 306 | return gb_operation_sync(gb_tty->connection, |
530430b7 | 307 | GB_UART_TYPE_SET_CONTROL_LINE_STATE, |
e51f1d1a | 308 | &request, sizeof(request), NULL, 0); |
b4be4043 GKH |
309 | } |
310 | ||
62229a1b | 311 | static int send_break(struct gb_tty *gb_tty, u8 state) |
b4be4043 | 312 | { |
e51f1d1a | 313 | struct gb_uart_set_break_request request; |
b4be4043 GKH |
314 | |
315 | if ((state != 0) && (state != 1)) { | |
e54b106d | 316 | dev_err(&gb_tty->gbphy_dev->dev, |
e51f1d1a | 317 | "invalid break state of %d\n", state); |
b4be4043 GKH |
318 | return -EINVAL; |
319 | } | |
320 | ||
e51f1d1a | 321 | request.state = state; |
a5192032 | 322 | return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SEND_BREAK, |
e51f1d1a | 323 | &request, sizeof(request), NULL, 0); |
b4be4043 GKH |
324 | } |
325 | ||
5dad5c31 AH |
326 | static int gb_uart_wait_for_all_credits(struct gb_tty *gb_tty) |
327 | { | |
328 | int ret; | |
329 | ||
330 | if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) | |
331 | return 0; | |
332 | ||
333 | ret = wait_for_completion_timeout(&gb_tty->credits_complete, | |
334 | msecs_to_jiffies(GB_UART_CREDIT_WAIT_TIMEOUT_MSEC)); | |
335 | if (!ret) { | |
336 | dev_err(&gb_tty->gbphy_dev->dev, | |
337 | "time out waiting for credits\n"); | |
338 | return -ETIMEDOUT; | |
339 | } | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
2b3b87f0 AH |
344 | static int gb_uart_flush(struct gb_tty *gb_tty, u8 flags) |
345 | { | |
346 | struct gb_uart_serial_flush_request request; | |
347 | ||
348 | request.flags = flags; | |
349 | return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_FLUSH_FIFOS, | |
350 | &request, sizeof(request), NULL, 0); | |
351 | } | |
ff45c265 | 352 | |
26746a36 | 353 | static struct gb_tty *get_gb_by_minor(unsigned int minor) |
ff45c265 GKH |
354 | { |
355 | struct gb_tty *gb_tty; | |
356 | ||
357 | mutex_lock(&table_lock); | |
358 | gb_tty = idr_find(&tty_minors, minor); | |
e68453ed GKH |
359 | if (gb_tty) { |
360 | mutex_lock(&gb_tty->mutex); | |
361 | if (gb_tty->disconnected) { | |
362 | mutex_unlock(&gb_tty->mutex); | |
363 | gb_tty = NULL; | |
364 | } else { | |
365 | tty_port_get(&gb_tty->port); | |
366 | mutex_unlock(&gb_tty->mutex); | |
367 | } | |
368 | } | |
ff45c265 GKH |
369 | mutex_unlock(&table_lock); |
370 | return gb_tty; | |
371 | } | |
372 | ||
373 | static int alloc_minor(struct gb_tty *gb_tty) | |
374 | { | |
375 | int minor; | |
376 | ||
377 | mutex_lock(&table_lock); | |
ff5f0b38 | 378 | minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); |
ff45c265 | 379 | mutex_unlock(&table_lock); |
ff5f0b38 AE |
380 | if (minor >= 0) |
381 | gb_tty->minor = minor; | |
ff45c265 GKH |
382 | return minor; |
383 | } | |
384 | ||
385 | static void release_minor(struct gb_tty *gb_tty) | |
386 | { | |
ff5f0b38 AE |
387 | int minor = gb_tty->minor; |
388 | ||
389 | gb_tty->minor = 0; /* Maybe should use an invalid value instead */ | |
ff45c265 | 390 | mutex_lock(&table_lock); |
ff5f0b38 | 391 | idr_remove(&tty_minors, minor); |
ff45c265 GKH |
392 | mutex_unlock(&table_lock); |
393 | } | |
394 | ||
395 | static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) | |
396 | { | |
397 | struct gb_tty *gb_tty; | |
398 | int retval; | |
399 | ||
400 | gb_tty = get_gb_by_minor(tty->index); | |
401 | if (!gb_tty) | |
402 | return -ENODEV; | |
403 | ||
404 | retval = tty_standard_install(driver, tty); | |
405 | if (retval) | |
406 | goto error; | |
407 | ||
408 | tty->driver_data = gb_tty; | |
409 | return 0; | |
410 | error: | |
411 | tty_port_put(&gb_tty->port); | |
412 | return retval; | |
413 | } | |
414 | ||
415 | static int gb_tty_open(struct tty_struct *tty, struct file *file) | |
416 | { | |
417 | struct gb_tty *gb_tty = tty->driver_data; | |
418 | ||
419 | return tty_port_open(&gb_tty->port, tty, file); | |
420 | } | |
421 | ||
422 | static void gb_tty_close(struct tty_struct *tty, struct file *file) | |
423 | { | |
424 | struct gb_tty *gb_tty = tty->driver_data; | |
425 | ||
426 | tty_port_close(&gb_tty->port, tty, file); | |
427 | } | |
428 | ||
429 | static void gb_tty_cleanup(struct tty_struct *tty) | |
430 | { | |
431 | struct gb_tty *gb_tty = tty->driver_data; | |
432 | ||
433 | tty_port_put(&gb_tty->port); | |
434 | } | |
435 | ||
436 | static void gb_tty_hangup(struct tty_struct *tty) | |
437 | { | |
438 | struct gb_tty *gb_tty = tty->driver_data; | |
439 | ||
440 | tty_port_hangup(&gb_tty->port); | |
441 | } | |
442 | ||
a18e1517 GKH |
443 | static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, |
444 | int count) | |
445 | { | |
b4be4043 | 446 | struct gb_tty *gb_tty = tty->driver_data; |
a18e1517 | 447 | |
8d6fbe9b | 448 | count = kfifo_in_spinlocked(&gb_tty->write_fifo, buf, count, |
319b78c3 | 449 | &gb_tty->write_lock); |
8d6fbe9b AH |
450 | if (count && !gb_tty->close_pending) |
451 | schedule_work(&gb_tty->tx_work); | |
452 | ||
453 | return count; | |
a18e1517 GKH |
454 | } |
455 | ||
456 | static int gb_tty_write_room(struct tty_struct *tty) | |
457 | { | |
dd1c64ed | 458 | struct gb_tty *gb_tty = tty->driver_data; |
8d6fbe9b AH |
459 | unsigned long flags; |
460 | int room; | |
a18e1517 | 461 | |
8d6fbe9b AH |
462 | spin_lock_irqsave(&gb_tty->write_lock, flags); |
463 | room = kfifo_avail(&gb_tty->write_fifo); | |
464 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
465 | ||
466 | room -= GB_UART_WRITE_ROOM_MARGIN; | |
467 | if (room < 0) | |
468 | return 0; | |
469 | ||
470 | return room; | |
a18e1517 GKH |
471 | } |
472 | ||
473 | static int gb_tty_chars_in_buffer(struct tty_struct *tty) | |
474 | { | |
8d6fbe9b AH |
475 | struct gb_tty *gb_tty = tty->driver_data; |
476 | unsigned long flags; | |
477 | int chars; | |
478 | ||
479 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
480 | chars = kfifo_len(&gb_tty->write_fifo); | |
a8bc00fb AH |
481 | if (gb_tty->credits < GB_UART_FIRMWARE_CREDITS) |
482 | chars += GB_UART_FIRMWARE_CREDITS - gb_tty->credits; | |
8d6fbe9b AH |
483 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); |
484 | ||
485 | return chars; | |
a18e1517 GKH |
486 | } |
487 | ||
e68453ed GKH |
488 | static int gb_tty_break_ctl(struct tty_struct *tty, int state) |
489 | { | |
b4be4043 | 490 | struct gb_tty *gb_tty = tty->driver_data; |
e68453ed | 491 | |
b4be4043 | 492 | return send_break(gb_tty, state ? 1 : 0); |
e68453ed GKH |
493 | } |
494 | ||
b4be4043 GKH |
495 | static void gb_tty_set_termios(struct tty_struct *tty, |
496 | struct ktermios *termios_old) | |
e68453ed | 497 | { |
b4be4043 GKH |
498 | struct gb_tty *gb_tty = tty->driver_data; |
499 | struct ktermios *termios = &tty->termios; | |
11fca140 | 500 | struct gb_tty_line_coding newline; |
ba4b099c | 501 | u8 newctrl = gb_tty->ctrlout; |
b4be4043 GKH |
502 | |
503 | newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); | |
62229a1b BD |
504 | newline.format = termios->c_cflag & CSTOPB ? |
505 | GB_SERIAL_2_STOP_BITS : GB_SERIAL_1_STOP_BITS; | |
b4be4043 GKH |
506 | newline.parity = termios->c_cflag & PARENB ? |
507 | (termios->c_cflag & PARODD ? 1 : 2) + | |
508 | (termios->c_cflag & CMSPAR ? 2 : 0) : 0; | |
509 | ||
510 | switch (termios->c_cflag & CSIZE) { | |
511 | case CS5: | |
11fca140 | 512 | newline.data_bits = 5; |
b4be4043 GKH |
513 | break; |
514 | case CS6: | |
11fca140 | 515 | newline.data_bits = 6; |
b4be4043 GKH |
516 | break; |
517 | case CS7: | |
11fca140 | 518 | newline.data_bits = 7; |
b4be4043 GKH |
519 | break; |
520 | case CS8: | |
521 | default: | |
11fca140 | 522 | newline.data_bits = 8; |
b4be4043 GKH |
523 | break; |
524 | } | |
525 | ||
526 | /* FIXME: needs to clear unsupported bits in the termios */ | |
527 | gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); | |
528 | ||
529 | if (C_BAUD(tty) == B0) { | |
530 | newline.rate = gb_tty->line_coding.rate; | |
e55c2520 | 531 | newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); |
b4be4043 | 532 | } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { |
e55c2520 | 533 | newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); |
b4be4043 GKH |
534 | } |
535 | ||
536 | if (newctrl != gb_tty->ctrlout) { | |
537 | gb_tty->ctrlout = newctrl; | |
538 | send_control(gb_tty, newctrl); | |
539 | } | |
540 | ||
e55c2520 AH |
541 | if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) |
542 | newline.flow_control |= GB_SERIAL_AUTO_RTSCTS_EN; | |
543 | else | |
544 | newline.flow_control &= ~GB_SERIAL_AUTO_RTSCTS_EN; | |
545 | ||
b4b1b7ed | 546 | if (memcmp(&gb_tty->line_coding, &newline, sizeof(newline))) { |
b4be4043 | 547 | memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); |
9f240f20 | 548 | send_line_coding(gb_tty); |
b4be4043 | 549 | } |
e68453ed GKH |
550 | } |
551 | ||
552 | static int gb_tty_tiocmget(struct tty_struct *tty) | |
553 | { | |
b4be4043 | 554 | struct gb_tty *gb_tty = tty->driver_data; |
e68453ed | 555 | |
b4be4043 GKH |
556 | return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | |
557 | (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | | |
558 | (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | | |
559 | (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | | |
560 | (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | | |
561 | TIOCM_CTS; | |
e68453ed GKH |
562 | } |
563 | ||
564 | static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, | |
565 | unsigned int clear) | |
566 | { | |
b4be4043 | 567 | struct gb_tty *gb_tty = tty->driver_data; |
ba4b099c | 568 | u8 newctrl = gb_tty->ctrlout; |
e68453ed | 569 | |
b4be4043 GKH |
570 | set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | |
571 | (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); | |
572 | clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | | |
573 | (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); | |
574 | ||
575 | newctrl = (newctrl & ~clear) | set; | |
576 | if (gb_tty->ctrlout == newctrl) | |
577 | return 0; | |
578 | ||
579 | gb_tty->ctrlout = newctrl; | |
580 | return send_control(gb_tty, newctrl); | |
e68453ed GKH |
581 | } |
582 | ||
a18e1517 GKH |
583 | static void gb_tty_throttle(struct tty_struct *tty) |
584 | { | |
585 | struct gb_tty *gb_tty = tty->driver_data; | |
980c7c50 GKH |
586 | unsigned char stop_char; |
587 | int retval; | |
588 | ||
589 | if (I_IXOFF(tty)) { | |
590 | stop_char = STOP_CHAR(tty); | |
591 | retval = gb_tty_write(tty, &stop_char, 1); | |
592 | if (retval <= 0) | |
593 | return; | |
594 | } | |
595 | ||
596 | if (tty->termios.c_cflag & CRTSCTS) { | |
597 | gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; | |
598 | retval = send_control(gb_tty, gb_tty->ctrlout); | |
599 | } | |
a18e1517 GKH |
600 | } |
601 | ||
602 | static void gb_tty_unthrottle(struct tty_struct *tty) | |
603 | { | |
604 | struct gb_tty *gb_tty = tty->driver_data; | |
980c7c50 GKH |
605 | unsigned char start_char; |
606 | int retval; | |
a18e1517 | 607 | |
980c7c50 GKH |
608 | if (I_IXOFF(tty)) { |
609 | start_char = START_CHAR(tty); | |
610 | retval = gb_tty_write(tty, &start_char, 1); | |
611 | if (retval <= 0) | |
612 | return; | |
613 | } | |
a18e1517 | 614 | |
980c7c50 GKH |
615 | if (tty->termios.c_cflag & CRTSCTS) { |
616 | gb_tty->ctrlout |= GB_UART_CTRL_RTS; | |
617 | retval = send_control(gb_tty, gb_tty->ctrlout); | |
a18e1517 GKH |
618 | } |
619 | } | |
620 | ||
e68453ed GKH |
621 | static int get_serial_info(struct gb_tty *gb_tty, |
622 | struct serial_struct __user *info) | |
623 | { | |
624 | struct serial_struct tmp; | |
625 | ||
e68453ed | 626 | memset(&tmp, 0, sizeof(tmp)); |
980c7c50 GKH |
627 | tmp.type = PORT_16550A; |
628 | tmp.line = gb_tty->minor; | |
629 | tmp.xmit_fifo_size = 16; | |
630 | tmp.baud_base = 9600; | |
e68453ed | 631 | tmp.close_delay = gb_tty->port.close_delay / 10; |
461ab807 GK |
632 | tmp.closing_wait = |
633 | gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? | |
634 | ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; | |
e68453ed GKH |
635 | |
636 | if (copy_to_user(info, &tmp, sizeof(tmp))) | |
637 | return -EFAULT; | |
3be03d42 | 638 | return 0; |
e68453ed GKH |
639 | } |
640 | ||
641 | static int set_serial_info(struct gb_tty *gb_tty, | |
642 | struct serial_struct __user *newinfo) | |
643 | { | |
644 | struct serial_struct new_serial; | |
645 | unsigned int closing_wait; | |
646 | unsigned int close_delay; | |
69f93abf | 647 | int retval = 0; |
e68453ed GKH |
648 | |
649 | if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) | |
650 | return -EFAULT; | |
651 | ||
652 | close_delay = new_serial.close_delay * 10; | |
653 | closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? | |
654 | ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; | |
655 | ||
656 | mutex_lock(&gb_tty->port.mutex); | |
657 | if (!capable(CAP_SYS_ADMIN)) { | |
658 | if ((close_delay != gb_tty->port.close_delay) || | |
659 | (closing_wait != gb_tty->port.closing_wait)) | |
660 | retval = -EPERM; | |
661 | else | |
662 | retval = -EOPNOTSUPP; | |
663 | } else { | |
664 | gb_tty->port.close_delay = close_delay; | |
665 | gb_tty->port.closing_wait = closing_wait; | |
666 | } | |
667 | mutex_unlock(&gb_tty->port.mutex); | |
668 | return retval; | |
669 | } | |
670 | ||
671 | static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) | |
672 | { | |
673 | int retval = 0; | |
674 | DECLARE_WAITQUEUE(wait, current); | |
675 | struct async_icount old; | |
676 | struct async_icount new; | |
677 | ||
caaa8a83 | 678 | if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) |
e68453ed GKH |
679 | return -EINVAL; |
680 | ||
681 | do { | |
2aae92bd | 682 | spin_lock_irq(&gb_tty->read_lock); |
e68453ed GKH |
683 | old = gb_tty->oldcount; |
684 | new = gb_tty->iocount; | |
685 | gb_tty->oldcount = new; | |
2aae92bd | 686 | spin_unlock_irq(&gb_tty->read_lock); |
e68453ed GKH |
687 | |
688 | if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) | |
689 | break; | |
690 | if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) | |
691 | break; | |
692 | if ((arg & TIOCM_RI) && (old.rng != new.rng)) | |
693 | break; | |
694 | ||
695 | add_wait_queue(&gb_tty->wioctl, &wait); | |
696 | set_current_state(TASK_INTERRUPTIBLE); | |
697 | schedule(); | |
698 | remove_wait_queue(&gb_tty->wioctl, &wait); | |
699 | if (gb_tty->disconnected) { | |
700 | if (arg & TIOCM_CD) | |
701 | break; | |
caaa8a83 AE |
702 | retval = -ENODEV; |
703 | } else if (signal_pending(current)) { | |
704 | retval = -ERESTARTSYS; | |
e68453ed GKH |
705 | } |
706 | } while (!retval); | |
707 | ||
708 | return retval; | |
709 | } | |
710 | ||
424a4b59 JH |
711 | static int gb_tty_get_icount(struct tty_struct *tty, |
712 | struct serial_icounter_struct *icount) | |
e68453ed | 713 | { |
424a4b59 | 714 | struct gb_tty *gb_tty = tty->driver_data; |
e68453ed | 715 | |
424a4b59 JH |
716 | icount->dsr = gb_tty->iocount.dsr; |
717 | icount->rng = gb_tty->iocount.rng; | |
718 | icount->dcd = gb_tty->iocount.dcd; | |
719 | icount->frame = gb_tty->iocount.frame; | |
720 | icount->overrun = gb_tty->iocount.overrun; | |
721 | icount->parity = gb_tty->iocount.parity; | |
722 | icount->brk = gb_tty->iocount.brk; | |
e68453ed | 723 | |
424a4b59 | 724 | return 0; |
e68453ed GKH |
725 | } |
726 | ||
727 | static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, | |
728 | unsigned long arg) | |
729 | { | |
730 | struct gb_tty *gb_tty = tty->driver_data; | |
e68453ed GKH |
731 | |
732 | switch (cmd) { | |
733 | case TIOCGSERIAL: | |
199d68d4 GKH |
734 | return get_serial_info(gb_tty, |
735 | (struct serial_struct __user *)arg); | |
e68453ed | 736 | case TIOCSSERIAL: |
199d68d4 GKH |
737 | return set_serial_info(gb_tty, |
738 | (struct serial_struct __user *)arg); | |
e68453ed | 739 | case TIOCMIWAIT: |
199d68d4 | 740 | return wait_serial_change(gb_tty, arg); |
e68453ed GKH |
741 | } |
742 | ||
199d68d4 | 743 | return -ENOIOCTLCMD; |
e68453ed GKH |
744 | } |
745 | ||
219ffcf3 AH |
746 | static void gb_tty_dtr_rts(struct tty_port *port, int on) |
747 | { | |
748 | struct gb_tty *gb_tty; | |
749 | u8 newctrl; | |
750 | ||
751 | gb_tty = container_of(port, struct gb_tty, port); | |
752 | newctrl = gb_tty->ctrlout; | |
753 | ||
754 | if (on) | |
755 | newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); | |
756 | else | |
757 | newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); | |
758 | ||
759 | gb_tty->ctrlout = newctrl; | |
760 | send_control(gb_tty, newctrl); | |
761 | } | |
79c822be | 762 | |
22b87087 AH |
763 | static int gb_tty_port_activate(struct tty_port *port, |
764 | struct tty_struct *tty) | |
765 | { | |
766 | struct gb_tty *gb_tty; | |
767 | ||
768 | gb_tty = container_of(port, struct gb_tty, port); | |
769 | ||
770 | return gbphy_runtime_get_sync(gb_tty->gbphy_dev); | |
771 | } | |
772 | ||
8d6fbe9b AH |
773 | static void gb_tty_port_shutdown(struct tty_port *port) |
774 | { | |
775 | struct gb_tty *gb_tty; | |
776 | unsigned long flags; | |
2b3b87f0 | 777 | int ret; |
8d6fbe9b AH |
778 | |
779 | gb_tty = container_of(port, struct gb_tty, port); | |
780 | ||
781 | gb_tty->close_pending = true; | |
782 | ||
783 | cancel_work_sync(&gb_tty->tx_work); | |
784 | ||
785 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
786 | kfifo_reset_out(&gb_tty->write_fifo); | |
787 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
788 | ||
5dad5c31 AH |
789 | if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) |
790 | goto out; | |
791 | ||
2b3b87f0 AH |
792 | ret = gb_uart_flush(gb_tty, GB_SERIAL_FLAG_FLUSH_TRANSMITTER); |
793 | if (ret) { | |
794 | dev_err(&gb_tty->gbphy_dev->dev, | |
795 | "error flushing transmitter: %d\n", ret); | |
796 | } | |
797 | ||
5dad5c31 AH |
798 | gb_uart_wait_for_all_credits(gb_tty); |
799 | ||
800 | out: | |
8d6fbe9b | 801 | gb_tty->close_pending = false; |
22b87087 AH |
802 | |
803 | gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev); | |
8d6fbe9b AH |
804 | } |
805 | ||
79c822be GKH |
806 | static const struct tty_operations gb_ops = { |
807 | .install = gb_tty_install, | |
808 | .open = gb_tty_open, | |
809 | .close = gb_tty_close, | |
810 | .cleanup = gb_tty_cleanup, | |
811 | .hangup = gb_tty_hangup, | |
812 | .write = gb_tty_write, | |
813 | .write_room = gb_tty_write_room, | |
814 | .ioctl = gb_tty_ioctl, | |
815 | .throttle = gb_tty_throttle, | |
816 | .unthrottle = gb_tty_unthrottle, | |
817 | .chars_in_buffer = gb_tty_chars_in_buffer, | |
818 | .break_ctl = gb_tty_break_ctl, | |
819 | .set_termios = gb_tty_set_termios, | |
820 | .tiocmget = gb_tty_tiocmget, | |
821 | .tiocmset = gb_tty_tiocmset, | |
424a4b59 | 822 | .get_icount = gb_tty_get_icount, |
79c822be GKH |
823 | }; |
824 | ||
4d79f431 | 825 | static const struct tty_port_operations gb_port_ops = { |
219ffcf3 | 826 | .dtr_rts = gb_tty_dtr_rts, |
22b87087 | 827 | .activate = gb_tty_port_activate, |
8d6fbe9b | 828 | .shutdown = gb_tty_port_shutdown, |
219ffcf3 | 829 | }; |
79c822be | 830 | |
e54b106d SP |
831 | static int gb_uart_probe(struct gbphy_device *gbphy_dev, |
832 | const struct gbphy_device_id *id) | |
79c822be | 833 | { |
7dbe1f49 | 834 | struct gb_connection *connection; |
066f950c | 835 | size_t max_payload; |
a18e1517 GKH |
836 | struct gb_tty *gb_tty; |
837 | struct device *tty_dev; | |
79c822be | 838 | int retval; |
a18e1517 GKH |
839 | int minor; |
840 | ||
8bf23e84 | 841 | gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); |
73d292d8 VK |
842 | if (!gb_tty) |
843 | return -ENOMEM; | |
7dbe1f49 | 844 | |
e54b106d SP |
845 | connection = gb_connection_create(gbphy_dev->bundle, |
846 | le16_to_cpu(gbphy_dev->cport_desc->id), | |
7dbe1f49 GKH |
847 | gb_uart_request_handler); |
848 | if (IS_ERR(connection)) { | |
849 | retval = PTR_ERR(connection); | |
850 | goto exit_tty_free; | |
55a8e355 | 851 | } |
f95ad78c | 852 | |
066f950c JH |
853 | max_payload = gb_operation_get_payload_size_max(connection); |
854 | if (max_payload < sizeof(struct gb_uart_send_data_request)) { | |
855 | retval = -EINVAL; | |
7dbe1f49 | 856 | goto exit_connection_destroy; |
066f950c JH |
857 | } |
858 | ||
859 | gb_tty->buffer_payload_max = max_payload - | |
7f29aded | 860 | sizeof(struct gb_uart_send_data_request); |
dd1c64ed BD |
861 | |
862 | gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); | |
863 | if (!gb_tty->buffer) { | |
55a8e355 | 864 | retval = -ENOMEM; |
7dbe1f49 | 865 | goto exit_connection_destroy; |
dd1c64ed BD |
866 | } |
867 | ||
8d6fbe9b AH |
868 | INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work); |
869 | ||
870 | retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE, | |
319b78c3 | 871 | GFP_KERNEL); |
8d6fbe9b AH |
872 | if (retval) |
873 | goto exit_buf_free; | |
874 | ||
a8bc00fb | 875 | gb_tty->credits = GB_UART_FIRMWARE_CREDITS; |
5dad5c31 | 876 | init_completion(&gb_tty->credits_complete); |
a8bc00fb | 877 | |
a18e1517 | 878 | minor = alloc_minor(gb_tty); |
ff5f0b38 AE |
879 | if (minor < 0) { |
880 | if (minor == -ENOSPC) { | |
4fa58912 | 881 | dev_err(&gbphy_dev->dev, |
b4be4043 | 882 | "no more free minor numbers\n"); |
55f22911 | 883 | retval = -ENODEV; |
7dbe1f49 GKH |
884 | } else { |
885 | retval = minor; | |
ff5f0b38 | 886 | } |
8d6fbe9b | 887 | goto exit_kfifo_free; |
a18e1517 GKH |
888 | } |
889 | ||
890 | gb_tty->minor = minor; | |
a18e1517 GKH |
891 | spin_lock_init(&gb_tty->write_lock); |
892 | spin_lock_init(&gb_tty->read_lock); | |
e68453ed GKH |
893 | init_waitqueue_head(&gb_tty->wioctl); |
894 | mutex_init(&gb_tty->mutex); | |
a18e1517 | 895 | |
f95ad78c | 896 | tty_port_init(&gb_tty->port); |
219ffcf3 | 897 | gb_tty->port.ops = &gb_port_ops; |
f95ad78c | 898 | |
7dbe1f49 | 899 | gb_tty->connection = connection; |
e54b106d | 900 | gb_tty->gbphy_dev = gbphy_dev; |
7dbe1f49 | 901 | gb_connection_set_data(connection, gb_tty); |
e54b106d | 902 | gb_gbphy_set_data(gbphy_dev, gb_tty); |
7dbe1f49 GKH |
903 | |
904 | retval = gb_connection_enable_tx(connection); | |
905 | if (retval) | |
906 | goto exit_release_minor; | |
907 | ||
b4be4043 GKH |
908 | send_control(gb_tty, gb_tty->ctrlout); |
909 | ||
910 | /* initialize the uart to be 9600n81 */ | |
911 | gb_tty->line_coding.rate = cpu_to_le32(9600); | |
912 | gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; | |
913 | gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; | |
11fca140 | 914 | gb_tty->line_coding.data_bits = 8; |
9f240f20 | 915 | send_line_coding(gb_tty); |
b4be4043 | 916 | |
7dbe1f49 GKH |
917 | retval = gb_connection_enable(connection); |
918 | if (retval) | |
919 | goto exit_connection_disable; | |
920 | ||
a18e1517 | 921 | tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, |
e54b106d | 922 | &gbphy_dev->dev); |
a18e1517 GKH |
923 | if (IS_ERR(tty_dev)) { |
924 | retval = PTR_ERR(tty_dev); | |
7dbe1f49 | 925 | goto exit_connection_disable; |
a18e1517 | 926 | } |
79c822be | 927 | |
22b87087 | 928 | gbphy_runtime_put_autosuspend(gbphy_dev); |
79c822be | 929 | return 0; |
7dbe1f49 GKH |
930 | |
931 | exit_connection_disable: | |
932 | gb_connection_disable(connection); | |
933 | exit_release_minor: | |
a18e1517 | 934 | release_minor(gb_tty); |
8d6fbe9b AH |
935 | exit_kfifo_free: |
936 | kfifo_free(&gb_tty->write_fifo); | |
7dbe1f49 | 937 | exit_buf_free: |
dd1c64ed | 938 | kfree(gb_tty->buffer); |
7dbe1f49 GKH |
939 | exit_connection_destroy: |
940 | gb_connection_destroy(connection); | |
941 | exit_tty_free: | |
b4be4043 | 942 | kfree(gb_tty); |
7dbe1f49 | 943 | |
a18e1517 | 944 | return retval; |
79c822be GKH |
945 | } |
946 | ||
e54b106d | 947 | static void gb_uart_remove(struct gbphy_device *gbphy_dev) |
79c822be | 948 | { |
e54b106d | 949 | struct gb_tty *gb_tty = gb_gbphy_get_data(gbphy_dev); |
7dbe1f49 | 950 | struct gb_connection *connection = gb_tty->connection; |
a18e1517 | 951 | struct tty_struct *tty; |
22b87087 AH |
952 | int ret; |
953 | ||
954 | ret = gbphy_runtime_get_sync(gbphy_dev); | |
955 | if (ret) | |
956 | gbphy_runtime_get_noresume(gbphy_dev); | |
a18e1517 | 957 | |
e68453ed GKH |
958 | mutex_lock(&gb_tty->mutex); |
959 | gb_tty->disconnected = true; | |
960 | ||
961 | wake_up_all(&gb_tty->wioctl); | |
e68453ed GKH |
962 | mutex_unlock(&gb_tty->mutex); |
963 | ||
a18e1517 GKH |
964 | tty = tty_port_tty_get(&gb_tty->port); |
965 | if (tty) { | |
966 | tty_vhangup(tty); | |
967 | tty_kref_put(tty); | |
968 | } | |
a18e1517 | 969 | |
7dbe1f49 | 970 | gb_connection_disable_rx(connection); |
a18e1517 GKH |
971 | tty_unregister_device(gb_tty_driver, gb_tty->minor); |
972 | ||
696e0cca | 973 | /* FIXME - free transmit / receive buffers */ |
e68453ed | 974 | |
7dbe1f49 | 975 | gb_connection_disable(connection); |
f95ad78c | 976 | tty_port_destroy(&gb_tty->port); |
7dbe1f49 | 977 | gb_connection_destroy(connection); |
6db9cc68 | 978 | release_minor(gb_tty); |
8d6fbe9b | 979 | kfifo_free(&gb_tty->write_fifo); |
dd1c64ed | 980 | kfree(gb_tty->buffer); |
e5f167f1 | 981 | kfree(gb_tty); |
79c822be GKH |
982 | } |
983 | ||
4d980989 | 984 | static int gb_tty_init(void) |
79c822be | 985 | { |
7fabc884 | 986 | int retval = 0; |
79c822be | 987 | |
f8089c0c | 988 | gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); |
7fabc884 | 989 | if (IS_ERR(gb_tty_driver)) { |
168db1cd | 990 | pr_err("Can not allocate tty driver\n"); |
7fabc884 MB |
991 | retval = -ENOMEM; |
992 | goto fail_unregister_dev; | |
993 | } | |
79c822be GKH |
994 | |
995 | gb_tty_driver->driver_name = "gb"; | |
7fabc884 | 996 | gb_tty_driver->name = GB_NAME; |
168db1cd GKH |
997 | gb_tty_driver->major = 0; |
998 | gb_tty_driver->minor_start = 0; | |
79c822be GKH |
999 | gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; |
1000 | gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; | |
1001 | gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; | |
1002 | gb_tty_driver->init_termios = tty_std_termios; | |
461ab807 GK |
1003 | gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | |
1004 | CREAD | HUPCL | CLOCAL; | |
79c822be GKH |
1005 | tty_set_operations(gb_tty_driver, &gb_ops); |
1006 | ||
1007 | retval = tty_register_driver(gb_tty_driver); | |
168db1cd GKH |
1008 | if (retval) { |
1009 | pr_err("Can not register tty driver: %d\n", retval); | |
7fabc884 | 1010 | goto fail_put_gb_tty; |
168db1cd | 1011 | } |
79c822be | 1012 | |
7fabc884 MB |
1013 | return 0; |
1014 | ||
543b8ed2 | 1015 | fail_put_gb_tty: |
7fabc884 | 1016 | put_tty_driver(gb_tty_driver); |
543b8ed2 | 1017 | fail_unregister_dev: |
79c822be GKH |
1018 | return retval; |
1019 | } | |
1020 | ||
4d980989 | 1021 | static void gb_tty_exit(void) |
79c822be | 1022 | { |
79c822be GKH |
1023 | tty_unregister_driver(gb_tty_driver); |
1024 | put_tty_driver(gb_tty_driver); | |
5c1ac694 | 1025 | idr_destroy(&tty_minors); |
79c822be | 1026 | } |
3689f974 | 1027 | |
e54b106d SP |
1028 | static const struct gbphy_device_id gb_uart_id_table[] = { |
1029 | { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_UART) }, | |
7dbe1f49 | 1030 | { }, |
19d03dec | 1031 | }; |
e54b106d | 1032 | MODULE_DEVICE_TABLE(gbphy, gb_uart_id_table); |
19d03dec | 1033 | |
e54b106d | 1034 | static struct gbphy_driver uart_driver = { |
7dbe1f49 GKH |
1035 | .name = "uart", |
1036 | .probe = gb_uart_probe, | |
1037 | .remove = gb_uart_remove, | |
1038 | .id_table = gb_uart_id_table, | |
1039 | }; | |
73d292d8 | 1040 | |
66b9e09e | 1041 | static int gb_uart_driver_init(void) |
73d292d8 VK |
1042 | { |
1043 | int ret; | |
1044 | ||
1045 | ret = gb_tty_init(); | |
1046 | if (ret) | |
1047 | return ret; | |
1048 | ||
e54b106d | 1049 | ret = gb_gbphy_register(&uart_driver); |
73d292d8 VK |
1050 | if (ret) { |
1051 | gb_tty_exit(); | |
1052 | return ret; | |
1053 | } | |
1054 | ||
1055 | return 0; | |
1056 | } | |
66b9e09e | 1057 | module_init(gb_uart_driver_init); |
73d292d8 | 1058 | |
66b9e09e | 1059 | static void gb_uart_driver_exit(void) |
73d292d8 | 1060 | { |
e54b106d | 1061 | gb_gbphy_deregister(&uart_driver); |
73d292d8 VK |
1062 | gb_tty_exit(); |
1063 | } | |
66b9e09e VK |
1064 | |
1065 | module_exit(gb_uart_driver_exit); | |
1066 | MODULE_LICENSE("GPL v2"); |