Commit c230035c authored by Sergey Ryazanov's avatar Sergey Ryazanov Committed by David S. Miller

net: wwan: core: implement terminal ioctls for AT port

It is not unreasonable to assume that users will use terminal emulation
software to communicate directly with a WWAN device over the AT port.
But terminal emulators  will refuse to work with a device that does not
support terminal IOCTLs (e.g. TCGETS, TCSETS, TIOCMSET, etc.). To make
it possible to interact with the WWAN AT port using a terminal emulator,
implement a minimal set of terminal IOCTLs.

The implementation is rather stub, no passed data are actually used to
control a port behaviour. An obtained configuration is kept inside the
port structure and returned back by a request. The latter is done to
fool a program that will test the configuration status by comparing the
readed back data from the device with earlier configured ones.

Tested with fresh versions of minicom and picocom terminal apps.

MBIM, QMI and other ports for binary protocols can hardly be considered
a terminal device, so terminal IOCTLs are only implemented for the AT
port.
Signed-off-by: default avatarSergey Ryazanov <ryazanov.s.a@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e263c5b2
...@@ -51,6 +51,8 @@ struct wwan_device { ...@@ -51,6 +51,8 @@ struct wwan_device {
* @dev: Underlying device * @dev: Underlying device
* @rxq: Buffer inbound queue * @rxq: Buffer inbound queue
* @waitqueue: The waitqueue for port fops (read/write/poll) * @waitqueue: The waitqueue for port fops (read/write/poll)
* @data_lock: Port specific data access serialization
* @at_data: AT port specific data
*/ */
struct wwan_port { struct wwan_port {
enum wwan_port_type type; enum wwan_port_type type;
...@@ -61,6 +63,13 @@ struct wwan_port { ...@@ -61,6 +63,13 @@ struct wwan_port {
struct device dev; struct device dev;
struct sk_buff_head rxq; struct sk_buff_head rxq;
wait_queue_head_t waitqueue; wait_queue_head_t waitqueue;
struct mutex data_lock; /* Port specific data access serialization */
union {
struct {
struct ktermios termios;
int mdmbits;
} at_data;
};
}; };
static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf)
...@@ -230,6 +239,7 @@ static void wwan_port_destroy(struct device *dev) ...@@ -230,6 +239,7 @@ static void wwan_port_destroy(struct device *dev)
struct wwan_port *port = to_wwan_port(dev); struct wwan_port *port = to_wwan_port(dev);
ida_free(&minors, MINOR(port->dev.devt)); ida_free(&minors, MINOR(port->dev.devt));
mutex_destroy(&port->data_lock);
skb_queue_purge(&port->rxq); skb_queue_purge(&port->rxq);
mutex_destroy(&port->ops_lock); mutex_destroy(&port->ops_lock);
kfree(port); kfree(port);
...@@ -344,6 +354,7 @@ struct wwan_port *wwan_create_port(struct device *parent, ...@@ -344,6 +354,7 @@ struct wwan_port *wwan_create_port(struct device *parent,
mutex_init(&port->ops_lock); mutex_init(&port->ops_lock);
skb_queue_head_init(&port->rxq); skb_queue_head_init(&port->rxq);
init_waitqueue_head(&port->waitqueue); init_waitqueue_head(&port->waitqueue);
mutex_init(&port->data_lock);
port->dev.parent = &wwandev->dev; port->dev.parent = &wwandev->dev;
port->dev.class = wwan_class; port->dev.class = wwan_class;
...@@ -619,10 +630,90 @@ static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait) ...@@ -619,10 +630,90 @@ static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait)
return mask; return mask;
} }
/* Implements minimalistic stub terminal IOCTLs support */
static long wwan_port_fops_at_ioctl(struct wwan_port *port, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
mutex_lock(&port->data_lock);
switch (cmd) {
case TCFLSH:
break;
case TCGETS:
if (copy_to_user((void __user *)arg, &port->at_data.termios,
sizeof(struct termios)))
ret = -EFAULT;
break;
case TCSETS:
case TCSETSW:
case TCSETSF:
if (copy_from_user(&port->at_data.termios, (void __user *)arg,
sizeof(struct termios)))
ret = -EFAULT;
break;
#ifdef TCGETS2
case TCGETS2:
if (copy_to_user((void __user *)arg, &port->at_data.termios,
sizeof(struct termios2)))
ret = -EFAULT;
break;
case TCSETS2:
case TCSETSW2:
case TCSETSF2:
if (copy_from_user(&port->at_data.termios, (void __user *)arg,
sizeof(struct termios2)))
ret = -EFAULT;
break;
#endif
case TIOCMGET:
ret = put_user(port->at_data.mdmbits, (int __user *)arg);
break;
case TIOCMSET:
case TIOCMBIC:
case TIOCMBIS: {
int mdmbits;
if (copy_from_user(&mdmbits, (int __user *)arg, sizeof(int))) {
ret = -EFAULT;
break;
}
if (cmd == TIOCMBIC)
port->at_data.mdmbits &= ~mdmbits;
else if (cmd == TIOCMBIS)
port->at_data.mdmbits |= mdmbits;
else
port->at_data.mdmbits = mdmbits;
break;
}
default:
ret = -ENOIOCTLCMD;
}
mutex_unlock(&port->data_lock);
return ret;
}
static long wwan_port_fops_ioctl(struct file *filp, unsigned int cmd, static long wwan_port_fops_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
struct wwan_port *port = filp->private_data; struct wwan_port *port = filp->private_data;
int res;
if (port->type == WWAN_PORT_AT) { /* AT port specific IOCTLs */
res = wwan_port_fops_at_ioctl(port, cmd, arg);
if (res != -ENOIOCTLCMD)
return res;
}
switch (cmd) { switch (cmd) {
case TIOCINQ: { /* aka SIOCINQ aka FIONREAD */ case TIOCINQ: { /* aka SIOCINQ aka FIONREAD */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment