summaryrefslogtreecommitdiff
path: root/src/nv3p.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nv3p.c')
-rw-r--r--src/nv3p.c737
1 files changed, 737 insertions, 0 deletions
diff --git a/src/nv3p.c b/src/nv3p.c
new file mode 100644
index 0000000..2db1905
--- /dev/null
+++ b/src/nv3p.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 2011, NVIDIA CORPORATION
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of NVIDIA CORPORATION nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <errno.h>
+
+#include "nv3p.h"
+#include "usb.h"
+#include "debug.h"
+
+typedef struct nv3p_state {
+ usb_device_t *usb;
+ uint32_t sequence;
+ uint32_t recv_sequence;
+
+ // for partial reads
+ uint32_t bytes_remaining;
+ uint32_t recv_checksum;
+
+ // the last nack code
+ uint32_t last_nack;
+} nv3p_state_t;
+
+/*
+ * double the currently-largest command size, just to have some wiggle-room
+ * (updates to commands without fixing this on accident, etc.)
+ */
+#define NV3P_MAX_COMMAND_SIZE 128
+
+#define NV3P_MAX_ACK_SIZE \
+ NV3P_PACKET_SIZE_BASIC + \
+ NV3P_PACKET_SIZE_NACK + \
+ NV3P_PACKET_SIZE_FOOTER
+
+unsigned char s_buffer[NV3P_MAX_COMMAND_SIZE];
+unsigned char s_args[NV3P_MAX_COMMAND_SIZE]; // handed out to clients
+
+#define WRITE64( packet, data ) \
+ do { \
+ (packet)[0] = (uint8_t)((uint64_t)(data)) & 0xff; \
+ (packet)[1] = (uint8_t)(((uint64_t)(data)) >> 8) & 0xff; \
+ (packet)[2] = (uint8_t)(((uint64_t)(data)) >> 16) & 0xff; \
+ (packet)[3] = (uint8_t)(((uint64_t)(data)) >> 24) & 0xff; \
+ (packet)[4] = (uint8_t)(((uint64_t)(data)) >> 32) & 0xff; \
+ (packet)[5] = (uint8_t)(((uint64_t)(data)) >> 40) & 0xff; \
+ (packet)[6] = (uint8_t)(((uint64_t)(data)) >> 48) & 0xff; \
+ (packet)[7] = (uint8_t)(((uint64_t)(data)) >> 56) & 0xff; \
+ (packet) += 8; \
+ } while( 0 )
+
+#define WRITE32( packet, data ) \
+ do { \
+ (packet)[0] = (data) & 0xff; \
+ (packet)[1] = ((data) >> 8) & 0xff; \
+ (packet)[2] = ((data) >> 16) & 0xff; \
+ (packet)[3] = ((data) >> 24) & 0xff; \
+ (packet) += 4; \
+ } while( 0 )
+
+#define WRITE8( packet, data ) \
+ do { \
+ (packet)[0] = (data) & 0xff; \
+ (packet) += 1; \
+ } while( 0 )
+
+#define READ64( packet, data ) \
+ do { \
+ (data) = (uint64_t)((packet)[0] \
+ | ((uint64_t)((packet)[1]) << 8) \
+ | ((uint64_t)((packet)[2]) << 16) \
+ | ((uint64_t)((packet)[3]) << 24) \
+ | ((uint64_t)((packet)[4]) << 32) \
+ | ((uint64_t)((packet)[5]) << 40) \
+ | ((uint64_t)((packet)[6]) << 48) \
+ | ((uint64_t)((packet)[7]) << 56)); \
+ (packet) += 8; \
+ } while( 0 )
+
+#define READ32( packet, data ) \
+ do { \
+ (data) = (uint32_t)((packet)[0] \
+ | (((packet)[1]) << 8) \
+ | (((packet)[2]) << 16) \
+ | (((packet)[3]) << 24)); \
+ (packet) += 4; \
+ } while( 0 )
+
+
+#define READ8( packet, data ) \
+ do { \
+ (data) = (packet)[0]; \
+ (packet) += 1; \
+ } while( 0 )
+
+// header structures for fun
+typedef struct {
+ uint32_t version;
+ uint32_t packet_type;
+ uint32_t sequence;
+} nv3p_header_t;
+
+static void nv3p_write_header(uint32_t type, uint32_t sequence, uint8_t *packet);
+static void nv3p_write_footer(uint32_t checksum, uint8_t *packet);
+static uint32_t nv3p_cksum(uint8_t *packet, uint32_t length);
+static void nv3p_write_cmd(nv3p_handle_t h3p, uint32_t command, void *args,
+ uint32_t *length, uint8_t *packet );
+static int nv3p_wait_ack(nv3p_handle_t h3p);
+static int nv3p_get_cmd_return(nv3p_handle_t h3p, uint32_t command, void *args);
+
+static int nv3p_recv_hdr(nv3p_handle_t h3p, nv3p_header_t *hdr,
+ uint32_t *checksum );
+static int nv3p_drain_packet(nv3p_handle_t h3p, nv3p_header_t *hdr );
+static void nv3p_nack(nv3p_handle_t h3p, uint32_t code);
+static int nv3p_data_recv(nv3p_handle_t h3p, uint8_t *data, uint32_t length);
+static int nv3p_read(usb_device_t *usb, uint8_t *buf, int len);
+static int nv3p_get_args(nv3p_handle_t h3p, uint32_t command, void **args,
+ uint8_t *packet );
+
+static void nv3p_write_header(uint32_t type, uint32_t sequence, uint8_t *packet)
+{
+ WRITE32(packet, NV3P_VERSION);
+ WRITE32(packet, type);
+ WRITE32(packet, sequence);
+}
+
+static void nv3p_write_footer(uint32_t checksum, uint8_t *packet)
+{
+ WRITE32(packet, checksum);
+}
+
+/*
+ * Just sum the bits. Don't get two's compliment
+ */
+static uint32_t nv3p_cksum(uint8_t *packet, uint32_t length)
+{
+ uint32_t i;
+ uint32_t sum;
+
+ sum = 0;
+ for (i = 0; i < length; i++) {
+ sum += *packet;
+ packet++;
+ }
+
+ return sum;
+}
+
+int nv3p_open(nv3p_handle_t *h3p, usb_device_t *usb)
+{
+ nv3p_state_t *state;
+
+ state = (nv3p_state_t *)malloc(sizeof(nv3p_state_t));
+ if (!state) {
+ return ENOMEM;
+ }
+ memset(state, 0, sizeof(nv3p_state_t));
+ state->last_nack = NV3P_NACK_SUCCESS;
+
+ state->usb = usb;
+
+ *h3p = state;
+ return 0;
+}
+
+void nv3p_close(nv3p_handle_t h3p)
+{
+ if (h3p)
+ free(h3p);
+}
+
+int nv3p_cmd_send(nv3p_handle_t h3p, uint32_t command, void *args)
+{
+ uint32_t checksum;
+ uint32_t length = 0;
+ uint8_t *packet;
+ uint8_t *tmp;
+ int ret = 0;
+
+ packet = &s_buffer[0];
+
+ nv3p_write_header(NV3P_PACKET_TYPE_CMD, h3p->sequence, packet);
+
+ tmp = packet + NV3P_PACKET_SIZE_BASIC;
+ nv3p_write_cmd(h3p, command, args, &length, tmp);
+
+ length += NV3P_PACKET_SIZE_BASIC;
+ length += NV3P_PACKET_SIZE_COMMAND;
+
+ checksum = nv3p_cksum(packet, length);
+ checksum = ~checksum + 1;
+ tmp = packet + length;
+ nv3p_write_footer(checksum, tmp);
+
+ length += NV3P_PACKET_SIZE_FOOTER;
+
+ // send the packet
+ ret = usb_write(h3p->usb, packet, length);
+ if (ret)
+ return ret;
+
+ h3p->sequence++;
+
+ // wait for ack/nack
+ ret = nv3p_wait_ack(h3p);
+ if (ret)
+ return ret;
+
+ // some commands have return data
+ ret = nv3p_get_cmd_return(h3p, command, args);
+ if (ret)
+ return ret;
+
+
+ return 0;
+}
+
+static void nv3p_write_cmd(nv3p_handle_t h3p, uint32_t command, void *args,
+ uint32_t *length, uint8_t *packet)
+{
+ uint8_t *tmp;
+
+ tmp = packet;
+
+ switch(command) {
+ case NV3P_CMD_GET_PLATFORM_INFO:
+ // no args or output only
+ *length = 0;
+ WRITE32(tmp, *length);
+ WRITE32(tmp, command);
+ break;
+ case NV3P_CMD_DL_BCT:
+ {
+ nv3p_cmd_dl_bct_t *a = (nv3p_cmd_dl_bct_t *)args;
+ *length = (1 * 4);
+ WRITE32(tmp, *length);
+ WRITE32(tmp, command);
+ WRITE32(tmp, a->length);
+ break;
+ }
+ case NV3P_CMD_DL_BL:
+ {
+ nv3p_cmd_dl_bl_t *a = (nv3p_cmd_dl_bl_t *)args;
+ *length = (2 * 4) + (1 * 8);
+ WRITE32(tmp, *length);
+ WRITE32(tmp, command);
+ WRITE64(tmp, a->length);
+ WRITE32(tmp, a->address);
+ WRITE32(tmp, a->entry);
+ break;
+ }
+ default:
+ dprintf("bad command: 0x%x\n", command);
+ break;
+ }
+}
+
+
+static int nv3p_wait_ack(nv3p_handle_t h3p)
+{
+ int ret;
+ nv3p_header_t hdr = {0,0,0};
+ uint32_t recv_checksum = 0, checksum;
+ uint32_t length = 0;
+
+ h3p->last_nack = NV3P_NACK_SUCCESS;
+
+ ret = nv3p_recv_hdr(h3p, &hdr, &recv_checksum);
+ if (ret)
+ return ret;
+
+ length = NV3P_PACKET_SIZE_BASIC;
+ switch(hdr.packet_type) {
+ case NV3P_PACKET_TYPE_ACK:
+ length += NV3P_PACKET_SIZE_ACK;
+ break;
+ case NV3P_PACKET_TYPE_NACK:
+ length += NV3P_PACKET_SIZE_NACK;
+ break;
+ default:
+ dprintf("unknown packet type received: 0x%x\n", hdr.packet_type);
+ return EINVAL;
+ }
+
+ if (hdr.packet_type == NV3P_PACKET_TYPE_NACK) {
+ // read 4 more bytes to get the error code
+ ret = nv3p_read(h3p->usb, (uint8_t *)&h3p->last_nack, 4);
+ if (ret)
+ return ret;
+
+ recv_checksum += nv3p_cksum((uint8_t *)&h3p->last_nack, 4);
+ }
+
+ // get/verify the checksum
+ ret = nv3p_read(h3p->usb, (uint8_t *)&checksum, 4);
+ if (ret)
+ return ret;
+
+ if (recv_checksum + checksum != 0) {
+ return EIO;
+ }
+
+ if (hdr.sequence != h3p->sequence - 1) {
+ return EIO;
+ }
+
+ if (hdr.packet_type == NV3P_PACKET_TYPE_NACK) {
+ return EIO;
+ }
+
+ return 0;
+}
+
+
+static int nv3p_get_cmd_return(nv3p_handle_t h3p, uint32_t command, void *args)
+{
+ int ret;
+ uint32_t length = 0;
+
+ switch (command) {
+ case NV3P_CMD_GET_PLATFORM_INFO:
+ length = sizeof(nv3p_platform_info_t);
+ break;
+ case NV3P_CMD_DL_BCT:
+ case NV3P_CMD_DL_BL:
+ break;
+ default:
+ dprintf("unknown command: 0x%x\n", command);
+ return EINVAL;
+ }
+
+
+ if (length) {
+ ret = nv3p_data_recv(h3p, args, length);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nv3p_recv_hdr(nv3p_handle_t h3p, nv3p_header_t *hdr,
+ uint32_t *checksum )
+{
+ int ret;
+ uint32_t length;
+ uint8_t *tmp;
+
+ tmp = &s_buffer[0];
+ length = NV3P_PACKET_SIZE_BASIC;
+ ret = nv3p_read(h3p->usb, tmp, length);
+ if (ret)
+ return ret;
+
+ READ32(tmp, hdr->version);
+ READ32(tmp, hdr->packet_type);
+ READ32(tmp, hdr->sequence);
+
+ if (hdr->version != NV3P_VERSION) {
+ return EINVAL;
+ }
+
+ h3p->recv_sequence = hdr->sequence;
+
+ *checksum = nv3p_cksum(&s_buffer[0], length);
+ return 0;
+}
+
+static int nv3p_data_recv(nv3p_handle_t h3p, uint8_t *data, uint32_t length)
+{
+ int ret;
+ uint8_t *tmp;
+ nv3p_header_t hdr = {0,0,0};
+ uint32_t checksum;
+ uint32_t recv_length;
+
+ // check for left over stuff from a previous read
+ if (h3p->bytes_remaining == 0) {
+ // get the basic header, verify it's data
+ ret = nv3p_recv_hdr(h3p, &hdr, &h3p->recv_checksum);
+ if (ret)
+ goto fail;
+
+ if (hdr.packet_type != NV3P_PACKET_TYPE_DATA)
+ return nv3p_drain_packet(h3p, &hdr);
+
+ tmp = &s_buffer[0];
+
+ // get length
+ ret = nv3p_read(h3p->usb, tmp, (1 * 4));
+ if (ret)
+ goto fail;
+
+ READ32(tmp, recv_length);
+
+ if (!recv_length) {
+ ret = EIO;
+ goto fail;
+ }
+
+ h3p->recv_checksum += nv3p_cksum((uint8_t *)&recv_length, 4);
+
+ // setup for partial reads
+ h3p->bytes_remaining = recv_length;
+ length = MIN(length, recv_length);
+ }
+ else {
+ length = MIN(h3p->bytes_remaining, length);
+ }
+
+ // read the data
+ ret = nv3p_read(h3p->usb, data, length);
+ if (ret)
+ goto fail;
+
+ h3p->recv_checksum += nv3p_cksum(data, length);
+
+ h3p->bytes_remaining -= length;
+ if (h3p->bytes_remaining == 0) {
+ // get/verify the checksum
+ ret = nv3p_read(h3p->usb, (uint8_t *)&checksum, 4);
+ if (ret)
+ goto fail;
+
+ if (h3p->recv_checksum + checksum != 0) {
+ ret = EIO;
+ goto fail;
+ }
+
+ nv3p_ack(h3p);
+ }
+
+ return 0;
+
+fail:
+ nv3p_nack(h3p, NV3P_NACK_BAD_DATA);
+ return ret;
+}
+
+
+static int nv3p_drain_packet(nv3p_handle_t h3p, nv3p_header_t *hdr)
+{
+ int ret = EIO;
+
+ /*
+ * consume an ack or nack packet. the other packet types are not
+ * recoverable.
+ */
+ if (hdr->packet_type == NV3P_PACKET_TYPE_ACK ||
+ hdr->packet_type == NV3P_PACKET_TYPE_NACK) {
+ uint32_t checksum;
+
+ if (hdr->packet_type == NV3P_PACKET_TYPE_NACK) {
+ uint32_t code;
+
+ // read 4 more bytes to get the error code
+ ret = nv3p_read(h3p->usb, (uint8_t *)&code, 4);
+ if (ret)
+ return ret;
+
+ h3p->last_nack = code;
+ }
+
+ // drain the checksum
+ ret = nv3p_read(h3p->usb, (uint8_t *)&checksum, 4);
+ if (ret)
+ return ret;
+
+ ret = EIO;
+ }
+
+ return ret;
+}
+
+void nv3p_ack(nv3p_handle_t h3p)
+{
+ uint32_t checksum;
+ uint8_t packet[NV3P_MAX_ACK_SIZE];
+ uint32_t length;
+
+ nv3p_write_header(NV3P_PACKET_TYPE_ACK, h3p->recv_sequence, packet);
+
+ length = NV3P_PACKET_SIZE_BASIC;
+ length += NV3P_PACKET_SIZE_ACK;
+
+ checksum = nv3p_cksum(packet, length);
+ checksum = ~checksum + 1;
+ nv3p_write_footer(checksum, &packet[length]);
+
+ length += NV3P_PACKET_SIZE_FOOTER;
+
+ // send the packet
+ usb_write(h3p->usb, packet, length);
+}
+
+static void nv3p_nack(nv3p_handle_t h3p, uint32_t code)
+{
+ uint32_t checksum;
+ uint8_t packet[NV3P_MAX_ACK_SIZE];
+ uint8_t *tmp;
+ uint32_t length;
+
+ nv3p_write_header(NV3P_PACKET_TYPE_NACK, h3p->recv_sequence, packet);
+
+ length = NV3P_PACKET_SIZE_BASIC;
+
+ tmp = &packet[length];
+
+ // write the nack code
+ WRITE32(tmp, code);
+
+ length += NV3P_PACKET_SIZE_NACK;
+ checksum = nv3p_cksum(packet, length);
+ checksum = ~checksum + 1;
+ nv3p_write_footer(checksum, tmp);
+
+ length += NV3P_PACKET_SIZE_FOOTER;
+
+ // send the packet
+ usb_write(h3p->usb, packet, length);
+}
+
+static int nv3p_get_args(nv3p_handle_t h3p, uint32_t command, void **args,
+ uint8_t *packet )
+{
+ uint8_t *tmp = packet;
+ uint8_t *buf = &s_args[0];
+
+ switch(command) {
+ case NV3P_CMD_GET_PLATFORM_INFO:
+ // output only
+ break;
+ case NV3P_CMD_STATUS:
+ {
+ nv3p_cmd_status_t *a = (nv3p_cmd_status_t *)buf;
+ memcpy(a->msg, tmp, NV3P_STRING_MAX);
+ tmp += NV3P_STRING_MAX;
+
+ READ32(tmp, a->code);
+ READ32(tmp, a->flags);
+ break;
+ }
+ case NV3P_CMD_DL_BCT:
+ {
+ nv3p_cmd_dl_bct_t *a = (nv3p_cmd_dl_bct_t *)buf;
+ READ32(tmp, a->length);
+ break;
+ }
+ case NV3P_CMD_DL_BL:
+ {
+ nv3p_cmd_dl_bl_t *a = (nv3p_cmd_dl_bl_t *)buf;
+ READ64(tmp, a->length);
+ READ32(tmp, a->address);
+ READ32(tmp, a->entry);
+ break;
+ }
+ default:
+ dprintf("unknown command: 0x%x\n", command);
+ return EINVAL;
+ }
+
+ *args = buf;
+ return 0;
+}
+
+int nv3p_cmd_recv(nv3p_handle_t h3p, uint32_t *command, void **args)
+{
+ int ret;
+ uint8_t *tmp;
+ nv3p_header_t hdr = {0,0,0};
+ uint32_t length;
+ uint32_t checksum, recv_checksum = 0;
+
+ // get the basic header, verify it's a command
+ ret = nv3p_recv_hdr(h3p, &hdr, &recv_checksum);
+ if (ret)
+ return ret;
+
+ if(hdr.packet_type != NV3P_PACKET_TYPE_CMD)
+ return nv3p_drain_packet(h3p, &hdr);
+
+ tmp = &s_buffer[0];
+
+ // get length and command number
+ ret = nv3p_read(h3p->usb, tmp, (2 * 4));
+ if (ret)
+ return ret;
+
+ READ32(tmp, length);
+ READ32(tmp, *(uint32_t *)command);
+
+ // read the args
+ if (length) {
+ ret = nv3p_read(h3p->usb, tmp, length);
+ if (ret)
+ return ret;
+
+ ret = nv3p_get_args(h3p, *command, args, tmp);
+ if (ret)
+ return ret;
+ } else {
+ // command may be output only
+ ret = nv3p_get_args(h3p, *command, args, 0);
+ if (ret)
+ *args = 0;
+ }
+
+ length += NV3P_PACKET_SIZE_COMMAND;
+ recv_checksum += nv3p_cksum(&s_buffer[0], length);
+
+ // get/verify the checksum
+ ret = nv3p_read(h3p->usb, (uint8_t *)&checksum, 4);
+ if (ret)
+ return ret;
+
+ if(recv_checksum + checksum != 0)
+ return EIO;
+
+ return 0;
+}
+
+int nv3p_data_send(nv3p_handle_t h3p, uint8_t *data, uint32_t length)
+{
+ int ret;
+ uint32_t checksum;
+ uint8_t *packet;
+ uint8_t *tmp;
+ uint32_t hdrlen;
+
+ packet = &s_buffer[0];
+
+ nv3p_write_header(NV3P_PACKET_TYPE_DATA, h3p->sequence, packet);
+ tmp = packet + NV3P_PACKET_SIZE_BASIC;
+
+ // data header
+ WRITE32(tmp, length);
+
+ hdrlen = NV3P_PACKET_SIZE_BASIC + NV3P_PACKET_SIZE_DATA;
+
+ // checksum
+ checksum = nv3p_cksum(packet, hdrlen);
+ checksum += nv3p_cksum(data, length);
+ checksum = ~checksum + 1;
+
+ // send the headers
+ ret = usb_write(h3p->usb, packet, hdrlen);
+ if (ret)
+ return ret;
+
+ // send the data
+ ret = usb_write(h3p->usb, data, length);
+ if (ret)
+ return ret;
+
+ // send checksum
+ ret = usb_write(h3p->usb, (uint8_t *)&checksum, 4);
+ if (ret)
+ return ret;
+
+ h3p->sequence++;
+
+ // wait for ack/nack
+ ret = nv3p_wait_ack(h3p);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int bytesleft = 0;
+uint8_t packet[NV3P_MAX_COMMAND_SIZE];
+int offset = 0;
+
+/*
+ * nv3p_read() - buffered read, target sometimes pads extra bytes to the
+ * end of responses, and we don't handle short reads well,
+ * so buffer reads and copy out
+ */
+static int nv3p_read(usb_device_t *usb, uint8_t *buf, int len)
+{
+ int actual_len;
+ int ret;
+
+ if (len > sizeof(packet)) {
+ dprintf("request for read too big (%d bytes)\n", len);
+ return E2BIG;
+ }
+
+ if (len > bytesleft) {
+ ret = usb_read(usb, packet, sizeof(packet), &actual_len);
+ if (ret) {
+ dprintf("USB read failed\n");
+ return ret;
+ }
+
+ offset = 0;
+ bytesleft = actual_len;
+
+ len = MIN(len, actual_len);
+ }
+
+ memcpy(buf, packet + offset, len);
+
+ offset += len;
+ bytesleft -= len;
+
+ return 0;
+}
+
+