blob: 54cc5f27d934fa40dd8b84df0c468bae4a2ee345 [file] [log] [blame]
#include <linux/err.h>
#include <linux/kfifo.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
extern int do_rsh_write(void *rpdev,
const unsigned char *buf,
int count);
struct rsh_struct
{
struct tty_port port;
struct timer_list timer;
struct kfifo fifo;
void *rpdev;
spinlock_t lock;
};
static struct rsh_struct g_rsh[3];
static struct tty_driver *g_driver;
void rsh_cb(int index, void *data, int len)
{
struct rsh_struct *rsh = &g_rsh[index];
kfifo_in_locked(&rsh->fifo, (char *)data, len, &rsh->lock);
}
static void rsh_receive_chars(unsigned long data)
{
struct rsh_struct *rsh = (struct rsh_struct *)data;
struct tty_port *port = &rsh->port;
char c;
while (kfifo_out_locked(&rsh->fifo, &c, sizeof(c), &rsh->lock))
tty_insert_flip_char(port, c, 0);
tty_schedule_flip(port);
spin_lock(&port->lock);
if (port->tty)
mod_timer(&rsh->timer, jiffies + 50);
spin_unlock(&port->lock);
}
static int rsh_write(struct tty_struct *tty, const unsigned char *buf,
int count)
{
struct rsh_struct *rsh = &g_rsh[tty->index];
return do_rsh_write(rsh->rpdev, buf, count);
}
static int rsh_write_room(struct tty_struct *tty)
{
return INT_MAX;
}
static int rsh_chars_in_buffer(struct tty_struct *tty)
{
return 0;
}
static int rsh_open(struct tty_struct *tty, struct file *filp)
{
struct rsh_struct *rsh = &g_rsh[tty->index];
struct tty_port *port = &rsh->port;
if (!port->tty) {
tty->driver_data = rsh;
tty->port = port;
port->tty = tty;
mod_timer(&rsh->timer, jiffies + 10);
}
return 0;
}
static void rsh_close(struct tty_struct *tty, struct file *filp)
{
struct rsh_struct *rsh = &g_rsh[tty->index];
struct tty_port *port = &rsh->port;
if (tty->count == 1) {
port->tty = NULL;
del_timer(&rsh->timer);
}
}
static const struct tty_operations rsh_ops =
{
.open = rsh_open,
.close = rsh_close,
.write = rsh_write,
.write_room = rsh_write_room,
.chars_in_buffer = rsh_chars_in_buffer,
};
extern int rsh_init_device(int index, void *rpdev)
{
struct rsh_struct *rsh = &g_rsh[index];
struct tty_port *port;
int ret;
const char *name[3] = {
"CA9-A <-> CA9-B",
"CA9-A <-> ARM11",
"CA9-B <-> ARM11",
};
rsh = &g_rsh[index];
rsh->rpdev = rpdev;
port = &rsh->port;
tty_port_init(port);
setup_timer(&rsh->timer, rsh_receive_chars, (unsigned long)rsh);
spin_lock_init(&rsh->lock);
ret = kfifo_alloc(&rsh->fifo, 1024 * 64, GFP_KERNEL);
if (ret)
return ret;
tty_port_register_device(port, g_driver, index, NULL);
printk("RPMSG: Remote Shell /dev/ttyRSH%d (%s)\n", index, name[index]);
return 0;
}
int rsh_init_driver(void)
{
struct tty_driver *driver;
int ret;
driver = tty_alloc_driver(3, 0);
if (!driver)
return -ENOMEM;
driver->driver_name = "ttyRSH";
driver->name = "ttyRSH";
driver->major = 0;
driver->minor_start = 0;
driver->type = TTY_DRIVER_TYPE_SYSTEM;
driver->subtype = SYSTEM_TYPE_SYSCONS;
driver->init_termios = tty_std_termios;
driver->flags = TTY_DRIVER_DYNAMIC_DEV;
tty_set_operations(driver, &rsh_ops);
ret = tty_register_driver(driver);
if (ret) {
put_tty_driver(driver);
return ret;
}
g_driver = driver;
return 0;
}