From dab6b5daeeb3e70bfb2ed2d6d6f02dd387e3b338 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 17 Mar 2017 13:12:46 +0200 Subject: Bluetooth: convert rfcomm_dlc.refcnt from atomic_t to refcount_t refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Elena Reshetova Signed-off-by: Hans Liljestrand Signed-off-by: Kees Cook Signed-off-by: David Windsor Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 4190af53a46a..da4acefe39c8 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -21,6 +21,8 @@ SOFTWARE IS DISCLAIMED. */ +#include + #ifndef __RFCOMM_H #define __RFCOMM_H @@ -174,7 +176,7 @@ struct rfcomm_dlc { struct mutex lock; unsigned long state; unsigned long flags; - atomic_t refcnt; + refcount_t refcnt; u8 dlci; u8 addr; u8 priority; @@ -247,12 +249,12 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel); static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) { - atomic_inc(&d->refcnt); + refcount_inc(&d->refcnt); } static inline void rfcomm_dlc_put(struct rfcomm_dlc *d) { - if (atomic_dec_and_test(&d->refcnt)) + if (refcount_dec_and_test(&d->refcnt)) rfcomm_dlc_free(d); } -- cgit v1.2.3 From fa09ae661fb5ab6f9826545d5128f2b7393bcf4a Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Sun, 12 Mar 2017 10:19:37 +0200 Subject: 6lowpan: Use netdev addr_len to determine lladdr len This allow technologies such as Bluetooth to use its native lladdr which is eui48 instead of eui64 which was expected by functions like lowpan_header_decompress and lowpan_header_compress. Signed-off-by: Luiz Augusto von Dentz Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 19 +++++++++++++++++++ net/6lowpan/iphc.c | 49 ++++++++++++++++++++++++++++++++++++++----------- net/bluetooth/6lowpan.c | 42 ++++++------------------------------------ 3 files changed, 63 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index 5ab4c9901ccc..c5792cb6c3eb 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -198,6 +198,25 @@ static inline void lowpan_iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr, ipaddr->s6_addr[8] ^= 0x02; } +static inline void lowpan_iphc_uncompress_eui48_lladdr(struct in6_addr *ipaddr, + const void *lladdr) +{ + /* fe:80::XXXX:XXff:feXX:XXXX + * \_________________/ + * hwaddr + */ + ipaddr->s6_addr[0] = 0xFE; + ipaddr->s6_addr[1] = 0x80; + memcpy(&ipaddr->s6_addr[8], lladdr, 3); + ipaddr->s6_addr[11] = 0xFF; + ipaddr->s6_addr[12] = 0xFE; + memcpy(&ipaddr->s6_addr[13], lladdr + 3, 3); + /* second bit-flip (Universe/Local) + * is done according RFC2464 + */ + ipaddr->s6_addr[8] ^= 0x02; +} + #ifdef DEBUG /* print data in line */ static inline void raw_dump_inline(const char *caller, char *msg, diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index fb5f6fa8f1df..6b1042e21656 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -278,6 +278,23 @@ lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev, return ret; } +static void lowpan_iphc_uncompress_lladdr(const struct net_device *dev, + struct in6_addr *ipaddr, + const void *lladdr) +{ + switch (dev->addr_len) { + case ETH_ALEN: + lowpan_iphc_uncompress_eui48_lladdr(ipaddr, lladdr); + break; + case EUI64_ADDR_LEN: + lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr); + break; + default: + WARN_ON_ONCE(1); + break; + } +} + /* Uncompress address function for source and * destination address(non-multicast). * @@ -320,7 +337,7 @@ static int lowpan_iphc_uncompress_addr(struct sk_buff *skb, lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr); break; default: - lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr); + lowpan_iphc_uncompress_lladdr(dev, ipaddr, lladdr); break; } break; @@ -381,7 +398,7 @@ static int lowpan_iphc_uncompress_ctx_addr(struct sk_buff *skb, lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr); break; default: - lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr); + lowpan_iphc_uncompress_lladdr(dev, ipaddr, lladdr); break; } ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen); @@ -810,6 +827,21 @@ lowpan_iphc_compress_ctx_802154_lladdr(const struct in6_addr *ipaddr, return lladdr_compress; } +static bool lowpan_iphc_addr_equal(const struct net_device *dev, + const struct lowpan_iphc_ctx *ctx, + const struct in6_addr *ipaddr, + const void *lladdr) +{ + struct in6_addr tmp = {}; + + lowpan_iphc_uncompress_lladdr(dev, &tmp, lladdr); + + if (ctx) + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen); + + return ipv6_addr_equal(&tmp, ipaddr); +} + static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev, const struct in6_addr *ipaddr, const struct lowpan_iphc_ctx *ctx, @@ -827,13 +859,7 @@ static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev, } break; default: - /* check for SAM/DAM = 11 */ - memcpy(&tmp.s6_addr[8], lladdr, EUI64_ADDR_LEN); - /* second bit-flip (Universe/Local) is done according RFC2464 */ - tmp.s6_addr[8] ^= 0x02; - /* context information are always used */ - ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen); - if (ipv6_addr_equal(&tmp, ipaddr)) { + if (lowpan_iphc_addr_equal(dev, ctx, ipaddr, lladdr)) { dam = LOWPAN_IPHC_DAM_11; goto out; } @@ -929,11 +955,12 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct net_device *dev, } break; default: - if (is_addr_mac_addr_based(ipaddr, lladdr)) { - dam = LOWPAN_IPHC_DAM_11; /* 0-bits */ + if (lowpan_iphc_addr_equal(dev, NULL, ipaddr, lladdr)) { + dam = LOWPAN_IPHC_DAM_11; pr_debug("address compression 0 bits\n"); goto out; } + break; } diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index bad0e9d1ea20..54bf4d765a7d 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -64,7 +64,7 @@ struct lowpan_peer { struct l2cap_chan *chan; /* peer addresses in various formats */ - unsigned char eui64_addr[EUI64_ADDR_LEN]; + unsigned char lladdr[ETH_ALEN]; struct in6_addr peer_addr; }; @@ -80,8 +80,6 @@ struct lowpan_btle_dev { struct delayed_work notify_peers; }; -static void set_addr(u8 *eui, u8 *addr, u8 addr_type); - static inline struct lowpan_btle_dev * lowpan_btle_dev(const struct net_device *netdev) { @@ -277,7 +275,6 @@ static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, const u8 *saddr; struct lowpan_btle_dev *dev; struct lowpan_peer *peer; - unsigned char eui64_daddr[EUI64_ADDR_LEN]; dev = lowpan_btle_dev(netdev); @@ -287,10 +284,9 @@ static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, if (!peer) return -EINVAL; - saddr = peer->eui64_addr; - set_addr(&eui64_daddr[0], chan->src.b, chan->src_type); + saddr = peer->lladdr; - return lowpan_header_decompress(skb, netdev, &eui64_daddr, saddr); + return lowpan_header_decompress(skb, netdev, netdev->dev_addr, saddr); } static int recv_pkt(struct sk_buff *skb, struct net_device *dev, @@ -477,7 +473,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, } } - daddr = peer->eui64_addr; + daddr = peer->lladdr; *peer_addr = addr; *peer_addr_type = addr_type; lowpan_cb(skb)->chan = peer->chan; @@ -663,27 +659,6 @@ static struct device_type bt_type = { .name = "bluetooth", }; -static void set_addr(u8 *eui, u8 *addr, u8 addr_type) -{ - /* addr is the BT address in little-endian format */ - eui[0] = addr[5]; - eui[1] = addr[4]; - eui[2] = addr[3]; - eui[3] = 0xFF; - eui[4] = 0xFE; - eui[5] = addr[2]; - eui[6] = addr[1]; - eui[7] = addr[0]; - - /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */ - if (addr_type == BDADDR_LE_PUBLIC) - eui[0] &= ~0x02; - else - eui[0] |= 0x02; - - BT_DBG("type %d addr %*phC", addr_type, 8, eui); -} - static void ifup(struct net_device *netdev) { int err; @@ -762,14 +737,9 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, peer->chan = chan; memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); - /* RFC 2464 ch. 5 */ - peer->peer_addr.s6_addr[0] = 0xFE; - peer->peer_addr.s6_addr[1] = 0x80; - set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b, - chan->dst_type); + baswap((void *)peer->lladdr, &chan->dst); - memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8, - EUI64_ADDR_LEN); + lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr); /* IPv6 address needs to have the U/L bit set properly so toggle * it back here. -- cgit v1.2.3 From 9dae2e030319811e9cdaa260faaa151cf0866186 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Sun, 12 Mar 2017 10:19:38 +0200 Subject: 6lowpan: Fix IID format for Bluetooth According to RFC 7668 U/L bit shall not be used: https://wiki.tools.ietf.org/html/rfc7668#section-3.2.2 [Page 10]: In the figure, letter 'b' represents a bit from the Bluetooth device address, copied as is without any changes on any bit. This means that no bit in the IID indicates whether the underlying Bluetooth device address is public or random. |0 1|1 3|3 4|4 6| |0 5|6 1|2 7|8 3| +----------------+----------------+----------------+----------------+ |bbbbbbbbbbbbbbbb|bbbbbbbb11111111|11111110bbbbbbbb|bbbbbbbbbbbbbbbb| +----------------+----------------+----------------+----------------+ Because of this the code cannot figure out the address type from the IP address anymore thus it makes no sense to use peer_lookup_ba as it needs the peer address type. Signed-off-by: Luiz Augusto von Dentz Reviewed-by: Stefan Schmidt Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 4 --- net/bluetooth/6lowpan.c | 79 ++++++++----------------------------------------- net/ipv6/addrconf.c | 6 +++- 3 files changed, 17 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index c5792cb6c3eb..a71378007e61 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -211,10 +211,6 @@ static inline void lowpan_iphc_uncompress_eui48_lladdr(struct in6_addr *ipaddr, ipaddr->s6_addr[11] = 0xFF; ipaddr->s6_addr[12] = 0xFE; memcpy(&ipaddr->s6_addr[13], lladdr + 3, 3); - /* second bit-flip (Universe/Local) - * is done according RFC2464 - */ - ipaddr->s6_addr[8] ^= 0x02; } #ifdef DEBUG diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 54bf4d765a7d..24348c8579dd 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -398,37 +398,6 @@ static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) return err; } -static u8 get_addr_type_from_eui64(u8 byte) -{ - /* Is universal(0) or local(1) bit */ - return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC); -} - -static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr) -{ - u8 *eui64 = ip6_daddr->s6_addr + 8; - - addr->b[0] = eui64[7]; - addr->b[1] = eui64[6]; - addr->b[2] = eui64[5]; - addr->b[3] = eui64[2]; - addr->b[4] = eui64[1]; - addr->b[5] = eui64[0]; -} - -static void convert_dest_bdaddr(struct in6_addr *ip6_daddr, - bdaddr_t *addr, u8 *addr_type) -{ - copy_to_bdaddr(ip6_daddr, addr); - - /* We need to toggle the U/L bit that we got from IPv6 address - * so that we get the proper address and type of the BD address. - */ - addr->b[5] ^= 0x02; - - *addr_type = get_addr_type_from_eui64(addr->b[5]); -} - static int setup_header(struct sk_buff *skb, struct net_device *netdev, bdaddr_t *peer_addr, u8 *peer_addr_type) { @@ -436,8 +405,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, struct ipv6hdr *hdr; struct lowpan_btle_dev *dev; struct lowpan_peer *peer; - bdaddr_t addr, *any = BDADDR_ANY; - u8 *daddr = any->b; + u8 *daddr; int err, status = 0; hdr = ipv6_hdr(skb); @@ -448,34 +416,24 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, if (ipv6_addr_is_multicast(&ipv6_daddr)) { lowpan_cb(skb)->chan = NULL; + daddr = NULL; } else { - u8 addr_type; + BT_DBG("dest IP %pI6c", &ipv6_daddr); - /* Get destination BT device from skb. - * If there is no such peer then discard the packet. + /* The packet might be sent to 6lowpan interface + * because of routing (either via default route + * or user set route) so get peer according to + * the destination address. */ - convert_dest_bdaddr(&ipv6_daddr, &addr, &addr_type); - - BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, - addr_type, &ipv6_daddr); - - peer = peer_lookup_ba(dev, &addr, addr_type); + peer = peer_lookup_dst(dev, &ipv6_daddr, skb); if (!peer) { - /* The packet might be sent to 6lowpan interface - * because of routing (either via default route - * or user set route) so get peer according to - * the destination address. - */ - peer = peer_lookup_dst(dev, &ipv6_daddr, skb); - if (!peer) { - BT_DBG("no such peer %pMR found", &addr); - return -ENOENT; - } + BT_DBG("no such peer"); + return -ENOENT; } daddr = peer->lladdr; - *peer_addr = addr; - *peer_addr_type = addr_type; + peer_addr = &peer->chan->dst; + *peer_addr_type = peer->chan->dst_type; lowpan_cb(skb)->chan = peer->chan; status = 1; @@ -717,14 +675,6 @@ static struct l2cap_chan *chan_create(void) return chan; } -static void set_ip_addr_bits(u8 addr_type, u8 *addr) -{ - if (addr_type == BDADDR_LE_PUBLIC) - *addr |= 0x02; - else - *addr &= ~0x02; -} - static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, struct lowpan_btle_dev *dev) { @@ -741,11 +691,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr); - /* IPv6 address needs to have the U/L bit set properly so toggle - * it back here. - */ - set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8); - spin_lock(&devices_lock); INIT_LIST_HEAD(&peer->list); peer_add(dev, peer); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b22796e707d3..3650f6e829e8 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2077,7 +2077,11 @@ static int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev) { switch (dev->addr_len) { case ETH_ALEN: - return addrconf_ifid_eui48(eui, dev); + memcpy(eui, dev->dev_addr, 3); + eui[3] = 0xFF; + eui[4] = 0xFE; + memcpy(eui + 5, dev->dev_addr + 3, 3); + break; case EUI64_ADDR_LEN: memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN); eui[0] ^= 2; -- cgit v1.2.3 From d8edd9ed156a1a840f1b1c2dbbf458684d6eea6e Mon Sep 17 00:00:00 2001 From: Marcin Kraglak Date: Wed, 8 Mar 2017 14:09:41 +0100 Subject: Bluetooth: L2CAP: Fix L2CAP_CR_SCID_IN_USE value Fix issue found during L2CAP qualification test TP/LE/CFC/BV-20-C. Signed-off-by: Marcin Kraglak Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 5ee3c689c863..0697fd413087 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -282,7 +282,7 @@ struct l2cap_conn_rsp { #define L2CAP_CR_BAD_KEY_SIZE 0x0007 #define L2CAP_CR_ENCRYPTION 0x0008 #define L2CAP_CR_INVALID_SCID 0x0009 -#define L2CAP_CR_SCID_IN_USE 0x0010 +#define L2CAP_CR_SCID_IN_USE 0x000A /* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 -- cgit v1.2.3 From b3f80c8f75efb2e6a817a0e48bf36cd30685a138 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:31 +0200 Subject: serdev: add serdev_device_wait_until_sent Add method, which waits until the transmission buffer has been sent. Note, that the change in ttyport_write_wakeup is related, since tty_wait_until_sent will hang without that change. Acked-by: Rob Herring Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel Acked-by: Greg Kroah-Hartman Signed-off-by: Marcel Holtmann --- drivers/tty/serdev/core.c | 11 +++++++++++ drivers/tty/serdev/serdev-ttyport.c | 18 ++++++++++++++---- include/linux/serdev.h | 3 +++ 3 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index f4c6c90add78..a63b74031e22 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -173,6 +173,17 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) } EXPORT_SYMBOL_GPL(serdev_device_set_flow_control); +void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->wait_until_sent) + return; + + ctrl->ops->wait_until_sent(ctrl, timeout); +} +EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent); + static int serdev_drv_probe(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index d05393594f15..50dc75c4d204 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -14,6 +14,7 @@ #include #include #include +#include #define SERPORT_ACTIVE 1 @@ -46,11 +47,11 @@ static void ttyport_write_wakeup(struct tty_port *port) struct serdev_controller *ctrl = port->client_data; struct serport *serport = serdev_controller_get_drvdata(ctrl); - if (!test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags)) - return; - - if (test_bit(SERPORT_ACTIVE, &serport->flags)) + if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) && + test_bit(SERPORT_ACTIVE, &serport->flags)) serdev_controller_write_wakeup(ctrl); + + wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT); } static const struct tty_port_client_operations client_ops = { @@ -167,6 +168,14 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable tty_set_termios(tty, &ktermios); } +static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + tty_wait_until_sent(tty, timeout); +} + static const struct serdev_controller_ops ctrl_ops = { .write_buf = ttyport_write_buf, .write_flush = ttyport_write_flush, @@ -175,6 +184,7 @@ static const struct serdev_controller_ops ctrl_ops = { .close = ttyport_close, .set_flow_control = ttyport_set_flow_control, .set_baudrate = ttyport_set_baudrate, + .wait_until_sent = ttyport_wait_until_sent, }; struct device *serdev_tty_port_register(struct tty_port *port, diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 9519da6253a8..a308b206d204 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -81,6 +81,7 @@ struct serdev_controller_ops { void (*close)(struct serdev_controller *); void (*set_flow_control)(struct serdev_controller *, bool); unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int); + void (*wait_until_sent)(struct serdev_controller *, long); }; /** @@ -186,6 +187,7 @@ int serdev_device_open(struct serdev_device *); void serdev_device_close(struct serdev_device *); unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); void serdev_device_set_flow_control(struct serdev_device *, bool); +void serdev_device_wait_until_sent(struct serdev_device *, long); int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t); void serdev_device_write_flush(struct serdev_device *); int serdev_device_write_room(struct serdev_device *); @@ -223,6 +225,7 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev return 0; } static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {} +static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {} static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count) { return -ENODEV; -- cgit v1.2.3 From 5659dab26f09a60db8bd1600e1ce89802fab1c7f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:32 +0200 Subject: serdev: implement get/set tiocm Add method for getting and setting tiocm. Acked-by: Pavel Machek Acked-by: Rob Herring Signed-off-by: Sebastian Reichel Acked-by: Greg Kroah-Hartman Signed-off-by: Marcel Holtmann --- drivers/tty/serdev/core.c | 22 ++++++++++++++++++++++ drivers/tty/serdev/serdev-ttyport.c | 24 ++++++++++++++++++++++++ include/linux/serdev.h | 13 +++++++++++++ 3 files changed, 59 insertions(+) (limited to 'include') diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index a63b74031e22..1e1cbae3a0ea 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -184,6 +184,28 @@ void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout) } EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent); +int serdev_device_get_tiocm(struct serdev_device *serdev) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->get_tiocm) + return -ENOTSUPP; + + return ctrl->ops->get_tiocm(ctrl); +} +EXPORT_SYMBOL_GPL(serdev_device_get_tiocm); + +int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->set_tiocm) + return -ENOTSUPP; + + return ctrl->ops->set_tiocm(ctrl, set, clear); +} +EXPORT_SYMBOL_GPL(serdev_device_set_tiocm); + static int serdev_drv_probe(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index 50dc75c4d204..487c88f6aa0e 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -176,6 +176,28 @@ static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout tty_wait_until_sent(tty, timeout); } +static int ttyport_get_tiocm(struct serdev_controller *ctrl) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!tty->ops->tiocmget) + return -ENOTSUPP; + + return tty->driver->ops->tiocmget(tty); +} + +static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!tty->ops->tiocmset) + return -ENOTSUPP; + + return tty->driver->ops->tiocmset(tty, set, clear); +} + static const struct serdev_controller_ops ctrl_ops = { .write_buf = ttyport_write_buf, .write_flush = ttyport_write_flush, @@ -185,6 +207,8 @@ static const struct serdev_controller_ops ctrl_ops = { .set_flow_control = ttyport_set_flow_control, .set_baudrate = ttyport_set_baudrate, .wait_until_sent = ttyport_wait_until_sent, + .get_tiocm = ttyport_get_tiocm, + .set_tiocm = ttyport_set_tiocm, }; struct device *serdev_tty_port_register(struct tty_port *port, diff --git a/include/linux/serdev.h b/include/linux/serdev.h index a308b206d204..e29a270f603c 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -15,6 +15,7 @@ #include #include +#include struct serdev_controller; struct serdev_device; @@ -82,6 +83,8 @@ struct serdev_controller_ops { void (*set_flow_control)(struct serdev_controller *, bool); unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int); void (*wait_until_sent)(struct serdev_controller *, long); + int (*get_tiocm)(struct serdev_controller *); + int (*set_tiocm)(struct serdev_controller *, unsigned int, unsigned int); }; /** @@ -188,6 +191,8 @@ void serdev_device_close(struct serdev_device *); unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); void serdev_device_set_flow_control(struct serdev_device *, bool); void serdev_device_wait_until_sent(struct serdev_device *, long); +int serdev_device_get_tiocm(struct serdev_device *); +int serdev_device_set_tiocm(struct serdev_device *, int, int); int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t); void serdev_device_write_flush(struct serdev_device *); int serdev_device_write_room(struct serdev_device *); @@ -226,6 +231,14 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev } static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {} static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {} +static inline int serdev_device_get_tiocm(struct serdev_device *serdev) +{ + return -ENOTSUPP; +} +static inline int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) +{ + return -ENOTSUPP; +} static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count) { return -ENODEV; -- cgit v1.2.3 From 756db778748949f6403b727fc6251674dbfcb1a2 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:33 +0200 Subject: serdev: add helpers for cts and rts handling Add serdev helper functions for handling of cts and rts lines using the serdev's tiocm functions. Acked-by: Rob Herring Signed-off-by: Sebastian Reichel Acked-by: Greg Kroah-Hartman Signed-off-by: Marcel Holtmann --- include/linux/serdev.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'include') diff --git a/include/linux/serdev.h b/include/linux/serdev.h index e29a270f603c..37395b8eb8f1 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -16,6 +16,7 @@ #include #include #include +#include struct serdev_controller; struct serdev_device; @@ -254,6 +255,36 @@ static inline int serdev_device_write_room(struct serdev_device *sdev) #endif /* CONFIG_SERIAL_DEV_BUS */ +static inline bool serdev_device_get_cts(struct serdev_device *serdev) +{ + int status = serdev_device_get_tiocm(serdev); + return !!(status & TIOCM_CTS); +} + +static inline int serdev_device_wait_for_cts(struct serdev_device *serdev, bool state, int timeout_ms) +{ + unsigned long timeout; + bool signal; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + while (time_is_after_jiffies(timeout)) { + signal = serdev_device_get_cts(serdev); + if (signal == state) + return 0; + usleep_range(1000, 2000); + } + + return -ETIMEDOUT; +} + +static inline int serdev_device_set_rts(struct serdev_device *serdev, bool enable) +{ + if (enable) + return serdev_device_set_tiocm(serdev, TIOCM_RTS, 0); + else + return serdev_device_set_tiocm(serdev, 0, TIOCM_RTS); +} + /* * serdev hooks into TTY core */ -- cgit v1.2.3