static void i2400m_tx_new(struct i2400m *i2400m) { struct device *dev = i2400m_dev(i2400m); struct i2400m_msg_hdr *tx_msg; bool try_head = false; BUG_ON(i2400m->tx_msg != NULL); try_head: tx_msg = i2400m_tx_fifo_push(i2400m, I2400M_TX_PLD_SIZE, i2400m->bus_tx_room_min, try_head); if (tx_msg == NULL) goto out; else if (tx_msg == TAIL_FULL) { i2400m_tx_skip_tail(i2400m); d_printf(2, dev, "new TX message: tail full, trying head\n"); try_head = true; goto try_head; } memset(tx_msg, 0, I2400M_TX_PLD_SIZE); tx_msg->size = I2400M_TX_PLD_SIZE; out: i2400m->tx_msg = tx_msg; d_printf(2, dev, "new TX message: %p @%zu\n", tx_msg, (void *) tx_msg - i2400m->tx_buf); }
/* * Start a new TX message header in the queue. * * Reserve memory from the base FIFO engine and then just initialize * the message header. * * We allocate the biggest TX message header we might need (one that'd * fit I2400M_TX_PLD_MAX payloads) -- when it is closed it will be * 'ironed it out' and the unneeded parts removed. * * NOTE: * * Assumes that the previous message is CLOSED (eg: either * there was none or 'i2400m_tx_close()' was called on it). * * Assumes i2400m->tx_lock is taken, and we use that as a barrier */ static void i2400m_tx_new(struct i2400m *i2400m) { struct device *dev = i2400m_dev(i2400m); struct i2400m_msg_hdr *tx_msg; bool try_head = false; BUG_ON(i2400m->tx_msg != NULL); /* * In certain situations, TX queue might have enough space to * accommodate the new message header I2400M_TX_PLD_SIZE, but * might not have enough space to accommodate the payloads. * Adding bus_tx_room_min padding while allocating a new TX message * increases the possibilities of including at least one payload of the * size <= bus_tx_room_min. */ try_head: tx_msg = i2400m_tx_fifo_push(i2400m, I2400M_TX_PLD_SIZE, i2400m->bus_tx_room_min, try_head); if (tx_msg == NULL) goto out; else if (tx_msg == TAIL_FULL) { i2400m_tx_skip_tail(i2400m); d_printf(2, dev, "new TX message: tail full, trying head\n"); try_head = true; goto try_head; } memset(tx_msg, 0, I2400M_TX_PLD_SIZE); tx_msg->size = I2400M_TX_PLD_SIZE; out: i2400m->tx_msg = tx_msg; d_printf(2, dev, "new TX message: %p @%zu\n", tx_msg, (void *) tx_msg - i2400m->tx_buf); }
static void i2400m_tx_close(struct i2400m *i2400m) { struct device *dev = i2400m_dev(i2400m); struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; struct i2400m_msg_hdr *tx_msg_moved; size_t aligned_size, padding, hdr_size; void *pad_buf; unsigned num_pls; if (tx_msg->size & I2400M_TX_SKIP) goto out; num_pls = le16_to_cpu(tx_msg->num_pls); if (num_pls == 0) { tx_msg->size |= I2400M_TX_SKIP; goto out; } hdr_size = sizeof(*tx_msg) + le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]); hdr_size = ALIGN(hdr_size, I2400M_PL_ALIGN); tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size; tx_msg_moved = (void *) tx_msg + tx_msg->offset; memmove(tx_msg_moved, tx_msg, hdr_size); tx_msg_moved->size -= tx_msg->offset; aligned_size = ALIGN(tx_msg_moved->size, i2400m->bus_tx_block_size); padding = aligned_size - tx_msg_moved->size; if (padding > 0) { pad_buf = i2400m_tx_fifo_push(i2400m, padding, 0, 0); if (unlikely(WARN_ON(pad_buf == NULL || pad_buf == TAIL_FULL))) { dev_err(dev, "SW BUG! Possible data leakage from memory the " "device should not read for padding - " "size %lu aligned_size %zu tx_buf %p in " "%zu out %zu\n", (unsigned long) tx_msg_moved->size, aligned_size, i2400m->tx_buf, i2400m->tx_in, i2400m->tx_out); } else memset(pad_buf, 0xad, padding); } tx_msg_moved->padding = cpu_to_le16(padding); tx_msg_moved->size += padding; if (tx_msg != tx_msg_moved) tx_msg->size += padding; out: i2400m->tx_msg = NULL; }
/** * i2400m_tx - send the data in a buffer to the device * * @buf: pointer to the buffer to transmit * * @buf_len: buffer size * * @pl_type: type of the payload we are sending. * * Returns: * 0 if ok, < 0 errno code on error (-ENOSPC, if there is no more * room for the message in the queue). * * Appends the buffer to the TX FIFO and notifies the bus-specific * part of the driver that there is new data ready to transmit. * Once this function returns, the buffer has been copied, so it can * be reused. * * The steps followed to append are explained in detail in the file * header. * * Whenever we write to a message, we increase msg->size, so it * reflects exactly how big the message is. This is needed so that if * we concatenate two messages before they can be sent, the code that * sends the messages can find the boundaries (and it will replace the * size with the real barker before sending). * * Note: * * Cold and warm reset payloads need to be sent as a single * payload, so we handle that. */ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, enum i2400m_pt pl_type) { int result = -ENOSPC; struct device *dev = i2400m_dev(i2400m); unsigned long flags; size_t padded_len; void *ptr; unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM || pl_type == I2400M_PT_RESET_COLD; d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", i2400m, buf, buf_len, pl_type); padded_len = ALIGN(buf_len, I2400M_PL_PAD); d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); /* If there is no current TX message, create one; if the * current one is out of payload slots or we have a singleton, * close it and start a new one */ spin_lock_irqsave(&i2400m->tx_lock, flags); try_new: if (unlikely(i2400m->tx_msg == NULL)) i2400m_tx_new(i2400m); else if (unlikely(!i2400m_tx_fits(i2400m) || (is_singleton && i2400m->tx_msg->num_pls != 0))) { d_printf(2, dev, "closing TX message (fits %u singleton " "%u num_pls %u)\n", i2400m_tx_fits(i2400m), is_singleton, i2400m->tx_msg->num_pls); i2400m_tx_close(i2400m); i2400m_tx_new(i2400m); } if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) { d_printf(2, dev, "TX: message too big, going new\n"); i2400m_tx_close(i2400m); i2400m_tx_new(i2400m); } if (i2400m->tx_msg == NULL) goto error_tx_new; /* So we have a current message header; now append space for * the message -- if there is not enough, try the head */ ptr = i2400m_tx_fifo_push(i2400m, padded_len, i2400m->bus_tx_block_size); if (ptr == TAIL_FULL) { /* Tail is full, try head */ d_printf(2, dev, "pl append: tail full\n"); i2400m_tx_close(i2400m); i2400m_tx_skip_tail(i2400m); goto try_new; } else if (ptr == NULL) { /* All full */ result = -ENOSPC; d_printf(2, dev, "pl append: all full\n"); } else { /* Got space, copy it, set padding */ struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; unsigned num_pls = le16_to_cpu(tx_msg->num_pls); memcpy(ptr, buf, buf_len); memset(ptr + buf_len, 0xad, padded_len - buf_len); i2400m_pld_set(&tx_msg->pld[num_pls], buf_len, pl_type); d_printf(3, dev, "pld 0x%08x (type 0x%1x len 0x%04zx\n", le32_to_cpu(tx_msg->pld[num_pls].val), pl_type, buf_len); tx_msg->num_pls = le16_to_cpu(num_pls+1); tx_msg->size += padded_len; d_printf(2, dev, "TX: appended %zu b (up to %u b) pl #%u \n", padded_len, tx_msg->size, num_pls+1); d_printf(2, dev, "TX: appended hdr @%zu %zu b pl #%u @%zu %zu/%zu b\n", (void *)tx_msg - i2400m->tx_buf, (size_t)tx_msg->size, num_pls+1, ptr - i2400m->tx_buf, buf_len, padded_len); result = 0; if (is_singleton) i2400m_tx_close(i2400m); } error_tx_new: spin_unlock_irqrestore(&i2400m->tx_lock, flags); i2400m->bus_tx_kick(i2400m); /* always kick, might free up space */ d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n", i2400m, buf, buf_len, pl_type, result); return result; }
/* * Finalize the current TX message header * * Sets the message header to be at the proper location depending on * how many descriptors we have (check documentation at the file's * header for more info on that). * * Appends padding bytes to make sure the whole TX message (counting * from the 'relocated' message header) is aligned to * tx_block_size. We assume the _append() code has left enough space * in the FIFO for that. If there are no payloads, just pass, as it * won't be transferred. * * The amount of padding bytes depends on how many payloads are in the * TX message, as the "msg header and payload descriptors" will be * shifted up in the buffer. */ static void i2400m_tx_close(struct i2400m *i2400m) { struct device *dev = i2400m_dev(i2400m); struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; struct i2400m_msg_hdr *tx_msg_moved; size_t aligned_size, padding, hdr_size; void *pad_buf; if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */ goto out; /* Relocate the message header * * Find the current header size, align it to 16 and if we need * to move it so the tail is next to the payloads, move it and * set the offset. * * If it moved, this header is good only for transmission; the * original one (it is kept if we moved) is still used to * figure out where the next TX message starts (and where the * offset to the moved header is). */ hdr_size = sizeof(*tx_msg) + le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]); hdr_size = ALIGN(hdr_size, I2400M_PL_PAD); tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size; tx_msg_moved = (void *) tx_msg + tx_msg->offset; memmove(tx_msg_moved, tx_msg, hdr_size); tx_msg_moved->size -= tx_msg->offset; /* * Now figure out how much we have to add to the (moved!) * message so the size is a multiple of i2400m->bus_tx_block_size. */ aligned_size = ALIGN(tx_msg_moved->size, i2400m->bus_tx_block_size); padding = aligned_size - tx_msg_moved->size; if (padding > 0) { pad_buf = i2400m_tx_fifo_push(i2400m, padding, 0); if (unlikely(WARN_ON(pad_buf == NULL || pad_buf == TAIL_FULL))) { /* This should not happen -- append should verify * there is always space left at least to append * tx_block_size */ dev_err(dev, "SW BUG! Possible data leakage from memory the " "device should not read for padding - " "size %lu aligned_size %zu tx_buf %p in " "%zu out %zu\n", (unsigned long) tx_msg_moved->size, aligned_size, i2400m->tx_buf, i2400m->tx_in, i2400m->tx_out); } else memset(pad_buf, 0xad, padding); } tx_msg_moved->padding = cpu_to_le16(padding); tx_msg_moved->size += padding; if (tx_msg != tx_msg_moved) tx_msg->size += padding; out: i2400m->tx_msg = NULL; }
int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, enum i2400m_pt pl_type) { int result = -ENOSPC; struct device *dev = i2400m_dev(i2400m); unsigned long flags; size_t padded_len; void *ptr; bool try_head = false; unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM || pl_type == I2400M_PT_RESET_COLD; d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", i2400m, buf, buf_len, pl_type); padded_len = ALIGN(buf_len, I2400M_PL_ALIGN); d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); spin_lock_irqsave(&i2400m->tx_lock, flags); if (i2400m->tx_buf == NULL) { result = -ESHUTDOWN; goto error_tx_new; } try_new: if (unlikely(i2400m->tx_msg == NULL)) i2400m_tx_new(i2400m); else if (unlikely(!i2400m_tx_fits(i2400m) || (is_singleton && i2400m->tx_msg->num_pls != 0))) { d_printf(2, dev, "closing TX message (fits %u singleton " "%u num_pls %u)\n", i2400m_tx_fits(i2400m), is_singleton, i2400m->tx_msg->num_pls); i2400m_tx_close(i2400m); i2400m_tx_new(i2400m); } if (i2400m->tx_msg == NULL) goto error_tx_new; if (i2400m->tx_msg->size + padded_len > I2400M_TX_MSG_SIZE) { d_printf(2, dev, "TX: message too big, going new\n"); i2400m_tx_close(i2400m); i2400m_tx_new(i2400m); } if (i2400m->tx_msg == NULL) goto error_tx_new; ptr = i2400m_tx_fifo_push(i2400m, padded_len, i2400m->bus_tx_block_size, try_head); if (ptr == TAIL_FULL) { d_printf(2, dev, "pl append: tail full\n"); i2400m_tx_close(i2400m); i2400m_tx_skip_tail(i2400m); try_head = true; goto try_new; } else if (ptr == NULL) { result = -ENOSPC; d_printf(2, dev, "pl append: all full\n"); } else { struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; unsigned num_pls = le16_to_cpu(tx_msg->num_pls); memcpy(ptr, buf, buf_len); memset(ptr + buf_len, 0xad, padded_len - buf_len); i2400m_pld_set(&tx_msg->pld[num_pls], buf_len, pl_type); d_printf(3, dev, "pld 0x%08x (type 0x%1x len 0x%04zx\n", le32_to_cpu(tx_msg->pld[num_pls].val), pl_type, buf_len); tx_msg->num_pls = le16_to_cpu(num_pls+1); tx_msg->size += padded_len; d_printf(2, dev, "TX: appended %zu b (up to %u b) pl #%u\n", padded_len, tx_msg->size, num_pls+1); d_printf(2, dev, "TX: appended hdr @%zu %zu b pl #%u @%zu %zu/%zu b\n", (void *)tx_msg - i2400m->tx_buf, (size_t)tx_msg->size, num_pls+1, ptr - i2400m->tx_buf, buf_len, padded_len); result = 0; if (is_singleton) i2400m_tx_close(i2400m); } error_tx_new: spin_unlock_irqrestore(&i2400m->tx_lock, flags); if (likely(result != -ESHUTDOWN)) i2400m->bus_tx_kick(i2400m); d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n", i2400m, buf, buf_len, pl_type, result); return result; }
/** * i2400m_tx - send the data in a buffer to the device * * @buf: pointer to the buffer to transmit * * @buf_len: buffer size * * @pl_type: type of the payload we are sending. * * Returns: * 0 if ok, < 0 errno code on error (-ENOSPC, if there is no more * room for the message in the queue). * * Appends the buffer to the TX FIFO and notifies the bus-specific * part of the driver that there is new data ready to transmit. * Once this function returns, the buffer has been copied, so it can * be reused. * * The steps followed to append are explained in detail in the file * header. * * Whenever we write to a message, we increase msg->size, so it * reflects exactly how big the message is. This is needed so that if * we concatenate two messages before they can be sent, the code that * sends the messages can find the boundaries (and it will replace the * size with the real barker before sending). * * Note: * * Cold and warm reset payloads need to be sent as a single * payload, so we handle that. */ int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len, enum i2400m_pt pl_type) { int result = -ENOSPC; struct device *dev = i2400m_dev(i2400m); unsigned long flags; size_t padded_len; void *ptr; bool try_head = false; unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM || pl_type == I2400M_PT_RESET_COLD; d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n", i2400m, buf, buf_len, pl_type); padded_len = ALIGN(buf_len, I2400M_PL_ALIGN); d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len); /* If there is no current TX message, create one; if the * current one is out of payload slots or we have a singleton, * close it and start a new one */ spin_lock_irqsave(&i2400m->tx_lock, flags); /* If tx_buf is NULL, device is shutdown */ if (i2400m->tx_buf == NULL) { result = -ESHUTDOWN; goto error_tx_new; } try_new: if (unlikely(i2400m->tx_msg == NULL)) i2400m_tx_new(i2400m); else if (unlikely(!i2400m_tx_fits(i2400m) || (is_singleton && i2400m->tx_msg->num_pls != 0))) { d_printf(2, dev, "closing TX message (fits %u singleton " "%u num_pls %u)\n", i2400m_tx_fits(i2400m), is_singleton, i2400m->tx_msg->num_pls); i2400m_tx_close(i2400m); i2400m_tx_new(i2400m); } if (i2400m->tx_msg == NULL) goto error_tx_new; /* * Check if this skb will fit in the TX queue's current active * TX message. The total message size must not exceed the maximum * size of each message I2400M_TX_MSG_SIZE. If it exceeds, * close the current message and push this skb into the new message. */ if (i2400m->tx_msg->size + padded_len > I2400M_TX_MSG_SIZE) { d_printf(2, dev, "TX: message too big, going new\n"); i2400m_tx_close(i2400m); i2400m_tx_new(i2400m); } if (i2400m->tx_msg == NULL) goto error_tx_new; /* So we have a current message header; now append space for * the message -- if there is not enough, try the head */ ptr = i2400m_tx_fifo_push(i2400m, padded_len, i2400m->bus_tx_block_size, try_head); if (ptr == TAIL_FULL) { /* Tail is full, try head */ d_printf(2, dev, "pl append: tail full\n"); i2400m_tx_close(i2400m); i2400m_tx_skip_tail(i2400m); try_head = true; goto try_new; } else if (ptr == NULL) { /* All full */ result = -ENOSPC; d_printf(2, dev, "pl append: all full\n"); } else { /* Got space, copy it, set padding */ struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg; unsigned num_pls = le16_to_cpu(tx_msg->num_pls); memcpy(ptr, buf, buf_len); memset(ptr + buf_len, 0xad, padded_len - buf_len); i2400m_pld_set(&tx_msg->pld[num_pls], buf_len, pl_type); d_printf(3, dev, "pld 0x%08x (type 0x%1x len 0x%04zx\n", le32_to_cpu(tx_msg->pld[num_pls].val), pl_type, buf_len); tx_msg->num_pls = le16_to_cpu(num_pls+1); tx_msg->size += padded_len; d_printf(2, dev, "TX: appended %zu b (up to %u b) pl #%u\n", padded_len, tx_msg->size, num_pls+1); d_printf(2, dev, "TX: appended hdr @%zu %zu b pl #%u @%zu %zu/%zu b\n", (void *)tx_msg - i2400m->tx_buf, (size_t)tx_msg->size, num_pls+1, ptr - i2400m->tx_buf, buf_len, padded_len); result = 0; if (is_singleton) i2400m_tx_close(i2400m); } error_tx_new: spin_unlock_irqrestore(&i2400m->tx_lock, flags); /* kick in most cases, except when the TX subsys is down, as * it might free space */ if (likely(result != -ESHUTDOWN)) i2400m->bus_tx_kick(i2400m); d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n", i2400m, buf, buf_len, pl_type, result); return result; }