diff mbox

[RFC,6/6] bluetooth: hack up ldisc to use serio

Message ID 20160824232437.9446-8-robh@kernel.org
State New
Headers show

Commit Message

Rob Herring Aug. 24, 2016, 11:24 p.m. UTC
This hacks up the BT ldisc in place to work as a serio driver. It will
need refactoring into common, ldisc, and serio parts.

It is working under QEMU to the point the driver can bind to a serio
device via DT, register as a BT device, start sending out initial
packets and receive data (typed at a terminal). Now I need to find a
real device.

Still need to figure out how to plumb a few tty things like
TTY_DO_WRITE_WAKEUP bit handling and tty_unthrottle.

Signed-off-by: Rob Herring <robh@kernel.org>

---
 drivers/bluetooth/hci_ldisc.c | 261 +++++++++++++++++-------------------------
 drivers/bluetooth/hci_uart.h  |   3 +
 2 files changed, 109 insertions(+), 155 deletions(-)

-- 
2.9.3
diff mbox

Patch

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index dda9739..8149952 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -34,7 +34,9 @@ 
 #include <linux/poll.h>
 
 #include <linux/slab.h>
-#include <linux/tty.h>
+#include <linux/serio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/signal.h>
@@ -138,7 +140,7 @@  int hci_uart_tx_wakeup(struct hci_uart *hu)
 static void hci_uart_write_work(struct work_struct *work)
 {
 	struct hci_uart *hu = container_of(work, struct hci_uart, write_work);
-	struct tty_struct *tty = hu->tty;
+	struct serio *serio = hu->serio;
 	struct hci_dev *hdev = hu->hdev;
 	struct sk_buff *skb;
 
@@ -152,8 +154,8 @@  restart:
 	while ((skb = hci_uart_dequeue(hu))) {
 		int len;
 
-		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-		len = tty->ops->write(tty, skb->data, skb->len);
+//		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+		len = serio_write_buf(serio, skb->data, skb->len);
 		hdev->stat.byte_tx += len;
 
 		skb_pull(skb, len);
@@ -215,17 +217,15 @@  static int hci_uart_open(struct hci_dev *hdev)
 static int hci_uart_flush(struct hci_dev *hdev)
 {
 	struct hci_uart *hu  = hci_get_drvdata(hdev);
-	struct tty_struct *tty = hu->tty;
 
-	BT_DBG("hdev %p tty %p", hdev, tty);
+	BT_DBG("hdev %p serio %p", hdev, hu->serio);
 
 	if (hu->tx_skb) {
 		kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
 	}
 
 	/* Flush any pending characters in the driver and discipline. */
-	tty_ldisc_flush(tty);
-	tty_driver_flush_buffer(tty);
+	serio_write_flush(hu->serio);
 
 	if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
 		hu->proto->flush(hu);
@@ -261,6 +261,8 @@  static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 /* Flow control or un-flow control the device */
 void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
 {
+	serio_set_flow_control(hu->serio, enable);
+#if 0
 	struct tty_struct *tty = hu->tty;
 	struct ktermios ktermios;
 	int status;
@@ -309,6 +311,7 @@  void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
 		BT_DBG("Enabling hardware flow control: %s",
 		       status ? "failed" : "success");
 	}
+#endif
 }
 
 void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
@@ -317,7 +320,7 @@  void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
 	hu->init_speed = init_speed;
 	hu->oper_speed = oper_speed;
 }
-
+#if 0
 void hci_uart_init_tty(struct hci_uart *hu)
 {
 	struct tty_struct *tty = hu->tty;
@@ -336,21 +339,13 @@  void hci_uart_init_tty(struct hci_uart *hu)
 	/* tty_set_termios() return not checked as it is always 0 */
 	tty_set_termios(tty, &ktermios);
 }
-
+#endif
 void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
 {
-	struct tty_struct *tty = hu->tty;
-	struct ktermios ktermios;
-
-	ktermios = tty->termios;
-	ktermios.c_cflag &= ~CBAUD;
-	tty_termios_encode_baud_rate(&ktermios, speed, speed);
-
-	/* tty_set_termios() return not checked as it is always 0 */
-	tty_set_termios(tty, &ktermios);
+	int out_speed = serio_set_baudrate(hu->serio, speed);
 
 	BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name,
-	       tty->termios.c_ispeed, tty->termios.c_ospeed);
+	       speed, out_speed);
 }
 
 static int hci_uart_setup(struct hci_dev *hdev)
@@ -428,83 +423,6 @@  done:
 	return 0;
 }
 
-/* ------ LDISC part ------ */
-/* hci_uart_tty_open
- *
- *     Called when line discipline changed to HCI_UART.
- *
- * Arguments:
- *     tty    pointer to tty info structure
- * Return Value:
- *     0 if success, otherwise error code
- */
-static int hci_uart_tty_open(struct tty_struct *tty)
-{
-	struct hci_uart *hu;
-
-	BT_DBG("tty %p", tty);
-
-	/* Error if the tty has no write op instead of leaving an exploitable
-	   hole */
-	if (tty->ops->write == NULL)
-		return -EOPNOTSUPP;
-
-	hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL);
-	if (!hu) {
-		BT_ERR("Can't allocate control structure");
-		return -ENFILE;
-	}
-
-	tty->disc_data = hu;
-	hu->tty = tty;
-	tty->receive_room = 65536;
-
-	INIT_WORK(&hu->init_ready, hci_uart_init_work);
-	INIT_WORK(&hu->write_work, hci_uart_write_work);
-
-	/* Flush any pending characters in the driver */
-	tty_driver_flush_buffer(tty);
-
-	return 0;
-}
-
-/* hci_uart_tty_close()
- *
- *    Called when the line discipline is changed to something
- *    else, the tty is closed, or the tty detects a hangup.
- */
-static void hci_uart_tty_close(struct tty_struct *tty)
-{
-	struct hci_uart *hu = tty->disc_data;
-	struct hci_dev *hdev;
-
-	BT_DBG("tty %p", tty);
-
-	/* Detach from the tty */
-	tty->disc_data = NULL;
-
-	if (!hu)
-		return;
-
-	hdev = hu->hdev;
-	if (hdev)
-		hci_uart_close(hdev);
-
-	cancel_work_sync(&hu->write_work);
-
-	if (test_and_clear_bit(HCI_UART_PROTO_READY, &hu->flags)) {
-		if (hdev) {
-			if (test_bit(HCI_UART_REGISTERED, &hu->flags))
-				hci_unregister_dev(hdev);
-			hci_free_dev(hdev);
-		}
-		hu->proto->close(hu);
-	}
-	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
-
-	kfree(hu);
-}
-
 /* hci_uart_tty_wakeup()
  *
  *    Callback for transmit wakeup. Called when low level
@@ -513,18 +431,18 @@  static void hci_uart_tty_close(struct tty_struct *tty)
  * Arguments:        tty    pointer to associated tty instance data
  * Return Value:    None
  */
-static void hci_uart_tty_wakeup(struct tty_struct *tty)
+static void hci_uart_serio_wakeup(struct serio *serio)
 {
-	struct hci_uart *hu = tty->disc_data;
+	struct hci_uart *hu = serio_get_drvdata(serio);
 
 	BT_DBG("");
 
 	if (!hu)
 		return;
 
-	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+//	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 
-	if (tty != hu->tty)
+	if (serio != hu->serio)
 		return;
 
 	if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
@@ -543,16 +461,16 @@  static void hci_uart_tty_wakeup(struct tty_struct *tty)
  *
  * Return Value:    None
  */
-static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
-				 char *flags, int count)
+static int hci_uart_serio_receive(struct serio *serio, const u8 *data,
+				   size_t count)
 {
-	struct hci_uart *hu = tty->disc_data;
+	struct hci_uart *hu = serio_get_drvdata(serio);
 
-	if (!hu || tty != hu->tty)
-		return;
+	if (!hu || serio != hu->serio)
+		return 0;
 
 	if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
-		return;
+		return 0;
 
 	/* It does not need a lock here as it is already protected by a mutex in
 	 * tty caller
@@ -562,7 +480,8 @@  static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
 	if (hu->hdev)
 		hu->hdev->stat.byte_rx += count;
 
-	tty_unthrottle(tty);
+//	tty_unthrottle(tty);
+	return count;
 }
 
 static int hci_uart_register_dev(struct hci_uart *hu)
@@ -595,7 +514,7 @@  static int hci_uart_register_dev(struct hci_uart *hu)
 	hdev->flush = hci_uart_flush;
 	hdev->send  = hci_uart_send_frame;
 	hdev->setup = hci_uart_setup;
-	SET_HCIDEV_DEV(hdev, hu->tty->dev);
+	SET_HCIDEV_DEV(hdev, &hu->serio->dev);
 
 	if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
 		set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
@@ -650,6 +569,7 @@  static int hci_uart_set_proto(struct hci_uart *hu, int id)
 
 	return 0;
 }
+#if 0
 
 static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
 {
@@ -733,56 +653,95 @@  static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
 
 	return err;
 }
+#endif
 
-/*
- * We don't provide read/write/poll interface for user space.
- */
-static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
-				 unsigned char __user *buf, size_t nr)
+static int hci_uart_connect(struct serio *serio, struct serio_driver *drv)
 {
-	return 0;
-}
+	int id, ret;
+	struct hci_uart *hu;
+
+	BT_INFO("HCI UART driver ver %s", VERSION);
+
+	id = (int)of_device_get_match_data(&serio->dev);
+
+	hu = devm_kzalloc(&serio->dev, sizeof(struct hci_uart), GFP_KERNEL);
+	if (!hu)
+		return -ENFILE;
+
+	serio_set_drvdata(serio, hu);
+	hu->serio = serio;
+
+//	tty->receive_room = 65536;
+
+	INIT_WORK(&hu->init_ready, hci_uart_init_work);
+	INIT_WORK(&hu->write_work, hci_uart_write_work);
+
+	ret = serio_open(serio, drv);
+	if (ret)
+		return ret;
+
+	set_bit(HCI_UART_PROTO_SET, &hu->flags);
+	ret = hci_uart_set_proto(hu, id);
+	if (ret) {
+		serio_close(serio);
+		return ret;
+	}
+
+	/* Flush any pending characters in the driver */
+//	tty_driver_flush_buffer(tty);
 
-static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
-				  const unsigned char *data, size_t count)
-{
-	return 0;
-}
 
-static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
-				      struct file *filp, poll_table *wait)
-{
 	return 0;
 }
 
-static int __init hci_uart_init(void)
+static void hci_uart_disconnect(struct serio *serio)
 {
-	static struct tty_ldisc_ops hci_uart_ldisc;
-	int err;
+	struct hci_dev *hdev;
+	struct hci_uart *hu = serio_get_drvdata(serio);
 
-	BT_INFO("HCI UART driver ver %s", VERSION);
+	hdev = hu->hdev;
+	if (hdev)
+		hci_uart_close(hdev);
 
-	/* Register the tty discipline */
-
-	memset(&hci_uart_ldisc, 0, sizeof(hci_uart_ldisc));
-	hci_uart_ldisc.magic		= TTY_LDISC_MAGIC;
-	hci_uart_ldisc.name		= "n_hci";
-	hci_uart_ldisc.open		= hci_uart_tty_open;
-	hci_uart_ldisc.close		= hci_uart_tty_close;
-	hci_uart_ldisc.read		= hci_uart_tty_read;
-	hci_uart_ldisc.write		= hci_uart_tty_write;
-	hci_uart_ldisc.ioctl		= hci_uart_tty_ioctl;
-	hci_uart_ldisc.poll		= hci_uart_tty_poll;
-	hci_uart_ldisc.receive_buf	= hci_uart_tty_receive;
-	hci_uart_ldisc.write_wakeup	= hci_uart_tty_wakeup;
-	hci_uart_ldisc.owner		= THIS_MODULE;
-
-	err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
-	if (err) {
-		BT_ERR("HCI line discipline registration failed. (%d)", err);
-		return err;
+	cancel_work_sync(&hu->write_work);
+
+	if (test_and_clear_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+		if (hdev) {
+			if (test_bit(HCI_UART_REGISTERED, &hu->flags))
+				hci_unregister_dev(hdev);
+			hci_free_dev(hdev);
+		}
+		hu->proto->close(hu);
 	}
+	clear_bit(HCI_UART_PROTO_SET, &hu->flags);
+
+	pr_info("hci_uart disconnect!!!\n");
+	serio_close(serio);
+}
+
+
+static const struct of_device_id hci_uart_of_match[] = {
+	{ .compatible = "loopback-uart", .data = (void *)HCI_UART_BCSP },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hci_uart_of_match);
+
+static struct serio_driver serio_hci_uart_drv = {
+	.driver		= {
+		.name	= "hci-uart",
+		.of_match_table = of_match_ptr(hci_uart_of_match),
+	},
+	.description	= "hci uart",
+	.write_wakeup	= hci_uart_serio_wakeup,
+	.receive_buf	= hci_uart_serio_receive,
+	.connect	= hci_uart_connect,
+	.disconnect	= hci_uart_disconnect,
+};
 
+module_serio_driver(serio_hci_uart_drv);
+
+static int __init hci_uart_init(void)
+{
 #ifdef CONFIG_BT_HCIUART_H4
 	h4_init();
 #endif
@@ -816,8 +775,6 @@  static int __init hci_uart_init(void)
 
 static void __exit hci_uart_exit(void)
 {
-	int err;
-
 #ifdef CONFIG_BT_HCIUART_H4
 	h4_deinit();
 #endif
@@ -845,11 +802,6 @@  static void __exit hci_uart_exit(void)
 #ifdef CONFIG_BT_HCIUART_AG6XX
 	ag6xx_deinit();
 #endif
-
-	/* Release tty registration of line discipline */
-	err = tty_unregister_ldisc(N_HCI);
-	if (err)
-		BT_ERR("Can't unregister HCI line discipline (%d)", err);
 }
 
 module_init(hci_uart_init);
@@ -859,4 +811,3 @@  MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_LDISC(N_HCI);
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 839bad1..c48dddc 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -73,8 +73,11 @@  struct hci_uart_proto {
 	struct sk_buff *(*dequeue)(struct hci_uart *hu);
 };
 
+struct serio;
+
 struct hci_uart {
 	struct tty_struct	*tty;
+	struct serio		*serio;
 	struct hci_dev		*hdev;
 	unsigned long		flags;
 	unsigned long		hdev_flags;