Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Rob Herring | 1245 | 86.82% | 1 | 7.69% |
Hans de Goede | 78 | 5.44% | 3 | 23.08% |
Ian Molton | 51 | 3.56% | 1 | 7.69% |
Balakrishna Godavarthi | 24 | 1.67% | 2 | 15.38% |
Sebastian Reichel | 15 | 1.05% | 2 | 15.38% |
Marcel Holtmann | 9 | 0.63% | 1 | 7.69% |
Lukas Wunner | 8 | 0.56% | 1 | 7.69% |
Colin Ian King | 2 | 0.14% | 1 | 7.69% |
Thomas Gleixner | 2 | 0.14% | 1 | 7.69% |
Total | 1434 | 13 |
// SPDX-License-Identifier: GPL-2.0-or-later /* * Bluetooth HCI serdev driver lib * * Copyright (C) 2017 Linaro, Ltd., Rob Herring <robh@kernel.org> * * Based on hci_ldisc.c: * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/serdev.h> #include <linux/skbuff.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include "hci_uart.h" static struct serdev_device_ops hci_serdev_client_ops; static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) { struct hci_dev *hdev = hu->hdev; /* Update HCI stat counters */ switch (pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; break; case HCI_ACLDATA_PKT: hdev->stat.acl_tx++; break; case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; } } static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) { struct sk_buff *skb = hu->tx_skb; if (!skb) { if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) skb = hu->proto->dequeue(hu); } else hu->tx_skb = NULL; return skb; } static void hci_uart_write_work(struct work_struct *work) { struct hci_uart *hu = container_of(work, struct hci_uart, write_work); struct serdev_device *serdev = hu->serdev; struct hci_dev *hdev = hu->hdev; struct sk_buff *skb; /* REVISIT: * should we cope with bad skbs or ->write() returning an error value? */ do { clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); while ((skb = hci_uart_dequeue(hu))) { int len; len = serdev_device_write_buf(serdev, skb->data, skb->len); hdev->stat.byte_tx += len; skb_pull(skb, len); if (skb->len) { hu->tx_skb = skb; break; } hci_uart_tx_complete(hu, hci_skb_pkt_type(skb)); kfree_skb(skb); } } while (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)); clear_bit(HCI_UART_SENDING, &hu->tx_state); } /* ------- Interface to HCI layer ------ */ /* Reset device */ static int hci_uart_flush(struct hci_dev *hdev) { struct hci_uart *hu = hci_get_drvdata(hdev); BT_DBG("hdev %p serdev %p", hdev, hu->serdev); if (hu->tx_skb) { kfree_skb(hu->tx_skb); hu->tx_skb = NULL; } /* Flush any pending characters in the driver and discipline. */ serdev_device_write_flush(hu->serdev); if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) hu->proto->flush(hu); return 0; } /* Initialize device */ static int hci_uart_open(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); /* Undo clearing this from hci_uart_close() */ hdev->flush = hci_uart_flush; return 0; } /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { BT_DBG("hdev %p", hdev); hci_uart_flush(hdev); hdev->flush = NULL; return 0; } /* Send frames from HCI layer */ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_uart *hu = hci_get_drvdata(hdev); BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb), skb->len); hu->proto->enqueue(hu, skb); hci_uart_tx_wakeup(hu); return 0; } static int hci_uart_setup(struct hci_dev *hdev) { struct hci_uart *hu = hci_get_drvdata(hdev); struct hci_rp_read_local_version *ver; struct sk_buff *skb; unsigned int speed; int err; /* Init speed if any */ if (hu->init_speed) speed = hu->init_speed; else if (hu->proto->init_speed) speed = hu->proto->init_speed; else speed = 0; if (speed) serdev_device_set_baudrate(hu->serdev, speed); /* Operational speed if any */ if (hu->oper_speed) speed = hu->oper_speed; else if (hu->proto->oper_speed) speed = hu->proto->oper_speed; else speed = 0; if (hu->proto->set_baudrate && speed) { err = hu->proto->set_baudrate(hu, speed); if (err) bt_dev_err(hdev, "Failed to set baudrate"); else serdev_device_set_baudrate(hu->serdev, speed); } if (hu->proto->setup) return hu->proto->setup(hu); if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags)) return 0; skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading local version info failed (%ld)", PTR_ERR(skb)); return 0; } if (skb->len != sizeof(*ver)) bt_dev_err(hdev, "Event length mismatch for version info"); kfree_skb(skb); return 0; } /** hci_uart_write_wakeup - transmit buffer wakeup * @serdev: serial device * * This function is called by the serdev framework when it accepts * more data being sent. */ static void hci_uart_write_wakeup(struct serdev_device *serdev) { struct hci_uart *hu = serdev_device_get_drvdata(serdev); BT_DBG(""); if (!hu || serdev != hu->serdev) { WARN_ON(1); return; } if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) hci_uart_tx_wakeup(hu); } /** hci_uart_receive_buf - receive buffer wakeup * @serdev: serial device * @data: pointer to received data * @count: count of received data in bytes * * This function is called by the serdev framework when it received data * in the RX buffer. * * Return: number of processed bytes */ static int hci_uart_receive_buf(struct serdev_device *serdev, const u8 *data, size_t count) { struct hci_uart *hu = serdev_device_get_drvdata(serdev); if (!hu || serdev != hu->serdev) { WARN_ON(1); return 0; } if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) return 0; /* It does not need a lock here as it is already protected by a mutex in * tty caller */ hu->proto->recv(hu, data, count); if (hu->hdev) hu->hdev->stat.byte_rx += count; return count; } static struct serdev_device_ops hci_serdev_client_ops = { .receive_buf = hci_uart_receive_buf, .write_wakeup = hci_uart_write_wakeup, }; int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p) { int err; struct hci_dev *hdev; BT_DBG(""); serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops); err = serdev_device_open(hu->serdev); if (err) return err; err = p->open(hu); if (err) goto err_open; hu->proto = p; set_bit(HCI_UART_PROTO_READY, &hu->flags); /* Initialize and register HCI device */ hdev = hci_alloc_dev(); if (!hdev) { BT_ERR("Can't allocate HCI device"); err = -ENOMEM; goto err_alloc; } hu->hdev = hdev; hdev->bus = HCI_UART; hci_set_drvdata(hdev, hu); INIT_WORK(&hu->init_ready, hci_uart_init_work); INIT_WORK(&hu->write_work, hci_uart_write_work); percpu_init_rwsem(&hu->proto_lock); /* Only when vendor specific setup callback is provided, consider * the manufacturer information valid. This avoids filling in the * value for Ericsson when nothing is specified. */ if (hu->proto->setup) hdev->manufacturer = hu->proto->manufacturer; hdev->open = hci_uart_open; hdev->close = hci_uart_close; hdev->flush = hci_uart_flush; hdev->send = hci_uart_send_frame; hdev->setup = hci_uart_setup; SET_HCIDEV_DEV(hdev, &hu->serdev->dev); if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags)) set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) hdev->dev_type = HCI_AMP; else hdev->dev_type = HCI_PRIMARY; if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) return 0; if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); err = -ENODEV; goto err_register; } set_bit(HCI_UART_REGISTERED, &hu->flags); return 0; err_register: hci_free_dev(hdev); err_alloc: clear_bit(HCI_UART_PROTO_READY, &hu->flags); p->close(hu); err_open: serdev_device_close(hu->serdev); return err; } EXPORT_SYMBOL_GPL(hci_uart_register_device); void hci_uart_unregister_device(struct hci_uart *hu) { struct hci_dev *hdev = hu->hdev; clear_bit(HCI_UART_PROTO_READY, &hu->flags); hci_unregister_dev(hdev); hci_free_dev(hdev); cancel_work_sync(&hu->write_work); hu->proto->close(hu); serdev_device_close(hu->serdev); } EXPORT_SYMBOL_GPL(hci_uart_unregister_device);
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1