/** * i2400m_tx_msg_get - Get the first TX message in the FIFO to start sending it * * @i2400m: device descriptors * @bus_size: where to place the size of the TX message * * Called by the bus-specific driver to get the first TX message at * the FIF that is ready for transmission. * * It sets the state in @i2400m to indicate the bus-specific driver is * transfering that message (i2400m->tx_msg_size). * * Once the transfer is completed, call i2400m_tx_msg_sent(). * * Notes: * * The size of the TX message to be transmitted might be smaller than * that of the TX message in the FIFO (in case the header was * shorter). Hence, we copy it in @bus_size, for the bus layer to * use. We keep the message's size in i2400m->tx_msg_size so that * when the bus later is done transferring we know how much to * advance the fifo. * * We collect statistics here as all the data is available and we * assume it is going to work [see i2400m_tx_msg_sent()]. */ struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *i2400m, size_t *bus_size) { struct device *dev = i2400m_dev(i2400m); struct i2400m_msg_hdr *tx_msg, *tx_msg_moved; unsigned long flags, pls; d_fnstart(3, dev, "(i2400m %p bus_size %p)\n", i2400m, bus_size); spin_lock_irqsave(&i2400m->tx_lock, flags); skip: tx_msg_moved = NULL; if (i2400m->tx_in == i2400m->tx_out) { /* Empty FIFO? */ i2400m->tx_in = 0; i2400m->tx_out = 0; d_printf(2, dev, "TX: FIFO empty: resetting\n"); goto out_unlock; } tx_msg = i2400m->tx_buf + i2400m->tx_out % I2400M_TX_BUF_SIZE; if (tx_msg->size & I2400M_TX_SKIP) { /* skip? */ d_printf(2, dev, "TX: skip: msg @%zu (%zu b)\n", i2400m->tx_out % I2400M_TX_BUF_SIZE, (size_t) tx_msg->size & ~I2400M_TX_SKIP); i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP; goto skip; } if (tx_msg->num_pls == 0) { /* No payloads? */ if (tx_msg == i2400m->tx_msg) { /* open, we are done */ d_printf(2, dev, "TX: FIFO empty: open msg w/o payloads @%zu\n", (void *) tx_msg - i2400m->tx_buf); tx_msg = NULL; goto out_unlock; } else { /* closed, skip it */ d_printf(2, dev, "TX: skip msg w/o payloads @%zu (%zu b)\n", (void *) tx_msg - i2400m->tx_buf, (size_t) tx_msg->size); i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP; goto skip; } } if (tx_msg == i2400m->tx_msg) /* open msg? */ i2400m_tx_close(i2400m); /* Now we have a valid TX message (with payloads) to TX */ tx_msg_moved = (void *) tx_msg + tx_msg->offset; i2400m->tx_msg_size = tx_msg->size; *bus_size = tx_msg_moved->size; d_printf(2, dev, "TX: pid %d msg hdr at @%zu offset +@%zu " "size %zu bus_size %zu\n", current->pid, (void *) tx_msg - i2400m->tx_buf, (size_t) tx_msg->offset, (size_t) tx_msg->size, (size_t) tx_msg_moved->size); tx_msg_moved->barker = le32_to_cpu(I2400M_H2D_PREVIEW_BARKER); tx_msg_moved->sequence = le32_to_cpu(i2400m->tx_sequence++); pls = le32_to_cpu(tx_msg_moved->num_pls); i2400m->tx_pl_num += pls; /* Update stats */ if (pls > i2400m->tx_pl_max) i2400m->tx_pl_max = pls; if (pls < i2400m->tx_pl_min) i2400m->tx_pl_min = pls; i2400m->tx_num++; i2400m->tx_size_acc += *bus_size; if (*bus_size < i2400m->tx_size_min) i2400m->tx_size_min = *bus_size; if (*bus_size > i2400m->tx_size_max) i2400m->tx_size_max = *bus_size; out_unlock: spin_unlock_irqrestore(&i2400m->tx_lock, flags); d_fnstart(3, dev, "(i2400m %p bus_size %p [%zu]) = %p\n", i2400m, bus_size, *bus_size, tx_msg_moved); return tx_msg_moved; }
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; 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; }
/** * 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; }