On Thu, 2018-04-26 at 11:47 +0200, Marcel Holtmann wrote:
> Hi Sean,
> 
> >>> This adds a driver for the MediaTek serial protocol based on H4 protocol,
> >>> which can enable the built-in Bluetooth device inside MT7622 SoC.
> >>> 
> >>> Signed-off-by: Sean Wang <sean.w...@mediatek.com>
> >>> ---

[... snip ...]

> >>> +
> >>> + /* Start to build a WMT header and its actual payload. */
> >>> + whdr = skb_put(skb, MTK_WMT_HDR_SIZE);
> >>> + whdr->dir = 1;
> >>> + whdr->op = opcode;
> >>> + whdr->dlen = cpu_to_le16(plen + 1);
> >>> + whdr->flag = flag;
> >>> + skb_put_data(skb, param, plen);
> >>> +
> >>> + mtk_enqueue(hu, skb);
> >>> + hci_uart_tx_wakeup(hu);
> >>> +
> >>> + /*
> >>> +  * Waiting a WMT event response, while we must take care in case of
> >>> +  * failures for the wait.
> >>> +  */
> >>> + ret = wait_for_completion_interruptible_timeout(&btdev->wmt_cmd, HZ);
> >>> +
> >>> + return ret > 0 ? 0 : ret < 0 ? ret : -ETIMEDOUT;
> >>> +}
> >> 
> >> All in all I am not convinced that this is super clean. I get that we need 
> >> something special for having this in the ACL data packets, but for the 
> >> standard HCI command I prefer that __hci_cmd_sync is used. I addition, it 
> >> seems that patch download is the only special case and that happens before 
> >> at the setup stage. So we could make things special for that. I need to 
> >> understand this a bit better. Can I get a btmon -w trace.log file from the 
> >> whole init procedure.
> >> 
> >> 
> > 
> > I can try to use __hci_cmd_sync to send those chip-specific hci commands
> > which only can be recognized by the chip, but no meaningful to bluez
> > core.
> > 
> > Is btmon able to capture these cmd/evt between driver and device?
> > 
> > my question's caused by that mtk_wmt_cmd_sync and its events only happen
> > between driver and device,  it doesn't go to core.
> 
> for Intel, Broadcom, Qualcomm, Realtek etc. we use __hci_cmd_sync() in the 
> ->setup() callback and it will show up in btmon. For at least Intel and 
> Broadcom, btmon will decode them as well. You can hint the manufacturer id 
> out of band it will be told to btmon. And that is also how I want it. If it 
> is using HCI as transport, I want it to go through the Bluetooth core.
> 

understood. I will allow chip-specific HCI cmd/evt to go to bluetooth
core.

> >>> +
> >>> +static int mtk_setup_fw(struct hci_uart *hu)
> >>> +{
> >>> + struct mtk_bt_dev *btdev = hu->priv;
> >>> + const struct firmware *fw;
> >>> + struct device *dev;
> >>> + const char *fwname;
> >>> + const u8 *fw_ptr;
> >>> + size_t fw_size;
> >>> + int err, dlen;
> >>> + u8 flag;
> >>> +
> >>> + dev = &hu->serdev->dev;
> >>> + fwname = FIRMWARE_MT7622;
> >>> +
> >>> + init_completion(&btdev->wmt_cmd);
> >>> +
> >>> + err = request_firmware(&fw, fwname, dev);
> >>> + if (err < 0) {
> >>> +         dev_err(dev, "%s: Failed to load firmware file (%d)",
> >>> +                 hu->hdev->name, err);
> >>> +         return err;
> >>> + }
> >>> +
> >>> + fw_ptr = fw->data;
> >>> + fw_size = fw->size;
> >>> +
> >>> + /* The size of a patch header at least has 30 bytes. */
> >>> + if (fw_size < 30)
> >>> +         return -EINVAL;
> >>> +
> >>> + while (fw_size > 0) {
> >>> +         dlen = min_t(int, 1000, fw_size);
> >>> +
> >>> +         /* Tell deivice the position in sequence. */
> >>> +         flag = (fw_size - dlen <= 0) ? 3 :
> >>> +                (fw_size < fw->size) ? 2 : 1;
> >>> +
> >>> +         err = mtk_wmt_cmd_sync(hu, MTK_WMT_PATCH_DWNLD, flag,
> >>> +                                dlen, fw_ptr);
> >>> +         if (err < 0)
> >>> +                 break;
> >>> +
> >>> +         fw_size -= dlen;
> >>> +         fw_ptr += dlen;
> >>> + }
> >>> +
> >>> + release_firmware(fw);
> >>> +
> >>> + return err;
> >>> +}
> >>> +
> >>> +static int mtk_open(struct hci_uart *hu)
> >>> +{
> >>> + struct mtk_bt_dev *btdev = hu->priv;
> >>> + struct device *dev;
> >>> + int err = 0;
> >>> +
> >>> + dev = &hu->serdev->dev;
> >>> +
> >>> + serdev_device_open(hu->serdev);
> >>> + skb_queue_head_init(&btdev->txq);
> >>> +
> >>> + /* Setup the usage of H4. */
> >>> + hu->alignment = 1;
> >>> + hu->padding = 0;
> >>> + mtk_stp_reset(btdev->sp);
> >>> +
> >>> + /* Enable the power domain and clock the device requires */
> >>> + pm_runtime_enable(dev);
> >>> + err = pm_runtime_get_sync(dev);
> >>> + if (err < 0) {
> >>> +         pm_runtime_disable(dev);
> >>> +         return err;
> >>> + }
> >>> +
> >>> + err = clk_prepare_enable(btdev->clk);
> >>> + if (err < 0) {
> >>> +         pm_runtime_put_sync(dev);
> >>> +         pm_runtime_disable(dev);
> >>> + }
> >>> +
> >>> + return err;
> >>> +}
> >>> +
> >>> +static int mtk_close(struct hci_uart *hu)
> >>> +{
> >>> + struct mtk_bt_dev *btdev = hu->priv;
> >>> + struct device *dev = &hu->serdev->dev;
> >>> + u8 param = 0x0;
> >>> +
> >>> + skb_queue_purge(&btdev->txq);
> >>> + kfree_skb(btdev->rx_skb);
> >>> +
> >>> + /* Disable the device. */
> >>> + mtk_wmt_cmd_sync(hu, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param), &param);
> >> 
> >> Wouldn’t this require a enable device in open callback?
> >> 
> > 
> > It's another story ...
> > 
> > At initial time I began working on the driver. I indeed made the
> > mtk_open and mtk_close be consistent, but it's required a few patches
> > for core to solve a problem: 
> > 
> > The problem is that hci_uart resource CAN'T be used in mtk_open to send
> > these specific command taking to the chip since hci_uart_proto->open()
> > would be called much earlier in hci_uart_register_device at which
> > hci_uart is even not being allocated yet. 
> > 
> > So, in the first version I want to make thing be simple, instead, I only
> > hold mtk_open is doing platform stuff such as clock enablement, and
> > power enablement and any other thing about configuring chip all is moved
> > to setup handler.
> 
> You are also not suppose to be sending HCI commands to close the transport in 
> close(). In close() you are suppose to close the transport. You can use 
> shutdown() if there more commands needed. And setup() if for HCI commands 
> after registration.
> 

okay. in the next changes, I will make that 

1. open(), close() would be handling open/close transport and
platform-specific thing such clk, pwr setup

2. setup() would be handling chip-specific commands/firmware download to
configure the chip 

3. shutdown() would be doing the reverse of setup(),which involve a few
chip-specific commands to disable something in that chip


> Using hci_uart has a bit of legacy in it due to the line discipline. Maybe 
> using btuart.c (which I posted) might be a better starting point since then 
> you hook directly into it as a plain HCI driver.
> 
> That all said, I have no issues with extending the core driver framework. I 
> rather do that than having drivers hack around it. That always ends badly.
> 


where could I find the newest btuart.c (which seems cannot be found in
4.17 rc2)? It seems a time to rewrite the driver based on btuart.c

> > 
> >>> +
> >>> + /* Shutdown the clock and power domain the device requires. */
> >>> + clk_disable_unprepare(btdev->clk);
> >>> +
> >>> + pm_runtime_put_sync(dev);
> >>> + pm_runtime_disable(dev);
> >>> +
> >>> + serdev_device_close(hu->serdev);
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +int mtk_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
> >>> +{
> >>> + struct hci_event_hdr *hdr = (void *)skb->data;
> >>> + struct hci_uart *hu = hci_get_drvdata(hdev);
> >>> + struct mtk_bt_dev *btdev = hu->priv;
> >>> +
> >>> + if (hci_skb_pkt_type(skb) == HCI_EVENT_PKT &&
> >>> +     hdr->evt == 0xe4) {
> >>> +         complete(&btdev->wmt_cmd);
> >>> +         kfree_skb(skb);
> >>> +         return 0;
> >>> + }
> >>> +
> >>> + return hci_recv_frame(hdev, skb);
> >>> +}
> >> 
> >> actually this is overdoing it. You can use hci_recv_frame for ACL and SCO 
> >> and just a special one for events. However all in all I question the need 
> >> for the special handling anyway. As I said above, I have the feeling this 
> >> can be done via __hci_cmd_sync or __hci_cmd_sync_ev. So I really like to 
> >> see a btmon -w trace.log for these so that I can see how they look.
> > 
> > in the next version, I would only use a special way for events; ACL and
> > SCO can just use a generic way. 
> > 
> > BTW, add more words for the mtk_recv_frame what it's doing for
> > 
> > * The special handling with hdr->evt 0xe4 in mtk_recv_frame is
> > corresponding to mtk_wmt_cmd_sync.
> > 
> > * It is expected to not to propagate the chip-specific events to bluez
> > core since it's only meaningful data between driver and device.
> 
> No, HCI vendor commands and events can happily go through the core. I 
> actually prefer it that way. Mind you that the core HCI handling is rock 
> solid. So trying to hack around it will just lead to bugs. And no other 
> manufacturer is doing that.



understood. I will allow chip-specific HCI cmd/evt to go to bluetooth
core. The discard for the specific event can be prevented.

> >>> +
> >>> +static const struct h4_recv_pkt mtk_recv_pkts[] = {
> >>> + { H4_RECV_ACL,          .recv = mtk_recv_frame },
> >>> + { H4_RECV_SCO,          .recv = mtk_recv_frame },
> >>> + { H4_RECV_EVENT,        .recv = mtk_recv_frame },
> >>> +};
> >>> +
> >>> +static int mtk_recv(struct hci_uart *hu, const void *data, int count)
> >>> +{
> >>> + const unsigned char *p_left = data, *p_h4;
> >>> + struct mtk_bt_dev *btdev = hu->priv;
> >>> + int sz_left = count, sz_h4, adv;
> >>> + struct device *dev;
> >>> + int err;
> >>> +
> >>> + if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
> >>> +         return -EUNATCH;
> >>> +
> >>> + dev = &hu->serdev->dev;
> >>> +
> >>> + while (sz_left > 0) {
> >>> +         p_h4 = mtk_stp_split(dev, btdev->sp, p_left, sz_left, &sz_h4);
> >>> +         if (!p_h4)
> >>> +                 break;
> >> 
> >> Now this is troubling me a bit. While there are some H:4 frames somewhere 
> >> in here, you need to do another set of framing. So what is the framing 
> >> that comes from the serial part in the first place?
> >> 
> > 
> > the chip always adds a fews bytes before and after the H:4 data, it's
> > something like that.
> > 
> > -------------------------------------  
> > | STP header  |  H:4   | STP tailer |  
> > -------------------------------------  
> > 
> > mtk_stp_split no looked into the details of H:4, only is used to find
> > where is the start of H:4 part and keep info. from STP header and then
> > feed the H:4 part into h4_recv_buf to reuse the extent logic to process
> > HCI/ACL/SCO pkts. So, I think it should be unnecessary to create a new
> > framing for the current usage.
> 
> I would disagree right now. I think taking btuart.c and writing your own 
> driver might be a lot simpler. You have less legacy and you have less things 
> to hack around.
> 
> If you have framing around H:4 packets anyway, then why bother trying to use 
> h4_recv_buf anyway (which you could even from a separate driver, see 
> bpa10x.c). The reason behind h4_recv_buf is that we have no framing and need 
> to understand H:4 to find the end of a packet. If you know it, then there is 
> not point in that (see bfusb.c driver).
> 

Thanks for the advice. I will check with btuart.c first and to see if I
have a clean and better solution for the parse logic.

> >> Btw. it is fine if hci_uart is not a good fit. I actually think it is a 
> >> bad fit and using the btuart driver I send around a few weeks ago is a 
> >> better starting point. However if the framing itself is more elaborate 
> >> actually, then even a brand new driver and just re-using h4_recv.h would 
> >> be good. If however the extra framing already does a real framing job, 
> >> then even h4_recv.h is useless and we can just pick the frames right out 
> >> of serial device. For example bfusb.c had such an extra framing on top of 
> >> H:4.
> > 
> > 
> > 
> >>> +
> >>> +         adv = p_h4 - p_left;
> >>> +         sz_left -= adv;
> >>> +         p_left += adv;
> >>> +
> >>> +         btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb,
> >>> +                                     p_h4, sz_h4, mtk_recv_pkts,
> >>> +                                     ARRAY_SIZE(mtk_recv_pkts));
> >>> +         if (IS_ERR(btdev->rx_skb)) {
> >>> +                 err = PTR_ERR(btdev->rx_skb);
> >>> +                 dev_err(dev, "Frame reassembly failed (%d)", err);
> >>> +                 btdev->rx_skb = NULL;
> >>> +                 return err;
> >>> +         }
> >>> +
> >>> +         sz_left -= sz_h4;
> >>> +         p_left += sz_h4;
> >>> + }
> >>> +
> >>> + return count;
> >>> +}
> >>> +
> >>> +static struct sk_buff *mtk_dequeue(struct hci_uart *hu)
> >>> +{
> >>> + struct mtk_bt_dev *btdev = hu->priv;
> >>> +
> >>> + return skb_dequeue(&btdev->txq);
> >>> +}
> >>> +
> >>> +static int mtk_flush(struct hci_uart *hu)
> >>> +{
> >>> + struct mtk_bt_dev *btdev = hu->priv;
> >>> +
> >>> + skb_queue_purge(&btdev->txq);
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int mtk_setup(struct hci_uart *hu)
> >>> +{
> >>> + struct device *dev;
> >>> + u8 param = 0x1;
> >>> + int err;
> >>> +
> >>> + dev = &hu->serdev->dev;
> >>> +
> >>> + /* Setup a firmware which the device definitely requires. */
> >>> + err = mtk_setup_fw(hu);
> >>> + if (err < 0) {
> >>> +         dev_err(dev, "Fail at setup FW (%d): %d", __LINE__, err);
> >>> +         return err;
> >>> + }
> >>> +
> >>> + /* Activate funciton the firmware provides to. */
> >>> + err = mtk_wmt_cmd_sync(hu, MTK_WMT_RST, 0x4, 0, 0);
> >>> + if (err < 0) {
> >>> +         dev_err(dev, "Fail at WMT RST (%d): %d", __LINE__, err);
> >>> +         return err;
> >>> + }
> >>> +
> >>> + /* Enable Bluetooth protocol. */
> >>> + err = mtk_wmt_cmd_sync(hu, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param),
> >>> +                        &param);
> >>> + if (err < 0)
> >>> +         dev_err(dev, "Fail at FUNC CTRL (%d): %d", __LINE__, err);
> >> 
> >> Just as a reminder, setup callback is only called once. You should enable 
> >> the transport in open callback.
> >> 
> > 
> > If capable, I would like to move all chip initialization stuff to
> > mtk_open and de-initialization stuff to mtk_close.
> 
> So open/close is for establishing the HCI transport. Sending commands there 
> is something we have not yet encountered.
> 
> > In that way, a user power up/down with bluetoothctl or hcitool can
> > completely recover the chip from any fatal errors. and even it seem at
> > bluez core, there's workqueue in charge of recovery thing.
> > 
> > But the core seems not supports me to move chip handling in
> > mtk_open :-( 
> 
> What do you need for your driver. On every power on, you need to execute a 
> HCI command to enable the chip? Do you need to re-load the firmware or does 
> it survive a power down/up cycle?
> 

the Bluetooth device can't survive in a power/down cycle and

* power on should be including
  - enable clk and power domain
  - download firmware through specific ACL command
  - send specific commands to configure bluetooth (Required to note that
the steps should be after downloading firmware because the behavior for
the command might be changed by the firmware)


* power off should be including
  - send specific commands, such as to disable bluetooth
  - disable power domain and clk

> > 
> >>> +
> >>> + return err;
> >>> +}
> >>> +
> >>> +static const struct hci_uart_proto mtk_proto = {
> >>> + .id             = HCI_UART_MTK,
> >>> + .name           = "MediaTek",
> >>> + .open           = mtk_open,
> >>> + .close          = mtk_close,
> >>> + .recv           = mtk_recv,
> >>> + .enqueue        = mtk_enqueue,
> >>> + .dequeue        = mtk_dequeue,
> >>> + .flush          = mtk_flush,
> >>> + .setup          = mtk_setup,
> >>> + .manufacturer   = 1,
> >> 
> >> Unless you are Nokia, this id is wrong.
> >> 
> > 
> > okay, i will remove it 
> > 
> >>> +};
> >>> +
> >>> +static int mtk_bluetooth_serdev_probe(struct serdev_device *serdev)
> >>> +{
> >>> + struct device *dev = &serdev->dev;
> >>> + struct mtk_bt_dev *btdev;
> >>> + int err = 0;
> >>> +
> >>> + btdev = devm_kzalloc(dev, sizeof(*btdev), GFP_KERNEL);
> >>> + if (!btdev)
> >>> +         return -ENOMEM;
> >>> +
> >>> + btdev->sp = devm_kzalloc(dev, sizeof(*btdev->sp), GFP_KERNEL);
> >>> + if (!btdev->sp)
> >>> +         return -ENOMEM;
> >>> +
> >>> + btdev->clk = devm_clk_get(dev, "ref");
> >>> + if (IS_ERR(btdev->clk))
> >>> +         return PTR_ERR(btdev->clk);
> >>> +
> >>> + btdev->hu.serdev = serdev;
> >>> + btdev->hu.priv = btdev;
> >>> +
> >>> + serdev_device_set_drvdata(serdev, btdev);
> >>> +
> >>> + err = hci_uart_register_device(&btdev->hu, &mtk_proto);
> >>> + if (err)
> >>> +         dev_err(dev, "Could not register bluetooth uart: %d", err);
> >>> +
> >>> + return err;
> >>> +}
> >>> +
> >>> +static void mtk_bluetooth_serdev_remove(struct serdev_device *serdev)
> >>> +{
> >>> + struct mtk_bt_dev *btdev = serdev_device_get_drvdata(serdev);
> >>> +
> >>> + hci_uart_unregister_device(&btdev->hu);
> >>> +}
> >>> +
> >>> +static const struct of_device_id mtk_bluetooth_of_match[] = {
> >>> + { .compatible = "mediatek,mt7622-bluetooth" },
> >>> + {},
> >>> +};
> >>> +MODULE_DEVICE_TABLE(of, mtk_bluetooth_of_match);
> >>> +
> >>> +static struct serdev_device_driver mtk_bluetooth_serdev_driver = {
> >>> + .probe = mtk_bluetooth_serdev_probe,
> >>> + .remove = mtk_bluetooth_serdev_remove,
> >>> + .driver = {
> >>> +         .name = "mediatek-bluetooth",
> >>> +         .of_match_table = of_match_ptr(mtk_bluetooth_of_match),
> >>> + },
> >>> +};
> >>> +
> >>> +module_serdev_device_driver(mtk_bluetooth_serdev_driver);
> >>> +
> >>> +MODULE_AUTHOR("Sean Wang <sean.w...@mediatek.com>");
> >>> +MODULE_DESCRIPTION("Bluetooth HCI Serial driver for MediaTek SoC");
> >>> +MODULE_LICENSE("GPL v2");
> >>> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
> >>> index 66e8c68..3f0911e 100644
> >>> --- a/drivers/bluetooth/hci_uart.h
> >>> +++ b/drivers/bluetooth/hci_uart.h
> >>> @@ -35,7 +35,7 @@
> >>> #define HCIUARTGETFLAGS           _IOR('U', 204, int)
> >>> 
> >>> /* UART protocols */
> >>> -#define HCI_UART_MAX_PROTO       12
> >>> +#define HCI_UART_MAX_PROTO       13
> >>> 
> >>> #define HCI_UART_H4       0
> >>> #define HCI_UART_BCSP     1
> >>> @@ -49,6 +49,7 @@
> >>> #define HCI_UART_AG6XX    9
> >>> #define HCI_UART_NOKIA    10
> >>> #define HCI_UART_MRVL     11
> >>> +#define HCI_UART_MTK     12
> >> 
> >> I am really trying to avoid new id assignments here and go for serdev only 
> >> drivers.
> >> 
> > 
> > it seems no necessary for a serdev device. can i remove it from driver?
> > but what id should be filled in hci_uart_proto ?
> > 
> > 430 static const struct hci_uart_proto mtk_proto = {
> > 431         .id             = HCI_UART_MTK,
> > 432         .name           = "MediaTek",
> > 433         .open           = mtk_open,
> 
> We can work on something to allow an unassigned id here, but I still feel 
> that you should go for a separate driver in the first place.
> 
> If you only have a hammer, then everything looks like a nail. hci_uart being 
> the hammer here. I think you would be better served with starting with 
> btuart.c and then evolve it into your own driver. Try that and see where your 
> problems are.
> 

Okay, lets see whether the addressed problems are still present on 2n
version based on btuart.c to evolve

> Regards
> 
> Marcel
> 


Reply via email to