brintos

brintos / linux-shallow public Read only

0
0
Text · 8.0 KiB · 3d7ae7f Raw
330 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2016-2017 Linaro Ltd., Rob Herring <robh@kernel.org>4 */5#include <linux/kernel.h>6#include <linux/serdev.h>7#include <linux/tty.h>8#include <linux/tty_driver.h>9#include <linux/poll.h>10 11#define SERPORT_ACTIVE		112 13struct serport {14	struct tty_port *port;15	struct tty_struct *tty;16	struct tty_driver *tty_drv;17	int tty_idx;18	unsigned long flags;19};20 21/*22 * Callback functions from the tty port.23 */24 25static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp,26				  const u8 *fp, size_t count)27{28	struct serdev_controller *ctrl = port->client_data;29	struct serport *serport = serdev_controller_get_drvdata(ctrl);30	size_t ret;31 32	if (!test_bit(SERPORT_ACTIVE, &serport->flags))33		return 0;34 35	ret = serdev_controller_receive_buf(ctrl, cp, count);36 37	dev_WARN_ONCE(&ctrl->dev, ret > count,38				"receive_buf returns %zu (count = %zu)\n",39				ret, count);40	if (ret > count)41		return count;42 43	return ret;44}45 46static void ttyport_write_wakeup(struct tty_port *port)47{48	struct serdev_controller *ctrl = port->client_data;49	struct serport *serport = serdev_controller_get_drvdata(ctrl);50	struct tty_struct *tty;51 52	tty = tty_port_tty_get(port);53	if (!tty)54		return;55 56	if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&57	    test_bit(SERPORT_ACTIVE, &serport->flags))58		serdev_controller_write_wakeup(ctrl);59 60	/* Wake up any tty_wait_until_sent() */61	wake_up_interruptible(&tty->write_wait);62 63	tty_kref_put(tty);64}65 66static const struct tty_port_client_operations client_ops = {67	.receive_buf = ttyport_receive_buf,68	.write_wakeup = ttyport_write_wakeup,69};70 71/*72 * Callback functions from the serdev core.73 */74 75static ssize_t ttyport_write_buf(struct serdev_controller *ctrl, const u8 *data, size_t len)76{77	struct serport *serport = serdev_controller_get_drvdata(ctrl);78	struct tty_struct *tty = serport->tty;79 80	if (!test_bit(SERPORT_ACTIVE, &serport->flags))81		return 0;82 83	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);84	return tty->ops->write(serport->tty, data, len);85}86 87static void ttyport_write_flush(struct serdev_controller *ctrl)88{89	struct serport *serport = serdev_controller_get_drvdata(ctrl);90	struct tty_struct *tty = serport->tty;91 92	tty_driver_flush_buffer(tty);93}94 95static int ttyport_write_room(struct serdev_controller *ctrl)96{97	struct serport *serport = serdev_controller_get_drvdata(ctrl);98	struct tty_struct *tty = serport->tty;99 100	return tty_write_room(tty);101}102 103static int ttyport_open(struct serdev_controller *ctrl)104{105	struct serport *serport = serdev_controller_get_drvdata(ctrl);106	struct tty_struct *tty;107	struct ktermios ktermios;108	int ret;109 110	tty = tty_init_dev(serport->tty_drv, serport->tty_idx);111	if (IS_ERR(tty))112		return PTR_ERR(tty);113	serport->tty = tty;114 115	if (!tty->ops->open || !tty->ops->close) {116		ret = -ENODEV;117		goto err_unlock;118	}119 120	ret = tty->ops->open(serport->tty, NULL);121	if (ret)122		goto err_close;123 124	tty_unlock(serport->tty);125 126	/* Bring the UART into a known 8 bits no parity hw fc state */127	ktermios = tty->termios;128	ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |129			      INLCR | IGNCR | ICRNL | IXON);130	ktermios.c_oflag &= ~OPOST;131	ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);132	ktermios.c_cflag &= ~(CSIZE | PARENB);133	ktermios.c_cflag |= CS8;134	ktermios.c_cflag |= CRTSCTS;135	/* Hangups are not supported so make sure to ignore carrier detect. */136	ktermios.c_cflag |= CLOCAL;137	tty_set_termios(tty, &ktermios);138 139	set_bit(SERPORT_ACTIVE, &serport->flags);140 141	return 0;142 143err_close:144	tty->ops->close(tty, NULL);145err_unlock:146	tty_unlock(tty);147	tty_release_struct(tty, serport->tty_idx);148 149	return ret;150}151 152static void ttyport_close(struct serdev_controller *ctrl)153{154	struct serport *serport = serdev_controller_get_drvdata(ctrl);155	struct tty_struct *tty = serport->tty;156 157	clear_bit(SERPORT_ACTIVE, &serport->flags);158 159	tty_lock(tty);160	if (tty->ops->close)161		tty->ops->close(tty, NULL);162	tty_unlock(tty);163 164	tty_release_struct(tty, serport->tty_idx);165}166 167static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed)168{169	struct serport *serport = serdev_controller_get_drvdata(ctrl);170	struct tty_struct *tty = serport->tty;171	struct ktermios ktermios = tty->termios;172 173	ktermios.c_cflag &= ~CBAUD;174	tty_termios_encode_baud_rate(&ktermios, speed, speed);175 176	/* tty_set_termios() return not checked as it is always 0 */177	tty_set_termios(tty, &ktermios);178	return ktermios.c_ospeed;179}180 181static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)182{183	struct serport *serport = serdev_controller_get_drvdata(ctrl);184	struct tty_struct *tty = serport->tty;185	struct ktermios ktermios = tty->termios;186 187	if (enable)188		ktermios.c_cflag |= CRTSCTS;189	else190		ktermios.c_cflag &= ~CRTSCTS;191 192	tty_set_termios(tty, &ktermios);193}194 195static int ttyport_set_parity(struct serdev_controller *ctrl,196			      enum serdev_parity parity)197{198	struct serport *serport = serdev_controller_get_drvdata(ctrl);199	struct tty_struct *tty = serport->tty;200	struct ktermios ktermios = tty->termios;201 202	ktermios.c_cflag &= ~(PARENB | PARODD | CMSPAR);203	if (parity != SERDEV_PARITY_NONE) {204		ktermios.c_cflag |= PARENB;205		if (parity == SERDEV_PARITY_ODD)206			ktermios.c_cflag |= PARODD;207	}208 209	tty_set_termios(tty, &ktermios);210 211	if ((tty->termios.c_cflag & (PARENB | PARODD | CMSPAR)) !=212	    (ktermios.c_cflag & (PARENB | PARODD | CMSPAR)))213		return -EINVAL;214 215	return 0;216}217 218static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout)219{220	struct serport *serport = serdev_controller_get_drvdata(ctrl);221	struct tty_struct *tty = serport->tty;222 223	tty_wait_until_sent(tty, timeout);224}225 226static int ttyport_get_tiocm(struct serdev_controller *ctrl)227{228	struct serport *serport = serdev_controller_get_drvdata(ctrl);229	struct tty_struct *tty = serport->tty;230 231	if (!tty->ops->tiocmget)232		return -EOPNOTSUPP;233 234	return tty->ops->tiocmget(tty);235}236 237static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)238{239	struct serport *serport = serdev_controller_get_drvdata(ctrl);240	struct tty_struct *tty = serport->tty;241 242	if (!tty->ops->tiocmset)243		return -EOPNOTSUPP;244 245	return tty->ops->tiocmset(tty, set, clear);246}247 248static int ttyport_break_ctl(struct serdev_controller *ctrl, unsigned int break_state)249{250	struct serport *serport = serdev_controller_get_drvdata(ctrl);251	struct tty_struct *tty = serport->tty;252 253	if (!tty->ops->break_ctl)254		return -EOPNOTSUPP;255 256	return tty->ops->break_ctl(tty, break_state);257}258 259static const struct serdev_controller_ops ctrl_ops = {260	.write_buf = ttyport_write_buf,261	.write_flush = ttyport_write_flush,262	.write_room = ttyport_write_room,263	.open = ttyport_open,264	.close = ttyport_close,265	.set_flow_control = ttyport_set_flow_control,266	.set_parity = ttyport_set_parity,267	.set_baudrate = ttyport_set_baudrate,268	.wait_until_sent = ttyport_wait_until_sent,269	.get_tiocm = ttyport_get_tiocm,270	.set_tiocm = ttyport_set_tiocm,271	.break_ctl = ttyport_break_ctl,272};273 274struct device *serdev_tty_port_register(struct tty_port *port,275					struct device *host,276					struct device *parent,277					struct tty_driver *drv, int idx)278{279	struct serdev_controller *ctrl;280	struct serport *serport;281	int ret;282 283	if (!port || !drv || !parent)284		return ERR_PTR(-ENODEV);285 286	ctrl = serdev_controller_alloc(host, parent, sizeof(struct serport));287	if (!ctrl)288		return ERR_PTR(-ENOMEM);289	serport = serdev_controller_get_drvdata(ctrl);290 291	serport->port = port;292	serport->tty_idx = idx;293	serport->tty_drv = drv;294 295	ctrl->ops = &ctrl_ops;296 297	port->client_ops = &client_ops;298	port->client_data = ctrl;299 300	ret = serdev_controller_add(ctrl);301	if (ret)302		goto err_reset_data;303 304	dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);305	return &ctrl->dev;306 307err_reset_data:308	port->client_data = NULL;309	port->client_ops = &tty_port_default_client_ops;310	serdev_controller_put(ctrl);311 312	return ERR_PTR(ret);313}314 315int serdev_tty_port_unregister(struct tty_port *port)316{317	struct serdev_controller *ctrl = port->client_data;318	struct serport *serport = serdev_controller_get_drvdata(ctrl);319 320	if (!serport)321		return -ENODEV;322 323	serdev_controller_remove(ctrl);324	port->client_data = NULL;325	port->client_ops = &tty_port_default_client_ops;326	serdev_controller_put(ctrl);327 328	return 0;329}330