Browse Source

mcp2210: Implement protocol required for NanoFury USB sticks

Luke Dashjr 12 years ago
parent
commit
386a5bfc7f
3 changed files with 296 additions and 7 deletions
  1. 1 1
      Makefile.am
  2. 267 6
      mcp2210.c
  3. 28 0
      mcp2210.h

+ 1 - 1
Makefile.am

@@ -245,7 +245,7 @@ bfgminer_SOURCES += driver-littlefury.c
 endif
 
 if HAS_NANOFURY
-bfgminer_SOURCES += mcp2210.c
+bfgminer_SOURCES += mcp2210.c mcp2210.h
 endif
 endif
 

+ 267 - 6
mcp2210.c

@@ -11,6 +11,7 @@
 
 #include <dlfcn.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -19,6 +20,9 @@
 
 #include "logging.h"
 #include "lowlevel.h"
+#include "miner.h"
+
+#include "mcp2210.h"
 
 #define MCP2210_IDVENDOR   0x04d8
 #define MCP2210_IDPRODUCT  0x00de
@@ -29,9 +33,10 @@
 #define HID_API_EXPORT /* */
 #endif
 struct hid_device_info HID_API_EXPORT *(*dlsym_hid_enumerate)(unsigned short, unsigned short);
-#define hid_enumerate dlsym_hid_enumerate
 void HID_API_EXPORT (*dlsym_hid_free_enumeration)(struct hid_device_info *);
-#define hid_free_enumeration dlsym_hid_free_enumeration
+hid_device * HID_API_EXPORT (*dlsym_hid_open_path)(const char *);
+int HID_API_EXPORT (*dlsym_hid_read)(hid_device *, unsigned char *, size_t);
+int HID_API_EXPORT (*dlsym_hid_write)(hid_device *, const unsigned char *, size_t);
 
 #define LOAD_SYM(sym)  do { \
 	if (!(dlsym_ ## sym = dlsym(dlh, #sym))) {  \
@@ -54,16 +59,19 @@ bool hidapi_try_lib(const char * const dlname)
 	}
 	
 	LOAD_SYM(hid_enumerate);
+	LOAD_SYM(hid_free_enumeration);
 	
-	hid_enum = hid_enumerate(0, 0);
+	hid_enum = dlsym_hid_enumerate(0, 0);
 	if (!hid_enum)
 	{
 		applog(LOG_DEBUG, "%s: Loaded %s, but no devices enumerated; trying other libraries", __func__, dlname);
 		goto fail;
 	}
+	dlsym_hid_free_enumeration(hid_enum);
 	
-	LOAD_SYM(hid_free_enumeration);
-	hid_free_enumeration(hid_enum);
+	LOAD_SYM(hid_open_path);
+	LOAD_SYM(hid_read);
+	LOAD_SYM(hid_write);
 	
 	applog(LOG_DEBUG, "%s: Successfully loaded %s", __func__, dlname);
 	
@@ -74,10 +82,16 @@ fail:
 	return false;
 }
 
+#define hid_enumerate dlsym_hid_enumerate
+#define hid_free_enumeration dlsym_hid_free_enumeration
+#define hid_open_path dlsym_hid_open_path
+#define hid_read dlsym_hid_read
+#define hid_write dlsym_hid_write
+
 static
 bool hidapi_load_library()
 {
-	if (dlsym_hid_free_enumeration)
+	if (dlsym_hid_write)
 		return true;
 	
 	const char **p;
@@ -163,6 +177,253 @@ struct lowlevel_device_info *mcp2210_devinfo_scan()
 	return devinfo_list;
 }
 
+struct mcp2210_device {
+	hid_device *hid;
+	
+	// http://ww1.microchip.com/downloads/en/DeviceDoc/22288A.pdf pg 34
+	uint8_t cfg_spi[0x11];
+	// http://ww1.microchip.com/downloads/en/DeviceDoc/22288A.pdf pg 40
+	uint8_t cfg_gpio[0xf];
+};
+
+static
+bool mcp2210_io(hid_device * const hid, uint8_t * const cmd, uint8_t * const buf)
+{
+	return likely(
+		64 == hid_write(hid, cmd, 64) &&
+		64 == hid_read(hid, buf, 64)
+	);
+}
+
+static
+bool mcp2210_get_configs(struct mcp2210_device * const h)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x41}, buf[0x40];
+	
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to get current %s config", __func__, "SPI");
+		return false;
+	}
+	memcpy(h->cfg_spi, &buf[4], sizeof(h->cfg_spi));
+	
+	cmd[0] = 0x20;
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to get current %s config", __func__, "GPIO");
+		return false;
+	}
+	memcpy(h->cfg_gpio, &buf[4], sizeof(h->cfg_gpio));
+	
+	return true;
+}
+
+struct mcp2210_device *mcp2210_open(struct lowlevel_device_info *info)
+{
+	struct mcp2210_device *h;
+	char * const path = info->lowl_data;
+	hid_device * const hid = hid_open_path(path);
+	
+	if (unlikely(!hid))
+		return NULL;
+	
+	h = malloc(sizeof(*h));
+	h->hid = hid;
+	
+	if (!mcp2210_get_configs(h))
+		goto fail;
+	
+	return h;
+
+fail:
+	free(h);
+	return NULL;
+}
+
+static
+bool mcp2210_set_cfg_spi(struct mcp2210_device * const h)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x40}, buf[0x40];
+	memcpy(&cmd[4], h->cfg_spi, sizeof(h->cfg_spi));
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to set current %s config", __func__, "SPI");
+		return false;
+	}
+	
+	if (buf[1] != 0)
+	{
+		applog(LOG_ERR, "%s: Error setting current %s config (%d)", __func__, "SPI", buf[1]);
+		return false;
+	}
+	
+	return true;
+}
+
+bool mcp2210_configure_spi(struct mcp2210_device * const h, const uint32_t bitrate, const uint16_t idlechipsel, const uint16_t activechipsel, const uint16_t chipseltodatadelay, const uint16_t lastbytetocsdelay, const uint16_t midbytedelay)
+{
+	uint8_t * const cfg = h->cfg_spi;
+	
+	cfg[0] = (bitrate >> 0x00) & 0xff;
+	cfg[1] = (bitrate >> 0x08) & 0xff;
+	cfg[2] = (bitrate >> 0x10) & 0xff;
+	cfg[3] = (bitrate >> 0x18) & 0xff;
+	
+	cfg[4] = (  idlechipsel >> 0) & 0xff;
+	cfg[5] = (  idlechipsel >> 8) & 0xff;
+	
+	cfg[6] = (activechipsel >> 0) & 0xff;
+	cfg[7] = (activechipsel >> 8) & 0xff;
+	
+	cfg[8] = (chipseltodatadelay >> 0) & 0xff;
+	cfg[9] = (chipseltodatadelay >> 8) & 0xff;
+	
+	cfg[0xa] = (lastbytetocsdelay >> 0) & 0xff;
+	cfg[0xb] = (lastbytetocsdelay >> 8) & 0xff;
+	
+	cfg[0xc] = (midbytedelay >> 0) & 0xff;
+	cfg[0xd] = (midbytedelay >> 8) & 0xff;
+	
+	return mcp2210_set_cfg_spi(h);
+}
+
+bool mcp2210_set_spimode(struct mcp2210_device * const h, const uint8_t spimode)
+{
+	uint8_t * const cfg = h->cfg_spi;
+	cfg[0x10] = spimode;
+	return mcp2210_set_cfg_spi(h);
+}
+
+bool mcp2210_spi_transfer(struct mcp2210_device * const h, const void * const tx, void * const rx, uint8_t sz)
+{
+	hid_device * const hid = h->hid;
+	uint8_t * const cfg = h->cfg_spi;
+	uint8_t cmd[0x40] = {0x42}, buf[0x40];
+	uint8_t *p = rx;
+	
+	if (unlikely(sz > 60))
+	{
+		applog(LOG_ERR, "%s: SPI transfer too long (%d bytes)", __func__, sz);
+		return false;
+	}
+	
+	cfg[0xe] = sz;
+	cfg[0xf] = 0;
+	if (!mcp2210_set_cfg_spi(h))
+		return false;
+	
+	cmd[1] = sz;
+	memcpy(&cmd[4], tx, sz);
+	if (unlikely(!mcp2210_io(hid, cmd, buf)))
+	{
+		applog(LOG_ERR, "%s: Failed to issue SPI transfer", __func__);
+		return false;
+	}
+	
+	while (true)
+	{
+		switch (buf[1])
+		{
+			case 0:     // accepted
+				cmd[1] = 0;
+				break;
+			case 0xf8:  // transfer in progress
+				applog(LOG_DEBUG, "%s: SPI transfer rejected temporarily (%d bytes remaining)", __func__, sz);
+				cgsleep_ms(20);
+				goto retry;
+			default:
+				applog(LOG_ERR, "%s: SPI transfer error (%d) (%d bytes remaining)", __func__, buf[1], sz);
+				return false;
+		}
+		if (buf[2] >= sz)
+		{
+			if (buf[2] > sz)
+				applog(LOG_WARNING, "%s: Received %d extra bytes in SPI transfer", __func__, sz - buf[2]);
+			memcpy(p, &buf[4], sz);
+			return true;
+		}
+		memcpy(p, &buf[4], buf[2]);
+		p += buf[2];
+		sz -= buf[2];
+retry:
+		if (unlikely(!mcp2210_io(hid, cmd, buf)))
+		{
+			applog(LOG_ERR, "%s: Failed to continue SPI transfer (%d bytes remaining)", __func__, sz);
+			return false;
+		}
+	}
+}
+
+static
+bool mcp2210_set_cfg_gpio(struct mcp2210_device * const h)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x21}, buf[0x40];
+	
+	// NOTE: NVRAM chip params access control is not set here
+	memcpy(&cmd[4], h->cfg_gpio, 0xe);
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to set current %s config", __func__, "GPIO");
+		return false;
+	}
+	
+	if (buf[1] != 0)
+	{
+		applog(LOG_ERR, "%s: Error setting current %s config (%d)", __func__, "GPIO", buf[1]);
+		return false;
+	}
+	
+	return true;
+}
+
+static
+bool mcp2210_set_gpio_(struct mcp2210_device * const h, const int pin, const int byteoffset, const bool value)
+{
+	const int bit = 1 << (pin % 8);
+	const int byte = (pin / 8) + byteoffset;
+	
+	if (value)
+		h->cfg_gpio[byte] |= bit;
+	else
+		h->cfg_gpio[byte] &= ~bit;
+	
+	return mcp2210_set_cfg_gpio(h);
+}
+
+bool mcp2210_set_gpio_output(struct mcp2210_device * const h, const int pin, const enum mcp2210_gpio_value d)
+{
+	h->cfg_gpio[pin] = 0;
+	if (!mcp2210_set_gpio_(h, pin, 0xb, false))
+		return false;
+	return mcp2210_set_gpio_(h, pin, 0x9, d == MGV_HIGH);
+}
+
+enum mcp2210_gpio_value mcp2210_get_gpio_input(struct mcp2210_device * const h, const int pin)
+{
+	hid_device * const hid = h->hid;
+	uint8_t cmd[0x40] = {0x31}, buf[0x40];
+	const int bit = 1 << (pin % 8);
+	const int byte = (pin / 8) + 4;
+	
+	h->cfg_gpio[pin] = 0;
+	if (!mcp2210_set_gpio_(h, pin, 0xb, true))
+		return MGV_ERROR;
+	
+	if (!mcp2210_io(hid, cmd, buf))
+	{
+		applog(LOG_ERR, "%s: Failed to get current GPIO input values", __func__);
+		return MGV_ERROR;
+	}
+	
+	if (buf[byte] & bit)
+		return MGV_HIGH;
+	else
+		return MGV_LOW;
+}
+
 struct lowlevel_driver lowl_mcp2210 = {
 	.devinfo_scan = mcp2210_devinfo_scan,
 	.devinfo_free = mcp2210_devinfo_free,

+ 28 - 0
mcp2210.h

@@ -0,0 +1,28 @@
+#ifndef BFG_MCP2210_H
+#define BFG_MCP2210_H
+
+#include <stdbool.h>
+
+enum mcp2210_gpio_direction {
+	MGD_OUTPUT,
+	MGD_INPUT,
+};
+
+enum mcp2210_gpio_value {
+	MGV_LOW,
+	MGV_HIGH,
+	MGV_ERROR,
+};
+
+struct mcp2210_device;
+
+extern struct mcp2210_device *mcp2210_open(struct lowlevel_device_info *);
+
+extern bool mcp2210_configure_spi(struct mcp2210_device *, uint32_t bitrate, uint16_t idlechipsel, uint16_t activechipsel, uint16_t chipseltodatadelay, uint16_t lastbytetocsdelay, uint16_t midbytedelay);
+extern bool mcp2210_set_spimode(struct mcp2210_device *, uint8_t spimode);
+extern bool mcp2210_spi_transfer(struct mcp2210_device *, const void *tx, void *rx, uint8_t sz);
+
+extern bool mcp2210_set_gpio_output(struct mcp2210_device *, int pin, enum mcp2210_gpio_value);
+extern enum mcp2210_gpio_value mcp2210_get_gpio_input(struct mcp2210_device *, int pin);
+
+#endif