/**************************************************************************** * drivers/serial/uart_bth5.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 /**************************************************************************** * Private Types ****************************************************************************/ #define MAX_OPENCNT (255) /* Limit of uint8_t */ #define HCI_3WIRE_ACK_PKT 0x00 #define HCI_COMMAND_PKT 0x01 #define HCI_ACLDATA_PKT 0x02 #define HCI_SCODATA_PKT 0x03 #define HCI_EVENT_PKT 0x04 #define HCI_ISODATA_PKT 0x05 #define HCI_3WIRE_LINK_PKT 0x0f #define HCI_VENDOR_PKT 0xff #define SLIP_DELIMITER 0xc0 #define SLIP_ESC 0xdb #define SLIP_ESC_DELIM 0xdc #define SLIP_ESC_ESC 0xdd #define H5_BIT_RX_ESC (0x00000001 << 0) #define H5_BIT_RX_CRC (0x00000001 << 1) #define H5_HDR_SEQ(hdr) ((hdr)[0] & 0x07) #define H5_HDR_ACK(hdr) (((hdr)[0] >> 3) & 0x07) #define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01) #define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01) #define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f) #define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0x0f) + ((hdr)[2] << 4)) #define H5_SET_SEQ(hdr, seq) ((hdr)[0] |= (seq)) #define H5_SET_ACK(hdr, ack) ((hdr)[0] |= (ack) << 3) #define H5_SET_RELIABLE(hdr) ((hdr)[0] |= 1 << 7) #define H5_SET_TYPE(hdr, type) ((hdr)[1] |= (type)) #define H5_SET_LEN(hdr, len) (((hdr)[1] |= ((len)&0x0f) << 4), ((hdr)[2] |= (len) >> 4)) #define H5_ACK_TIMEOUT MSEC2TICK(250) /* 250ms */ #define H5_RTX_TIMEOUT MSEC2TICK(150) /* 150ms */ union bt_hdr_u { struct bt_hci_cmd_hdr_s cmd; struct bt_hci_acl_hdr_s acl; struct bt_hci_evt_hdr_s evt; struct bt_hci_iso_hdr_s iso; }; enum { H5_MSG_INVALID, H5_MSG_SYNC_REQ, H5_MSG_SYNC_RSP, H5_MSG_CONF_REQ, H5_MSG_CONF_RSP, }; struct unack_pool_s { size_t start; size_t end; size_t size; uint8_t buf[CONFIG_UART_BTH5_TXWIN][CONFIG_UART_BTH5_TXBUFSIZE]; }; struct uart_bth5_s { FAR struct bt_driver_s *drv; struct circbuf_s circbuf; uint8_t sendbuf[CONFIG_UART_BTH5_TXBUFSIZE]; uint8_t recvbuf[CONFIG_UART_BTH5_TXBUFSIZE * 2]; bool crcvalid; uint8_t openrefs; uint16_t crcvalue; unsigned long flags; size_t sendlen; /* sendbuffer hci data len */ size_t recvlen; size_t rxpending; /* Expecting more bytes */ uint8_t rxack; /* Last ack number received */ uint8_t txseq; /* Next seq number to send */ uint8_t txack; /* Next ack number to send */ uint8_t txwin; /* Sliding window size */ mutex_t openlock; mutex_t sendlock; mutex_t recvlock; sem_t opensem; sem_t recvsem; sem_t acksem; struct work_s retxworker; struct work_s ackworker; struct unack_pool_s unackpool; CODE int (*rxfunc)(FAR struct uart_bth5_s *dev, uint8_t c); enum { H5_UNINITIALIZED, H5_INITIALIZED, H5_ACTIVE, } state; FAR struct pollfd *fds[CONFIG_UART_BTH5_NPOLLWAITERS]; }; struct unack_frame_s { enum bt_buf_type_e type; size_t pktlen; uint8_t data[1]; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int uart_bth5_open(FAR struct file *filep); static int uart_bth5_close(FAR struct file *filep); static ssize_t uart_bth5_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t uart_bth5_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static int uart_bth5_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static void uart_bth5_post(FAR sem_t *sem); static int uart_bth5_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); static void uart_bth5_pollnotify(FAR struct uart_bth5_s *dev, pollevent_t eventset); static void h5_rx_reset(FAR struct uart_bth5_s *dev); static int uart_h5_send(FAR struct uart_bth5_s *dev, uint8_t type, FAR const uint8_t *payload, size_t len); static void h5_ack_work(FAR void *arg); static void h5_retx_work(FAR void *arg); /**************************************************************************** * Private Data ****************************************************************************/ static const struct file_operations g_uart_bth5_ops = { uart_bth5_open, /* open */ uart_bth5_close, /* close */ uart_bth5_read, /* read */ uart_bth5_write, /* write */ NULL, /* seek */ uart_bth5_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ uart_bth5_poll /* poll */ }; /**************************************************************************** * Private Functions ****************************************************************************/ static void h5_peer_reset(FAR struct uart_bth5_s *dev) { dev->state = H5_UNINITIALIZED; dev->txseq = 0; dev->txack = 0; work_cancel(HPWORK, &dev->retxworker); work_cancel(HPWORK, &dev->ackworker); } static uint8_t h5_crc_rev8(uint8_t byte) { static uint8_t rev8table[256] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff }; return rev8table[byte]; } static uint16_t h5_crc_rev16(uint16_t x) { return (h5_crc_rev8(x & 0xff) << 8) | h5_crc_rev8(x >> 8); } static void h5_crc_update(FAR uint16_t *crc, uint8_t d) { uint16_t reg; static const uint16_t crctable[16] = { 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, 0x8408, 0x9489, 0xa50a, 0xb58b, 0xc60c, 0xd68d, 0xe70e, 0xf78f }; reg = *crc; reg = (reg >> 4) ^ crctable[(reg ^ d) & 0x000f]; reg = (reg >> 4) ^ crctable[(reg ^ (d >> 4)) & 0x000f]; *crc = reg; } static uint16_t h5_get_crc(FAR struct uart_bth5_s *dev) { uint8_t *hdr = dev->recvbuf; uint8_t *data = hdr + 4 + H5_HDR_LEN(hdr); return data[1] + (data[0] << 8); } static void h5_unack_init(FAR struct unack_pool_s *pool) { pool->start = 0; pool->end = 0; pool->size = 0; } static FAR void * h5_unack_ctor(FAR struct unack_pool_s *pool) { FAR void *p; p = pool->buf[pool->start++]; pool->start %= CONFIG_UART_BTH5_TXWIN; pool->size++; return p; } static FAR void * h5_unack_dtor(FAR struct unack_pool_s *pool) { FAR void *p; p = pool->buf[pool->end++]; pool->end %= CONFIG_UART_BTH5_TXWIN; pool->size--; return p; } static size_t h5_unack_size(FAR struct unack_pool_s *pool) { return pool->size; } static void h5_unack_cleanup(FAR struct unack_pool_s *pool) { h5_unack_init(pool); } static void h5_link_control(FAR struct uart_bth5_s *dev, FAR const void *data, size_t len) { uart_h5_send(dev, HCI_3WIRE_LINK_PKT, data, len); } static void h5_message_handle(FAR struct uart_bth5_s *dev) { FAR const uint8_t *hdr = dev->recvbuf; FAR const uint8_t *data = &dev->recvbuf[4]; const uint8_t sync_req[] = { 0x01, 0x7e }; const uint8_t sync_rsp[] = { 0x02, 0x7d }; uint8_t conf_req[] = { 0x03, 0xfc, 0x10 | CONFIG_UART_BTH5_TXWIN }; const uint8_t conf_rsp[] = { 0x04, 0x7b }; if (H5_HDR_LEN(hdr) < 2 || (H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT)) { return; } if (memcmp(data, sync_req, 2) == 0) { h5_link_control(dev, sync_rsp, 2); } else if (memcmp(data, sync_rsp, 2) == 0) { wlinfo("h5: initialized"); dev->state = H5_INITIALIZED; h5_link_control(dev, conf_req, 3); } else if (memcmp(data, conf_req, 2) == 0) { h5_link_control(dev, conf_rsp, 2); h5_link_control(dev, conf_req, 3); } else if (memcmp(data, conf_rsp, 2) == 0) { if (dev->state == H5_ACTIVE) { return; } if (H5_HDR_LEN(hdr) > 2) { dev->txwin = (data[2] & 0x07); dev->crcvalid = ((data[2] & 0x10) == 0x10); wlinfo("h5 txwin:%d, crcvalid:%d", dev->txwin, dev->crcvalid); } if (dev->txwin < CONFIG_UART_BTH5_TXWIN) { wlerr("h5, txwin(%d) overflow(%d)", dev->txwin, CONFIG_UART_BTH5_TXWIN); return; } wlinfo("h5: active"); dev->state = H5_ACTIVE; nxsem_post(&dev->opensem); } else { wlerr("ERROR Link Control: 0x%02hhx 0x%02hhx", data[0], data[1]); } } static void h5_unack_handle(FAR struct uart_bth5_s *dev) { size_t to_remove; uint8_t seq; nxmutex_lock(&dev->sendlock); to_remove = h5_unack_size(&dev->unackpool); if (to_remove == 0) { goto end; } seq = dev->txseq; do { if (dev->rxack == seq) { break; } seq = (seq - 1) & 0x07; } while (--to_remove > 0); if (seq != dev->rxack) { wlerr("error, %s seq:%d != rxack:%d", __func__, seq, dev->rxack); goto end; } while (to_remove > 0) { h5_unack_dtor(&dev->unackpool); if (to_remove == dev->txwin) { uart_bth5_post(&dev->acksem); } to_remove--; } if (!h5_unack_size(&dev->unackpool)) { work_cancel(HPWORK, &dev->retxworker); } end: nxmutex_unlock(&dev->sendlock); } static void h5_recv_handle(FAR struct uart_bth5_s *dev) { int reserve = dev->drv->head_reserve; FAR uint8_t *hdr = dev->recvbuf; uint8_t type; if (H5_HDR_RELIABLE(hdr)) { dev->txack = (dev->txack + 1) & 0x07; if (work_available(&dev->ackworker)) { work_queue(HPWORK, &dev->ackworker, h5_ack_work, dev, H5_ACK_TIMEOUT); } } dev->rxack = H5_HDR_ACK(hdr); type = H5_HDR_PKT_TYPE(hdr); h5_unack_handle(dev); switch (H5_HDR_PKT_TYPE(hdr)) { case HCI_EVENT_PKT: case HCI_ACLDATA_PKT: case HCI_SCODATA_PKT: case HCI_ISODATA_PKT: { nxmutex_lock(&dev->recvlock); if (circbuf_space(&dev->circbuf) >= dev->recvlen + reserve) { circbuf_write(&dev->circbuf, &type, reserve); circbuf_write(&dev->circbuf, dev->recvbuf + 4, dev->recvlen - 4); } uart_bth5_pollnotify(dev, POLLIN); nxmutex_unlock(&dev->recvlock); break; } case HCI_3WIRE_LINK_PKT: { h5_message_handle(dev); break; } default: break; } h5_rx_reset(dev); } static int h5_rx_crc(FAR struct uart_bth5_s *dev, uint8_t c) { if (h5_crc_rev16(dev->crcvalue) != h5_get_crc(dev)) { wlerr("error, crcvalue(%04x) recv(%04x)", h5_crc_rev16(dev->crcvalue), h5_get_crc(dev)); h5_rx_reset(dev); return -EINVAL; } dev->recvlen -= 2; h5_recv_handle(dev); return 0; } static int h5_rx_payload(FAR struct uart_bth5_s *dev, uint8_t c) { FAR uint8_t *hdr = dev->recvbuf; if (H5_HDR_CRC(hdr)) { dev->rxfunc = h5_rx_crc; dev->rxpending = 2; dev->flags |= H5_BIT_RX_CRC; } else { h5_recv_handle(dev); } return 0; } static int h5_rx_header(FAR struct uart_bth5_s *dev, uint8_t c) { FAR uint8_t *hdr = dev->recvbuf; wlinfo("rx t:%d l:%d s:%d a:%d", H5_HDR_PKT_TYPE(hdr), H5_HDR_LEN(hdr), H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr)); if (((hdr[0] + hdr[1] + hdr[2] + hdr[3]) & 0xff) != 0xff) { wlerr("error: invalid header checksum"); h5_rx_reset(dev); return -EINVAL; } if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != dev->txack) { work_queue(HPWORK, &dev->ackworker, h5_ack_work, dev, 0); h5_rx_reset(dev); return -EINVAL; } if (dev->state != H5_ACTIVE && H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) { wlerr("error: non-link packet received in non-active state"); h5_rx_reset(dev); return -EINVAL; } dev->rxfunc = h5_rx_payload; dev->rxpending = H5_HDR_LEN(hdr); return 0; } static int h5_rx_start(FAR struct uart_bth5_s *dev, uint8_t c) { if (c == SLIP_DELIMITER) { return 1; } dev->rxfunc = h5_rx_header; dev->rxpending = 4; dev->crcvalue = 0xffff; return 0; } static int h5_rx_delimiter(FAR struct uart_bth5_s *dev, uint8_t c) { if (c == SLIP_DELIMITER) { dev->rxfunc = h5_rx_start; } return 1; } static void h5_rx_reset(FAR struct uart_bth5_s *dev) { dev->recvlen = 0; dev->rxfunc = h5_rx_delimiter; dev->rxpending = 0; dev->flags = 0; } static int h5_unslip_one_byte(FAR struct uart_bth5_s *dev, uint8_t c) { uint8_t byte = c; if (!(dev->flags & H5_BIT_RX_ESC) && c == SLIP_ESC) { dev->flags |= H5_BIT_RX_ESC; return 0; } if (dev->flags & H5_BIT_RX_ESC) { dev->flags &= ~H5_BIT_RX_ESC; switch (c) { case SLIP_ESC_DELIM: byte = SLIP_DELIMITER; break; case SLIP_ESC_ESC: byte = SLIP_ESC; break; default: wlerr("error: invalid esc byte 0x%02hhx", c); h5_rx_reset(dev); return -EINVAL; } } dev->recvbuf[dev->recvlen++] = byte; dev->rxpending--; if (H5_HDR_CRC(dev->recvbuf) && !(dev->flags & H5_BIT_RX_CRC)) { h5_crc_update(&dev->crcvalue, byte); } return 0; } static bool h5_reliable_packet(uint8_t type) { switch (type) { case HCI_COMMAND_PKT: case HCI_ACLDATA_PKT: case HCI_EVENT_PKT: case HCI_ISODATA_PKT: return true; default: return false; } } static int h5_slip_delim(FAR uint8_t *frame, int index) { frame[index] = SLIP_DELIMITER; return 1; } static int h5_slip_one_byte(FAR uint8_t *frame, int index, uint8_t c) { int ret; switch (c) { case SLIP_DELIMITER: { frame[index++] = SLIP_ESC; frame[index] = SLIP_ESC_DELIM; ret = 2; break; } case SLIP_ESC: { frame[index++] = SLIP_ESC; frame[index] = SLIP_ESC_ESC; ret = 2; break; } default: frame[index] = c; ret = 1; break; } return ret; } static int h5_uart_header(FAR uint8_t *data, enum bt_buf_type_e *type, size_t *pktlen, size_t *hdrlen, size_t reserved) { int ret = OK; FAR union bt_hdr_u *hdr = (FAR union bt_hdr_u *)data; switch (*(data - reserved)) { case H4_CMD: { *hdrlen = sizeof(struct bt_hci_cmd_hdr_s); *pktlen = hdr->cmd.param_len; *type = HCI_COMMAND_PKT; break; } case H4_ACL: { *hdrlen = sizeof(struct bt_hci_acl_hdr_s); *pktlen = hdr->acl.len; *type = HCI_ACLDATA_PKT; break; } case H4_ISO: { *hdrlen = sizeof(struct bt_hci_iso_hdr_s); *pktlen = hdr->iso.len; *type = HCI_ISODATA_PKT; break; } default: { ret = -EINVAL; break; } } return ret; } static void h5_ack_work(FAR void *arg) { FAR struct uart_bth5_s *dev = arg; if (dev->state != H5_ACTIVE) { wlerr("%s state:%d not active", __func__, dev->state); return; } uart_h5_send(dev, HCI_3WIRE_ACK_PKT, NULL, 0); } static void h5_retx_work(FAR void *arg) { FAR struct uart_bth5_s *dev = arg; FAR struct unack_frame_s *frame; size_t size; if (dev->state != H5_ACTIVE) { wlerr("%s state:%d not active", __func__, dev->state); return; } nxmutex_lock(&dev->sendlock); size = h5_unack_size(&dev->unackpool); while (size > 0) { frame = (FAR struct unack_frame_s *)h5_unack_dtor(&dev->unackpool); uart_h5_send(dev, frame->type, frame->data, frame->pktlen); size--; } nxmutex_unlock(&dev->sendlock); } static void uart_bth5_post(FAR sem_t *sem) { int semcount; nxsem_get_value(sem, &semcount); if (semcount < 1) { nxsem_post(sem); } } static void uart_bth5_pollnotify(FAR struct uart_bth5_s *dev, pollevent_t eventset) { poll_notify(dev->fds, CONFIG_UART_BTH5_NPOLLWAITERS, eventset); if ((eventset & POLLIN) != 0) { uart_bth5_post(&dev->recvsem); } } static int uart_bth5_receive(FAR struct bt_driver_s *drv, enum bt_buf_type_e type, FAR void *buffer, size_t buflen) { FAR struct uart_bth5_s *dev = drv->priv; FAR const uint8_t *ptr = buffer; int processed; while (buflen > 0) { if (dev->rxpending > 0) { if (*ptr == SLIP_DELIMITER) { wlerr("error, too short H5 packet"); h5_rx_reset(dev); continue; } h5_unslip_one_byte(dev, *ptr); ptr++; buflen--; continue; } processed = dev->rxfunc(dev, *ptr); if (processed < 0) { return processed; } ptr += processed; buflen -= processed; } return 0; } static int uart_bth5_open(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct uart_bth5_s *dev = inode->i_private; int ret; const uint8_t sync_req[] = { 0x01, 0x7e }; ret = nxmutex_lock(&dev->openlock); if (ret < 0) { return ret; } if (dev->openrefs == MAX_OPENCNT) { ret = -EMFILE; goto end; } else { dev->openrefs++; } if (dev->openrefs > 1) { goto end; } ret = dev->drv->open(dev->drv); if (ret < 0) { goto end; } dev->sendlen = 0; dev->state = H5_UNINITIALIZED; h5_link_control(dev, sync_req, sizeof(sync_req)); ret = nxsem_tickwait_uninterruptible(&dev->opensem, SEC2TICK(3)); if (ret == -ETIMEDOUT) { wlerr("error, bluetooth driver open timeout"); nxmutex_unlock(&dev->openlock); return ret; } h5_unack_init(&dev->unackpool); end: nxmutex_unlock(&dev->openlock); return OK; } static int uart_bth5_close(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct uart_bth5_s *dev = inode->i_private; int ret = OK; ret = nxmutex_lock(&dev->openlock); if (ret < 0) { goto end; } if (dev->openrefs == 0) { ret = -EIO; goto end; } else { dev->openrefs--; } if (dev->openrefs > 0) { goto end; } dev->drv->close(dev->drv); dev->state = H5_UNINITIALIZED; h5_peer_reset(dev); h5_rx_reset(dev); uart_bth5_pollnotify(dev, POLLIN | POLLOUT); nxsem_post(&dev->opensem); h5_unack_cleanup(&dev->unackpool); end: nxmutex_unlock(&dev->openlock); return ret; } static ssize_t uart_bth5_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct uart_bth5_s *dev = inode->i_private; ssize_t nread; nxmutex_lock(&dev->recvlock); while (1) { nread = circbuf_read(&dev->circbuf, buffer, buflen); if (nread != 0 || (filep->f_oflags & O_NONBLOCK)) { break; } while (circbuf_is_empty(&dev->circbuf)) { nxmutex_unlock(&dev->recvlock); nxsem_wait_uninterruptible(&dev->recvsem); nxmutex_lock(&dev->recvlock); } } nxmutex_unlock(&dev->recvlock); return nread; } static int uart_h5_send(FAR struct uart_bth5_s *dev, uint8_t type, FAR const uint8_t *payload, size_t len) { uint8_t frame[CONFIG_UART_BTH5_TXBUFSIZE]; uint8_t hdr[4]; int idx; int length = 0; uint16_t h5_txmsg_crc = 0xffff; memset(hdr, 0, sizeof(hdr)); if (h5_reliable_packet(type)) { H5_SET_RELIABLE(hdr); H5_SET_SEQ(hdr, dev->txseq); dev->txseq = (dev->txseq + 1) & 0x07; work_queue(HPWORK, &dev->retxworker, h5_retx_work, dev, H5_RTX_TIMEOUT); } H5_SET_ACK(hdr, dev->txack); H5_SET_TYPE(hdr, type); H5_SET_LEN(hdr, len); if (dev->crcvalid) { hdr[0] |= 0x40; } /* Set head checksum */ hdr[3] = ~((hdr[0] + hdr[1] + hdr[2]) & 0xff); length += h5_slip_delim(frame, length); /* Put h5 header */ for (idx = 0; idx < 4; idx++) { length += h5_slip_one_byte(frame, length, hdr[idx]); if (dev->crcvalid) { h5_crc_update(&h5_txmsg_crc, hdr[idx]); } } /* Put h5 payload */ for (idx = 0; idx < len; idx++) { length += h5_slip_one_byte(frame, length, payload[idx]); if (dev->crcvalid) { h5_crc_update(&h5_txmsg_crc, payload[idx]); } } if (dev->crcvalid) { h5_txmsg_crc = h5_crc_rev16(h5_txmsg_crc); length += h5_slip_one_byte(frame, length, (uint8_t)((h5_txmsg_crc >> 8) & 0x00ff)); length += h5_slip_one_byte(frame, length, (uint8_t)(h5_txmsg_crc & 0x00ff)); } length += h5_slip_delim(frame, length); work_cancel(HPWORK, &dev->ackworker); wlinfo("tx t:%d l:%d s:%d a:%d\n", H5_HDR_PKT_TYPE(hdr), H5_HDR_LEN(hdr), H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr)); return dev->drv->send(dev->drv, type, frame, length); } static ssize_t uart_bth5_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct uart_bth5_s *dev = inode->i_private; enum bt_buf_type_e type; size_t reserved = dev->drv->head_reserve; FAR uint8_t *data; size_t pktlen; size_t hdrlen; int ret; FAR struct unack_frame_s *frame; ret = nxmutex_lock(&dev->sendlock); if (ret < 0) { wlerr("%s error, nxmutex_lock", __func__); return ret; } data = dev->sendbuf + reserved + dev->sendlen; if (dev->sendlen + buflen > CONFIG_UART_BTH5_TXBUFSIZE - reserved) { ret = -E2BIG; goto err; } memcpy(data - reserved + dev->sendlen, buffer, buflen); dev->sendlen += buflen; for (; ; ) { ret = h5_uart_header(data, &type, &pktlen, &hdrlen, reserved); if (ret < 0) { goto err; } /* Reassembly is incomplete ? */ if (dev->sendlen < hdrlen) { goto out; } pktlen += hdrlen; if (dev->sendlen < pktlen) { goto out; } /* Got the full packet, send out */ if (h5_unack_size(&dev->unackpool) > dev->txwin) { work_queue(HPWORK, &dev->ackworker, h5_ack_work, dev, 0); if (filep->f_oflags & O_NONBLOCK) { ret = -EAGAIN; goto out; } else { nxmutex_unlock(&dev->sendlock); nxsem_wait_uninterruptible(&dev->acksem); nxmutex_lock(&dev->sendlock); } } ret = uart_h5_send(dev, type, data, pktlen); if (ret < 0) { goto err; } frame = (FAR struct unack_frame_s *)h5_unack_ctor(&dev->unackpool); frame->type = type; frame->pktlen = pktlen; memcpy(frame->data, data, pktlen); dev->sendlen -= pktlen + reserved; if (dev->sendlen > 0) { memmove(data - reserved, dev->sendbuf + pktlen, dev->sendlen); } } err: dev->sendlen = 0; out: nxmutex_unlock(&dev->sendlock); return ret < 0 ? ret : buflen; } static int uart_bth5_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode = filep->f_inode; FAR struct uart_bth5_s *dev = inode->i_private; if (!dev->drv->ioctl) { return -ENOTTY; } return dev->drv->ioctl(dev->drv, cmd, arg); } static int uart_bth5_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct inode *inode = filep->f_inode; FAR struct uart_bth5_s *dev = inode->i_private; pollevent_t eventset = 0; int ret = 0; int i; if (setup) { for (i = 0; i < CONFIG_UART_BTH5_NPOLLWAITERS; i++) { /* Find an available slot */ if (!dev->fds[i]) { /* Bind the poll structure and this slot */ dev->fds[i] = fds; fds->priv = &dev->fds[i]; break; } } if (i >= CONFIG_UART_BTH5_NPOLLWAITERS) { fds->priv = NULL; ret = -EBUSY; } nxmutex_lock(&dev->recvlock); if (!circbuf_is_empty(&dev->circbuf)) { eventset |= POLLIN; } nxmutex_unlock(&dev->recvlock); eventset |= POLLOUT; poll_notify(&fds, 1, eventset); } else if (fds->priv != NULL) { for (i = 0; i < CONFIG_UART_BTH5_NPOLLWAITERS; i++) { if (fds == dev->fds[i]) { dev->fds[i] = NULL; fds->priv = NULL; break; } } } return ret; } /**************************************************************************** * Public Functions ****************************************************************************/ int uart_bth5_register(FAR const char *path, FAR struct bt_driver_s *drv) { FAR struct uart_bth5_s *dev; int ret; dev = kmm_zalloc(sizeof(struct uart_bth5_s)); if (dev == NULL) { return -ENOMEM; } ret = circbuf_init(&dev->circbuf, NULL, CONFIG_UART_BTH5_RXBUFSIZE); if (ret < 0) { kmm_free(dev); return -ENOMEM; } dev->drv = drv; drv->receive = uart_bth5_receive; drv->priv = dev; nxsem_init(&dev->opensem, 0, 0); nxsem_init(&dev->recvsem, 0, 0); nxsem_init(&dev->acksem, 0, 0); nxmutex_init(&dev->sendlock); nxmutex_init(&dev->recvlock); nxmutex_init(&dev->openlock); h5_rx_reset(dev); ret = register_driver(path, &g_uart_bth5_ops, 0666, dev); if (ret < 0) { nxsem_destroy(&dev->recvsem); nxsem_destroy(&dev->opensem); nxsem_destroy(&dev->acksem); nxmutex_destroy(&dev->sendlock); nxmutex_destroy(&dev->openlock); nxmutex_destroy(&dev->recvlock); circbuf_uninit(&dev->circbuf); kmm_free(dev); } return ret; }