/**************************************************************************** * drivers/wireless/bluetooth/bt_uart_shim.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Private Types ****************************************************************************/ /* This structure is the variable state of the binding to the UART */ struct hciuart_state_s { /* Registered Rx callback */ btuart_rxcallback_t callback; /* Rx callback function */ FAR void *arg; /* Rx callback argument */ struct file f; /* File structure */ struct pollfd p; /* Poll structure */ }; struct hciuart_config_s { /* Setup the interface from the upper to the lower */ struct btuart_lowerhalf_s lower; /* Generic UART lower half */ struct hciuart_state_s state; /* Variable state */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* UART Lower-Half Methods */ static void hciuart_rxattach(FAR const struct btuart_lowerhalf_s *lower, btuart_rxcallback_t callback, FAR void *arg); static void hciuart_rxpollcb(FAR struct pollfd *fds); static void hciuart_rxenable(FAR const struct btuart_lowerhalf_s *lower, bool enable); static int hciuart_setbaud(FAR const struct btuart_lowerhalf_s *lower, uint32_t baud); static ssize_t hciuart_read(FAR const struct btuart_lowerhalf_s *lower, FAR void *buffer, size_t buflen); static ssize_t hciuart_write(FAR const struct btuart_lowerhalf_s *lower, FAR const void *buffer, size_t buflen); static ssize_t hciuart_rxdrain(FAR const struct btuart_lowerhalf_s *lower); static int hciuart_ioctl(FAR const struct btuart_lowerhalf_s *lower, int cmd, unsigned long arg); /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: hciuart_rxattach * * Description: * Attach/detach the upper half Rx callback. * * rxattach() allows the upper half logic to attach a callback function * that will be used to inform the upper half that an Rx frame is * available. This callback will, most likely, be invoked in the * context of an interrupt callback. The receive() method should then * be invoked in order to receive the obtain the Rx frame data. * ****************************************************************************/ static void hciuart_rxattach(FAR const struct btuart_lowerhalf_s *lower, btuart_rxcallback_t callback, FAR void *arg) { struct hciuart_config_s *config = (FAR struct hciuart_config_s *)lower; struct hciuart_state_s *state; irqstate_t flags; state = &config->state; /* If the callback is NULL, then we are detaching */ flags = spin_lock_irqsave(NULL); if (callback == NULL) { /* Disable Rx callbacks and detach the Rx callback */ state->callback = NULL; state->arg = NULL; } /* Otherwise, we are attaching */ else { state->arg = arg; state->callback = callback; } spin_unlock_irqrestore(NULL, flags); } /**************************************************************************** * Name: hciuart_rxpollcb * * Description: * Callback to receive the UART driver POLLIN notification. * ****************************************************************************/ static void hciuart_rxpollcb(FAR struct pollfd *fds) { FAR struct hciuart_config_s *n = (FAR struct hciuart_config_s *)fds->arg; FAR struct hciuart_state_s *s = &n->state; if (fds->revents & POLLIN) { fds->revents = 0; if (s->callback != NULL) { wlinfo("Activating callback\n"); s->callback(&n->lower, s->arg); } else { wlwarn("Dropping data (no CB)\n"); hciuart_rxdrain(&n->lower); } } } /**************************************************************************** * Name: hciuart_rxenable * * Description: * Enable/disable RX callbacks from the HCI UART. * * hciuart_rxenable() may be used to enable or disable callback events. * This probably translates to enabling and disabled Rx interrupts at * the UART. NOTE: Rx event notification should be done sparingly: * Rx data overrun may occur when Rx events are disabled! * ****************************************************************************/ static void hciuart_rxenable(FAR const struct btuart_lowerhalf_s *lower, bool enable) { FAR struct hciuart_config_s *config = (FAR struct hciuart_config_s *)lower; FAR struct hciuart_state_s *s = &config->state; if (enable != !!s->p.priv) { file_poll(&s->f, &s->p, enable); } } /**************************************************************************** * Name: hciuart_setbaud * * Description: * The HCI UART comes up with some initial BAUD rate. Some support * auto-BAUD detection, some support writing a configuration file to * select the initial BAUD. The simplest strategy, however, is simply * to use the HCI UART's default initial BAUD to perform the basic * bring up, then send a vendor-specific command to increase the HCI * UARTs BAUD. This method then may be used to adjust the lower half * driver to the new HCI UART BAUD. * ****************************************************************************/ static int hciuart_setbaud(FAR const struct btuart_lowerhalf_s *lower, uint32_t baud) { #ifdef CONFIG_SERIAL_TERMIOS struct termios tio; int ret; ret = hciuart_ioctl(lower, TCGETS, (unsigned long)&tio); if (ret) { wlerr("ERROR during TCGETS\n"); return ret; } if (baud != 0) { cfsetspeed(&tio, baud); } /* To be a H4 interface, CTS/RTS are needed */ tio.c_cflag |= CRTS_IFLOW | CCTS_OFLOW; ret = hciuart_ioctl(lower, TCSETS, (unsigned long)&tio); if (ret) { wlerr("ERROR during TCSETS, does UART support CTS/RTS?\n"); return ret; } return OK; #else return -ENOSYS; #endif } /**************************************************************************** * Name: hciuart_read * * Description: * Read UART data. * * hciuart_read() after receipt of a callback notifying the upper half of * the availability of Rx frame, the upper half may call the receive() * method in order to obtain the buffered Rx frame data. * ****************************************************************************/ static ssize_t hciuart_read(FAR const struct btuart_lowerhalf_s *lower, FAR void *buffer, size_t buflen) { FAR struct hciuart_config_s *config = (FAR struct hciuart_config_s *)lower; FAR struct hciuart_state_s *state = &config->state; wlinfo("config %p buffer %p buflen %zu\n", config, buffer, buflen); /* NOTE: This assumes that the caller has exclusive access to the Rx * buffer, i.e., one lower half instance can server only one upper half! */ return file_read(&state->f, buffer, buflen); } /**************************************************************************** * Name: hciuart_write * * Description: * Write UART data. * * hciuart_write() will add the outgoing frame to the Tx buffer and will * return immediately. This function may block only in the event that * there is insufficient buffer space to hold the Tx frame data. In that * case the lower half will block until there is sufficient to buffer * the entire outgoing packet. * ****************************************************************************/ static ssize_t hciuart_write(FAR const struct btuart_lowerhalf_s *lower, FAR const void *buffer, size_t buflen) { FAR struct hciuart_config_s *config = (FAR struct hciuart_config_s *)lower; FAR struct hciuart_state_s *state = &config->state; wlinfo("config %p buffer %p buflen %zu\n", config, buffer, buflen); return file_write(&state->f, buffer, buflen); } /**************************************************************************** * Name: hciuart_rxdrain * * Description: * Flush/drain all buffered RX data * ****************************************************************************/ static ssize_t hciuart_rxdrain(FAR const struct btuart_lowerhalf_s *lower) { return hciuart_ioctl(lower, TCDRN, 0); } /**************************************************************************** * Name: hciuart_ioctl ****************************************************************************/ static int hciuart_ioctl(FAR const struct btuart_lowerhalf_s *lower, int cmd, unsigned long arg) { FAR struct hciuart_config_s *config = (FAR struct hciuart_config_s *)lower; FAR struct hciuart_state_s *s = &config->state; return file_ioctl(&s->f, cmd, arg); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: btuart_shim_getdevice * * Description: * Get a pointer to the device that will be used to communicate with the * regular serial port on the HCI. * * Input Parameters: * Entry in filesystem hierarchy for device * * Returned Value: * Pointer to device interface * ****************************************************************************/ FAR struct btuart_lowerhalf_s *btuart_shim_getdevice(FAR const char *path) { FAR struct hciuart_config_s *n; FAR struct hciuart_state_s *s; int ret; /* Get the memory for this shim instance */ n = (FAR struct hciuart_config_s *) kmm_zalloc(sizeof(struct hciuart_config_s)); if (!n) { return NULL; } s = &n->state; ret = file_open(&s->f, path, O_RDWR | O_CLOEXEC); if (ret < 0) { kmm_free(n); return NULL; } /* Setup poll structure */ s->p.events = POLLIN; s->p.arg = n; s->p.cb = hciuart_rxpollcb; /* Hook the routines in */ n->lower.rxattach = hciuart_rxattach; n->lower.rxenable = hciuart_rxenable; n->lower.setbaud = hciuart_setbaud; n->lower.read = hciuart_read; n->lower.write = hciuart_write; n->lower.rxdrain = hciuart_rxdrain; n->lower.ioctl = hciuart_ioctl; return (FAR struct btuart_lowerhalf_s *)n; }